Coverage for pygeodesy/unitsBase.py: 99%
101 statements
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-06 16:50 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-06 16:50 -0400
2# -*- coding: utf-8 -*-
4u'''Basic C{Float}, C{Int} and C{Str}ing units classes.
5'''
7from pygeodesy.errors import UnitError, _XError, _xkwds_item2
8from pygeodesy.interns import NN, _degrees_, _degrees2_, _invalid_, \
9 _meter_, _radians_, _radians2_, \
10 _radius_, _UNDER_, _std_ # PYCHOK used!
11from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
12from pygeodesy.named import modulename, _Named, property_doc_
13# from pygeodesy.props import property_doc_ # from .named
14from pygeodesy.streprs import Fmt, fstr
16__all__ = _ALL_LAZY.unitsBase
17__version__ = '24.02.20'
20class _NamedUnit(_Named):
21 '''(INTERNAL) Base class for C{units}.
22 '''
23 _std_repr = True # set below
24 _units = None
26 @property_doc_(' standard C{repr} or named C{toRepr} representation.')
27 def std_repr(self):
28 '''Get the representation (C{bool}, C{True} means standard).
29 '''
30 return self._std_repr
32 @std_repr.setter # PYCHOK setter!
33 def std_repr(self, std):
34 '''Set the representation (C{True} or C{"std"} for standard).
35 '''
36 self._std_repr = std in (True, _std_)
38 def _toRepr(self, value):
39 '''(INTERNAL) Representation "<name> (<value>)" or "<classname>(<value>)".
40 '''
41 return Fmt.PARENSPACED(self.name, value) if self.name else \
42 Fmt.PAREN( self.classname, value)
44 @property_doc_(' units name.')
45 def units(self):
46 '''Get the units name (C{str}).
47 '''
48 if self._units is None:
49 self._units = self.classname
50 return self._units
52 @units.setter # PYCHOK setter!
53 def units(self, units):
54 '''Set the units name for this instance (C{str} or C{None} for default).
55 '''
56 self._units = None if units is None else str(units)
59class Float(float, _NamedUnit):
60 '''Named C{float}.
61 '''
62 # _std_repr = True # set below
64 def __new__(cls, arg=None, name=NN, Error=UnitError, **name_arg):
65 '''New C{Ffloat} instance.
67 @kwarg arg: The value (any C{type} convertable to C{float}).
68 @kwarg name: Optional instance name (C{str}).
69 @kwarg Error: Optional error to raise, overriding the default
70 L{UnitError}.
71 @kwarg name_arg: Optional C{name=arg} keyword argument, inlieu
72 of B{C{name}} and B{C{arg}}.
74 @returns: A C{Float} instance.
76 @raise Error: Invalid B{C{arg}}.
77 '''
78 if name_arg:
79 name, arg = _xkwds_item2(name_arg)
80 try:
81 self = float.__new__(cls, arg)
82 if name:
83 _NamedUnit.name.fset(self, name) # see _Named.name
84 except Exception as x: # XXX not ... as x:
85 raise _Error(cls, arg, name, Error, x=x)
86 return self
88 def __repr__(self): # to avoid MRO(float)
89 '''Return a representation of this C{Float}.
91 @see: Method C{Float.toRepr} and property C{Float.std_repr}.
93 @note: Use C{env} variable C{PYGEODESY_FLOAT_STD_REPR=std} prior
94 to C{import pygeodesy} to get the standard C{repr} or set
95 property C{std_repr=False} to always get the named C{toRepr}
96 representation.
97 '''
98 return self.toRepr(std=self._std_repr)
100 def __str__(self): # to avoid MRO(float)
101 '''Return this C{Float} as standard C{str}.
102 '''
103 # XXX must use super(Float, self)... since super()...
104 # only works for Python 3+ and float.__str__(self)
105 # invokes .__repr__(self); calling self.toRepr(std=True)
106 # super(Float, self).__repr__() mimicks this behavior
107 # XXX the default number of decimals is 10-12 when using
108 # float.__str__(self) with both python 3.8+ and 2.7-, but
109 # float.__repr__(self) shows DIG decimals in python2.7!
110 # return super(Float, self).__repr__() # see .test.testCss.py
111 return float.__str__(self) # always _std_str_
113 def toRepr(self, std=False, **prec_fmt_ints): # PYCHOK prec=8, ...
114 '''Return a representation of this C{Float}.
116 @kwarg std: If C{True} return the standard C{repr},
117 otherwise the named representation (C{bool}).
119 @see: Methods L{Float.__repr__}, L{Float.toStr} and function
120 L{pygeodesy.fstr} for more documentation.
121 '''
122 # XXX must use super(Float, self)... since
123 # super()... only works for Python 3+
124 # return super(Float, self).__repr__() if std else \
125 return float.__repr__(self) if std else \
126 self._toRepr(self.toStr(**prec_fmt_ints))
128 def toStr(self, prec=12, fmt=Fmt.g, ints=False): # PYCHOK prec=8, ...
129 '''Format this C{Float} as C{str}.
131 @see: Method L{Float.__repr__} and function L{pygeodesy.fstr}
132 for more documentation.
133 '''
134 return fstr(self, prec=prec, fmt=fmt, ints=ints)
137class Int(int, _NamedUnit):
138 '''Named C{int}.
139 '''
140 # _std_repr = True # set below
142 def __new__(cls, arg=None, name=NN, Error=UnitError, **name_arg):
143 '''New C{Int} instance.
145 @kwarg arg: The value (any C{type} convertable to C{float}).
146 @kwarg name: Optional instance name (C{str}).
147 @kwarg Error: Optional error to raise, overriding the
148 default L{UnitError}.
149 @kwarg name_arg: Optional C{name=arg} keyword argument,
150 inlieu of B{C{name}} and B{C{arg}}.
152 @returns: An C{Int} instance.
154 @raise Error: Invalid B{C{arg}}.
155 '''
156 if name_arg:
157 name, arg = _xkwds_item2(name_arg)
158 try:
159 self = int.__new__(cls, arg)
160 if name:
161 _NamedUnit.name.fset(self, name) # see _Named.name
162 except Exception as x: # XXX not ... as x:
163 raise _Error(cls, arg, name, Error, x=x)
164 return self
166 def __repr__(self): # to avoid MRO(int)
167 '''Return a representation of this named C{int}.
169 @see: Method C{Int.toRepr} and property C{Int.std_repr}.
171 @note: Use C{env} variable C{PYGEODESY_INT_STD_REPR=std}
172 prior to C{import pygeodesy} to get the standard
173 C{repr} or set property C{std_repr=False} to always
174 get the named C{toRepr} representation.
175 '''
176 return self.toRepr(std=self._std_repr)
178 def __str__(self): # to avoid MRO(int)
179 '''Return this C{Int} as standard C{str}.
180 '''
181 return self.toStr()
183 def toRepr(self, std=False, **unused): # PYCHOK **unused
184 '''Return a representation of this C{Int}.
186 @kwarg std: If C{True} return the standard C{repr},
187 otherwise the named representation (C{bool}).
189 @see: Method L{Int.__repr__} for more documentation.
190 '''
191 r = int.__repr__(self) # self.toStr()
192 return r if std else self._toRepr(r)
194 def toStr(self, **unused): # PYCHOK **unused
195 '''Return this C{Int} as standard C{str}.
197 @see: Method L{Int.__repr__} for more documentation.
198 '''
199 # XXX must use '%d' % (self,) since
200 # int.__str__(self) fails with 3.8+
201 return '%d' % (self,)
204class Radius(Float):
205 '''Named C{float} representing a radius, conventionally in C{meter}.
206 '''
207 def __new__(cls, arg=None, name=_radius_, **Error_name_arg):
208 '''New L{Radius} instance, see L{Float}.
209 '''
210 return Float.__new__(cls, arg=arg, name=name, **Error_name_arg)
213class Str(str, _NamedUnit):
214 '''Named, callable C{str}.
215 '''
216 # _std_repr = True # set below
218 def __new__(cls, arg=None, name=NN, Error=UnitError, **name_arg):
219 '''New C{Str} instance.
221 @kwarg cls: This class (C{Str} or sub-class).
222 @kwarg arg: The value (any C{type} convertable to C{str}).
223 @kwarg name: Optional instance name (C{str}).
224 @kwarg Error: Optional error to raise, overriding the
225 default (C{ValueError}).
226 @kwarg name_arg: Optional C{name=arg} keyword argument,
227 inlieu of B{C{name}} and B{C{arg}}.
229 @returns: A L{Str} instance.
231 @raise Error: Invalid B{C{arg}}.
233 @see: Callable, not-nameable class L{pygeodesy.Str_}.
234 '''
235 if name_arg:
236 name, arg = _xkwds_item2(name_arg)
237 try:
238 self = str.__new__(cls, arg)
239 if name:
240 _NamedUnit.name.fset(self, name) # see _Named.name
241 except Exception as x: # XXX not ... as x:
242 raise _Error(cls, arg, name, Error, x=x)
243 return self
245 def __repr__(self):
246 '''Return a representation of this C{Str}.
248 @see: Method C{Str.toRepr} and property C{Str.std_repr}.
250 @note: Use C{env} variable C{PYGEODESY_STR_STD_REPR=std}
251 prior to C{import pygeodesy} to get the standard
252 C{repr} or set property C{std_repr=False} to always
253 get the named C{toRepr} representation.
254 '''
255 return self.toRepr(std=self._std_repr) # see .test/testGars.py
257 def __str__(self):
258 '''Return this C{Str} as standard C{str}.
259 '''
260 return self.toStr()
262 def join_(self, *args, **name_Error):
263 '''Join all positional B{C{args}} like C{self.join(B{args})}.
265 @return: All B{C{args}} joined by this instance (L{Str_}).
267 @note: An other L{Str} instance is returned to make the
268 result re-callable.
269 '''
270 return Str(str.join(self, map(str, args)), **name_Error) # re-callable
272 __call__ = join_
274 def toRepr(self, std=False, **unused): # PYCHOK **unused
275 '''Return a representation of this C{Str}.
277 @kwarg std: If C{True} return the standard C{repr},
278 otherwise the named representation (C{bool}).
280 @see: Method L{Str.__repr__} for more documentation.
281 '''
282 # must use super(Str, self).. since
283 # super()... only works for Python 3+ and
284 # str.__repr__(self) fails with Python 3.8+
285 r = super(Str, self).__repr__()
286 return r if std else self._toRepr(r)
288 def toStr(self, **unused): # PYCHOK **unused
289 '''Return this C{Str} as standard C{str}.
290 '''
291 # must use super(Str, self)... since
292 # super()... only works for Python 3+ and
293 # str.__str__(self) fails with Python 3.8+
294 return super(Str, self).__str__()
297_Str_degrees = Str(_degrees_) # PYCHOK in .frechet, .hausdorff
298_Str_degrees2 = Str(_degrees2_) # PYCHOK in .frechet, .hausdorff
299_Str_meter = Str(_meter_) # PYCHOK in .frechet, .hausdorff
300_Str_NN = Str(NN) # PYCHOK in .frechet, .hausdorff
301_Str_radians = Str(_radians_) # PYCHOK in .frechet, .hausdorff
302_Str_radians2 = Str(_radians2_) # PYCHOK in .frechet, .hausdorff
305def _Error(clas, arg, name, Error, txt=_invalid_, x=None):
306 '''(INTERNAL) Return an error with explanation.
308 @arg clas: The C{units} class or sub-class.
309 @arg arg: The original C{unit} value.
310 @arg name: The instance name (C{str}).
311 @arg Error: The Error class to use (C{Excetion}).
312 @kwarg txt: An explanation of the error )C{str}).
313 @kwarg x: Caught exception, used for exception
314 chaining (iff enabled in Python3+).
316 @returns: An B{C{Error}} instance.
317 '''
318 if x is not None: # caught exception, cause
319 if Error is UnitError: # and isError(x)
320 Error = type(x) # i.e. if not overridden
321 if txt is _invalid_:
322 txt = str(x) # i.e. if not overridden
323 n = name if name else modulename(clas).lstrip(_UNDER_)
324 return _XError(Error, n, arg, txt=txt, cause=x)
327__all__ += _ALL_DOCS(_NamedUnit)
329# **) MIT License
330#
331# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
332#
333# Permission is hereby granted, free of charge, to any person obtaining a
334# copy of this software and associated documentation files (the "Software"),
335# to deal in the Software without restriction, including without limitation
336# the rights to use, copy, modify, merge, publish, distribute, sublicense,
337# and/or sell copies of the Software, and to permit persons to whom the
338# Software is furnished to do so, subject to the following conditions:
339#
340# The above copyright notice and this permission notice shall be included
341# in all copies or substantial portions of the Software.
342#
343# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
344# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
345# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
346# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
347# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
348# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
349# OTHER DEALINGS IN THE SOFTWARE.