pygeodesy 24.3.24__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyGeodesy-24.3.24.dist-info/METADATA +272 -0
- PyGeodesy-24.3.24.dist-info/RECORD +115 -0
- PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
- PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
- pygeodesy/LICENSE +21 -0
- pygeodesy/__init__.py +615 -0
- pygeodesy/__main__.py +103 -0
- pygeodesy/albers.py +867 -0
- pygeodesy/auxilats/_CX_4.py +218 -0
- pygeodesy/auxilats/_CX_6.py +314 -0
- pygeodesy/auxilats/_CX_8.py +475 -0
- pygeodesy/auxilats/__init__.py +54 -0
- pygeodesy/auxilats/__main__.py +86 -0
- pygeodesy/auxilats/auxAngle.py +548 -0
- pygeodesy/auxilats/auxDLat.py +302 -0
- pygeodesy/auxilats/auxDST.py +296 -0
- pygeodesy/auxilats/auxLat.py +848 -0
- pygeodesy/auxilats/auxily.py +272 -0
- pygeodesy/azimuthal.py +1150 -0
- pygeodesy/basics.py +892 -0
- pygeodesy/booleans.py +2031 -0
- pygeodesy/cartesianBase.py +1062 -0
- pygeodesy/clipy.py +704 -0
- pygeodesy/constants.py +516 -0
- pygeodesy/css.py +660 -0
- pygeodesy/datums.py +752 -0
- pygeodesy/deprecated/__init__.py +61 -0
- pygeodesy/deprecated/bases.py +40 -0
- pygeodesy/deprecated/classes.py +262 -0
- pygeodesy/deprecated/consterns.py +54 -0
- pygeodesy/deprecated/datum.py +40 -0
- pygeodesy/deprecated/functions.py +375 -0
- pygeodesy/deprecated/nvector.py +48 -0
- pygeodesy/deprecated/rhumbBase.py +32 -0
- pygeodesy/deprecated/rhumbaux.py +33 -0
- pygeodesy/deprecated/rhumbsolve.py +33 -0
- pygeodesy/deprecated/rhumbx.py +33 -0
- pygeodesy/dms.py +986 -0
- pygeodesy/ecef.py +1348 -0
- pygeodesy/elevations.py +279 -0
- pygeodesy/ellipsoidalBase.py +1224 -0
- pygeodesy/ellipsoidalBaseDI.py +913 -0
- pygeodesy/ellipsoidalExact.py +343 -0
- pygeodesy/ellipsoidalGeodSolve.py +343 -0
- pygeodesy/ellipsoidalKarney.py +403 -0
- pygeodesy/ellipsoidalNvector.py +685 -0
- pygeodesy/ellipsoidalVincenty.py +590 -0
- pygeodesy/ellipsoids.py +2476 -0
- pygeodesy/elliptic.py +1198 -0
- pygeodesy/epsg.py +243 -0
- pygeodesy/errors.py +804 -0
- pygeodesy/etm.py +1190 -0
- pygeodesy/fmath.py +1013 -0
- pygeodesy/formy.py +1818 -0
- pygeodesy/frechet.py +865 -0
- pygeodesy/fstats.py +760 -0
- pygeodesy/fsums.py +1898 -0
- pygeodesy/gars.py +358 -0
- pygeodesy/geodesicw.py +581 -0
- pygeodesy/geodesicx/_C4_24.py +1699 -0
- pygeodesy/geodesicx/_C4_27.py +2395 -0
- pygeodesy/geodesicx/_C4_30.py +3301 -0
- pygeodesy/geodesicx/__init__.py +48 -0
- pygeodesy/geodesicx/__main__.py +91 -0
- pygeodesy/geodesicx/gx.py +1382 -0
- pygeodesy/geodesicx/gxarea.py +535 -0
- pygeodesy/geodesicx/gxbases.py +154 -0
- pygeodesy/geodesicx/gxline.py +669 -0
- pygeodesy/geodsolve.py +426 -0
- pygeodesy/geohash.py +914 -0
- pygeodesy/geoids.py +1884 -0
- pygeodesy/hausdorff.py +892 -0
- pygeodesy/heights.py +1155 -0
- pygeodesy/interns.py +687 -0
- pygeodesy/iters.py +545 -0
- pygeodesy/karney.py +919 -0
- pygeodesy/ktm.py +633 -0
- pygeodesy/latlonBase.py +1766 -0
- pygeodesy/lazily.py +960 -0
- pygeodesy/lcc.py +684 -0
- pygeodesy/ltp.py +1107 -0
- pygeodesy/ltpTuples.py +1563 -0
- pygeodesy/mgrs.py +721 -0
- pygeodesy/named.py +1324 -0
- pygeodesy/namedTuples.py +683 -0
- pygeodesy/nvectorBase.py +695 -0
- pygeodesy/osgr.py +781 -0
- pygeodesy/points.py +1686 -0
- pygeodesy/props.py +628 -0
- pygeodesy/resections.py +1048 -0
- pygeodesy/rhumb/__init__.py +46 -0
- pygeodesy/rhumb/aux_.py +397 -0
- pygeodesy/rhumb/bases.py +1148 -0
- pygeodesy/rhumb/ekx.py +563 -0
- pygeodesy/rhumb/solve.py +572 -0
- pygeodesy/simplify.py +647 -0
- pygeodesy/solveBase.py +472 -0
- pygeodesy/sphericalBase.py +724 -0
- pygeodesy/sphericalNvector.py +1264 -0
- pygeodesy/sphericalTrigonometry.py +1447 -0
- pygeodesy/streprs.py +627 -0
- pygeodesy/trf.py +2079 -0
- pygeodesy/triaxials.py +1484 -0
- pygeodesy/units.py +969 -0
- pygeodesy/unitsBase.py +349 -0
- pygeodesy/ups.py +538 -0
- pygeodesy/utily.py +1231 -0
- pygeodesy/utm.py +762 -0
- pygeodesy/utmups.py +318 -0
- pygeodesy/utmupsBase.py +517 -0
- pygeodesy/vector2d.py +785 -0
- pygeodesy/vector3d.py +968 -0
- pygeodesy/vector3dBase.py +1049 -0
- pygeodesy/webmercator.py +383 -0
- pygeodesy/wgrs.py +439 -0
pygeodesy/basics.py
ADDED
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''Some, basic definitions, functions and dependencies.
|
|
5
|
+
|
|
6
|
+
Use env variable C{PYGEODESY_XPACKAGES} to avoid import of dependencies
|
|
7
|
+
C{geographiclib}, C{numpy} and/or C{scipy}. Set C{PYGEODESY_XPACKAGES}
|
|
8
|
+
to a comma-separated list of package names to be excluded from import.
|
|
9
|
+
'''
|
|
10
|
+
# make sure int/int division yields float quotient
|
|
11
|
+
from __future__ import division
|
|
12
|
+
division = 1 / 2 # .albers, .azimuthal, .constants, etc., .utily
|
|
13
|
+
if not division:
|
|
14
|
+
raise ImportError('%s 1/2 == %s' % ('division', division))
|
|
15
|
+
del division
|
|
16
|
+
|
|
17
|
+
from pygeodesy.errors import _AttributeError, _ImportError, _NotImplementedError, \
|
|
18
|
+
_TypeError, _TypesError, _ValueError, _xAssertionError, \
|
|
19
|
+
_xkwds_get
|
|
20
|
+
from pygeodesy.interns import MISSING, NN, _1_, _by_, _COMMA_, _DOT_, _DEPRECATED_, \
|
|
21
|
+
_ELLIPSIS4_, _enquote, _EQUAL_, _in_, _invalid_, _N_A_, \
|
|
22
|
+
_not_scalar_, _SPACE_, _UNDER_, _version_, _version_info
|
|
23
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS, \
|
|
24
|
+
_getenv, LazyImportError, _sys, _sys_version_info2
|
|
25
|
+
|
|
26
|
+
from copy import copy as _copy, deepcopy as _deepcopy
|
|
27
|
+
from math import copysign as _copysign
|
|
28
|
+
import inspect as _inspect
|
|
29
|
+
|
|
30
|
+
__all__ = _ALL_LAZY.basics
|
|
31
|
+
__version__ = '24.03.19'
|
|
32
|
+
|
|
33
|
+
_0_0 = 0.0 # in .constants
|
|
34
|
+
_below_ = 'below'
|
|
35
|
+
_list_tuple_types = (list, tuple)
|
|
36
|
+
_list_tuple_set_types = (list, tuple, set)
|
|
37
|
+
_odd_ = 'odd'
|
|
38
|
+
_PYGEODESY_XPACKAGES_ = 'PYGEODESY_XPACKAGES'
|
|
39
|
+
_required_ = 'required'
|
|
40
|
+
|
|
41
|
+
try: # Luciano Ramalho, "Fluent Python", O'Reilly, 2016 p. 395, 2022 p. 577+
|
|
42
|
+
from numbers import Integral as _Ints, Real as _Scalars # .units
|
|
43
|
+
except ImportError:
|
|
44
|
+
try:
|
|
45
|
+
_Ints = int, long # int objects (C{tuple})
|
|
46
|
+
except NameError: # Python 3+
|
|
47
|
+
_Ints = int, # int objects (C{tuple})
|
|
48
|
+
_Scalars = _Ints + (float,)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
try: # use C{from collections.abc import ...} in Python 3.9+
|
|
52
|
+
from collections.abc import Sequence as _Sequence # in .points
|
|
53
|
+
except ImportError: # no .abc in Python 3.8- and 2.7-
|
|
54
|
+
from collections import Sequence as _Sequence # in .points
|
|
55
|
+
if isinstance([], _Sequence) and isinstance((), _Sequence):
|
|
56
|
+
# and isinstance(range(1), _Sequence):
|
|
57
|
+
_Seqs = _Sequence
|
|
58
|
+
else:
|
|
59
|
+
raise ImportError() # _AssertionError
|
|
60
|
+
except ImportError:
|
|
61
|
+
_Sequence = tuple # immutable for .points._Basequence
|
|
62
|
+
_Seqs = list, _Sequence # range for function len2 below
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _passarg(arg): # in .auxilats.auxLat
|
|
66
|
+
'''(INTERNAL) Helper, no-op.
|
|
67
|
+
'''
|
|
68
|
+
return arg
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _passargs(*args): # in .utily
|
|
72
|
+
'''(INTERNAL) Helper, no-op.
|
|
73
|
+
'''
|
|
74
|
+
return args
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
_Bytes = unicode, bytearray # PYCHOK expected
|
|
79
|
+
_Strs = basestring, str # XXX , bytes
|
|
80
|
+
str2ub = ub2str = _passarg # avoids UnicodeDecodeError
|
|
81
|
+
|
|
82
|
+
def _Xstr(exc): # PYCHOK no cover
|
|
83
|
+
'''I{Invoke only with caught ImportError} B{C{exc}}.
|
|
84
|
+
|
|
85
|
+
C{... "can't import name _distributor_init" ...}
|
|
86
|
+
|
|
87
|
+
only for C{numpy}, C{scipy} import errors occurring
|
|
88
|
+
on arm64 Apple Silicon running macOS' Python 2.7.16?
|
|
89
|
+
'''
|
|
90
|
+
t = str(exc)
|
|
91
|
+
if '_distributor_init' in t:
|
|
92
|
+
from sys import exc_info
|
|
93
|
+
from traceback import extract_tb
|
|
94
|
+
tb = exc_info()[2] # 3-tuple (type, value, traceback)
|
|
95
|
+
t4 = extract_tb(tb, 1)[0] # 4-tuple (file, line, name, 'import ...')
|
|
96
|
+
t = _SPACE_("can't", t4[3] or _N_A_)
|
|
97
|
+
del tb, t4
|
|
98
|
+
return t
|
|
99
|
+
|
|
100
|
+
except NameError: # Python 3+
|
|
101
|
+
from pygeodesy.interns import _utf_8_
|
|
102
|
+
|
|
103
|
+
_Bytes = bytes, bytearray
|
|
104
|
+
_Strs = str, # tuple
|
|
105
|
+
_Xstr = str
|
|
106
|
+
|
|
107
|
+
def str2ub(sb):
|
|
108
|
+
'''Convert C{str} to C{unicode bytes}.
|
|
109
|
+
'''
|
|
110
|
+
if isinstance(sb, _Strs):
|
|
111
|
+
sb = sb.encode(_utf_8_)
|
|
112
|
+
return sb
|
|
113
|
+
|
|
114
|
+
def ub2str(ub):
|
|
115
|
+
'''Convert C{unicode bytes} to C{str}.
|
|
116
|
+
'''
|
|
117
|
+
if isinstance(ub, _Bytes):
|
|
118
|
+
ub = str(ub.decode(_utf_8_))
|
|
119
|
+
return ub
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _args_kwds_names(func):
|
|
123
|
+
'''(INTERNAL) Get a C{func}'s args and kwds names, including
|
|
124
|
+
C{self} for methods.
|
|
125
|
+
|
|
126
|
+
@note: Python 2 may I{not} include the C{*args} nor the
|
|
127
|
+
C{**kwds} names.
|
|
128
|
+
'''
|
|
129
|
+
try:
|
|
130
|
+
args_kwds = _inspect.signature(func).parameters.keys()
|
|
131
|
+
except AttributeError: # .signature new Python 3+
|
|
132
|
+
args_kwds = _inspect.getargspec(func).args
|
|
133
|
+
return tuple(args_kwds)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def clips(sb, limit=50, white=NN):
|
|
137
|
+
'''Clip a string to the given length limit.
|
|
138
|
+
|
|
139
|
+
@arg sb: String (C{str} or C{bytes}).
|
|
140
|
+
@kwarg limit: Length limit (C{int}).
|
|
141
|
+
@kwarg white: Optionally, replace all whitespace (C{str}).
|
|
142
|
+
|
|
143
|
+
@return: The clipped or unclipped B{C{sb}}.
|
|
144
|
+
'''
|
|
145
|
+
T = type(sb)
|
|
146
|
+
if len(sb) > limit > 8:
|
|
147
|
+
h = limit // 2
|
|
148
|
+
sb = T(_ELLIPSIS4_).join((sb[:h], sb[-h:]))
|
|
149
|
+
if white: # replace whitespace
|
|
150
|
+
sb = T(white).join(sb.split())
|
|
151
|
+
return sb
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def copysign0(x, y):
|
|
155
|
+
'''Like C{math.copysign(x, y)} except C{zero}, I{unsigned}.
|
|
156
|
+
|
|
157
|
+
@return: C{math.copysign(B{x}, B{y})} if B{C{x}} else
|
|
158
|
+
C{type(B{x})(0)}.
|
|
159
|
+
'''
|
|
160
|
+
return _copysign(x, (y if y else 0)) if x else copytype(0, x)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def copytype(x, y):
|
|
164
|
+
'''Return the value of B{x} as C{type} of C{y}.
|
|
165
|
+
|
|
166
|
+
@return: C{type(B{y})(B{x})}.
|
|
167
|
+
'''
|
|
168
|
+
return type(y)(x if x else _0_0)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def halfs2(str2):
|
|
172
|
+
'''Split a string in 2 halfs.
|
|
173
|
+
|
|
174
|
+
@arg str2: String to split (C{str}).
|
|
175
|
+
|
|
176
|
+
@return: 2-Tuple C{(_1st, _2nd)} half (C{str}).
|
|
177
|
+
|
|
178
|
+
@raise ValueError: Zero or odd C{len(B{str2})}.
|
|
179
|
+
'''
|
|
180
|
+
h, r = divmod(len(str2), 2)
|
|
181
|
+
if r or not h:
|
|
182
|
+
raise _ValueError(str2=str2, txt=_odd_)
|
|
183
|
+
return str2[:h], str2[h:]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def int1s(x):
|
|
187
|
+
'''Count the number of 1-bits in an C{int}, I{unsigned}.
|
|
188
|
+
|
|
189
|
+
@note: C{int1s(-B{x}) == int1s(abs(B{x}))}.
|
|
190
|
+
'''
|
|
191
|
+
try:
|
|
192
|
+
return x.bit_count() # Python 3.10+
|
|
193
|
+
except AttributeError:
|
|
194
|
+
# bin(-x) = '-' + bin(abs(x))
|
|
195
|
+
return bin(x).count(_1_)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def isbool(obj):
|
|
199
|
+
'''Check whether an object is C{bool}ean.
|
|
200
|
+
|
|
201
|
+
@arg obj: The object (any C{type}).
|
|
202
|
+
|
|
203
|
+
@return: C{True} if B{C{obj}} is C{bool}ean,
|
|
204
|
+
C{False} otherwise.
|
|
205
|
+
'''
|
|
206
|
+
return isinstance(obj, bool) # and (obj is False
|
|
207
|
+
# or obj is True)
|
|
208
|
+
|
|
209
|
+
assert not (isbool(1) or isbool(0) or isbool(None)) # PYCHOK 2
|
|
210
|
+
|
|
211
|
+
if _FOR_DOCS: # XXX avoid epydoc Python 2.7 error
|
|
212
|
+
|
|
213
|
+
def isclass(obj):
|
|
214
|
+
'''Return C{True} if B{C{obj}} is a C{class} or C{type}.
|
|
215
|
+
|
|
216
|
+
@see: Python's C{inspect.isclass}.
|
|
217
|
+
'''
|
|
218
|
+
return _inspect.isclass(obj)
|
|
219
|
+
else:
|
|
220
|
+
isclass = _inspect.isclass
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def isCartesian(obj, ellipsoidal=None):
|
|
224
|
+
'''Is B{C{obj}} some C{Cartesian}?
|
|
225
|
+
|
|
226
|
+
@arg obj: The object (any C{type}).
|
|
227
|
+
@kwarg ellipsoidal: If C{None}, return the type of any C{Cartesian},
|
|
228
|
+
if C{True}, only an ellipsoidal C{Cartesian type}
|
|
229
|
+
or if C{False}, only a spherical C{Cartesian type}.
|
|
230
|
+
|
|
231
|
+
@return: C{type(B{obj}} if B{C{obj}} is a C{Cartesian} instance of
|
|
232
|
+
the required type, C{False} if a C{Cartesian} of an other
|
|
233
|
+
type or C{None} otherwise.
|
|
234
|
+
'''
|
|
235
|
+
if ellipsoidal is not None:
|
|
236
|
+
try:
|
|
237
|
+
return obj.ellipsoidalCartesian if ellipsoidal else obj.sphericalCartesian
|
|
238
|
+
except AttributeError:
|
|
239
|
+
return None
|
|
240
|
+
return isinstanceof(obj, _MODS.cartesianBase.CartesianBase)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def iscomplex(obj):
|
|
244
|
+
'''Check whether an object is a C{complex} or complex C{str}.
|
|
245
|
+
|
|
246
|
+
@arg obj: The object (any C{type}).
|
|
247
|
+
|
|
248
|
+
@return: C{True} if B{C{obj}} is C{complex}, otherwise
|
|
249
|
+
C{False}.
|
|
250
|
+
'''
|
|
251
|
+
try: # hasattr('conjugate'), hasattr('real') and hasattr('imag')
|
|
252
|
+
return isinstance(obj, complex) or (isstr(obj)
|
|
253
|
+
and isinstance(complex(obj), complex)) # numbers.Complex?
|
|
254
|
+
except (TypeError, ValueError):
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def isDEPRECATED(obj):
|
|
259
|
+
'''Return C{True} if C{B{obj}} is a C{DEPRECATED} class, method
|
|
260
|
+
or function, C{False} if not or C{None} if undetermined.
|
|
261
|
+
'''
|
|
262
|
+
try: # XXX inspect.getdoc(obj)
|
|
263
|
+
return bool(obj.__doc__.lstrip().startswith(_DEPRECATED_))
|
|
264
|
+
except AttributeError:
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def isfloat(obj):
|
|
269
|
+
'''Check whether an object is a C{float} or float C{str}.
|
|
270
|
+
|
|
271
|
+
@arg obj: The object (any C{type}).
|
|
272
|
+
|
|
273
|
+
@return: C{True} if B{C{obj}} is a C{float}, otherwise
|
|
274
|
+
C{False}.
|
|
275
|
+
'''
|
|
276
|
+
try:
|
|
277
|
+
return isinstance( obj, float) or (isstr(obj)
|
|
278
|
+
and isinstance(float(obj), float))
|
|
279
|
+
except (TypeError, ValueError):
|
|
280
|
+
return False
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
isidentifier = str.isidentifier # Python 3, must be str
|
|
285
|
+
except AttributeError: # Python 2-
|
|
286
|
+
|
|
287
|
+
def isidentifier(obj):
|
|
288
|
+
'''Return C{True} if B{C{obj}} is a Python identifier.
|
|
289
|
+
'''
|
|
290
|
+
return bool(obj and isstr(obj)
|
|
291
|
+
and obj.replace(_UNDER_, NN).isalnum()
|
|
292
|
+
and not obj[:1].isdigit())
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def isinstanceof(obj, *classes):
|
|
296
|
+
'''Is B{C{ob}} an instance of one of the C{classes}?
|
|
297
|
+
|
|
298
|
+
@arg obj: The instance (any C{type}).
|
|
299
|
+
@arg classes: One or more classes (C{class}).
|
|
300
|
+
|
|
301
|
+
@return: C{type(B{obj}} if B{C{obj}} is an instance
|
|
302
|
+
of the B{C{classes}}, C{None} otherwise.
|
|
303
|
+
'''
|
|
304
|
+
return type(obj) if isinstance(obj, classes) else None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def isint(obj, both=False):
|
|
308
|
+
'''Check for C{int} type or an integer C{float} value.
|
|
309
|
+
|
|
310
|
+
@arg obj: The object (any C{type}).
|
|
311
|
+
@kwarg both: If C{true}, check C{float} and L{Fsum}
|
|
312
|
+
type and value (C{bool}).
|
|
313
|
+
|
|
314
|
+
@return: C{True} if B{C{obj}} is C{int} or I{integer}
|
|
315
|
+
C{float} or L{Fsum}, C{False} otherwise.
|
|
316
|
+
|
|
317
|
+
@note: Both C{isint(True)} and C{isint(False)} return
|
|
318
|
+
C{False} (and no longer C{True}).
|
|
319
|
+
'''
|
|
320
|
+
if isinstance(obj, _Ints) and not isbool(obj):
|
|
321
|
+
return True
|
|
322
|
+
elif both: # and isinstance(obj, (float, Fsum))
|
|
323
|
+
try: # NOT , _Scalars) to include Fsum!
|
|
324
|
+
return obj.is_integer()
|
|
325
|
+
except AttributeError:
|
|
326
|
+
pass # XXX float(int(obj)) == obj?
|
|
327
|
+
return False
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
from keyword import iskeyword # Python 2.7+
|
|
332
|
+
except ImportError:
|
|
333
|
+
|
|
334
|
+
def iskeyword(unused):
|
|
335
|
+
'''Not Implemented, C{False} always.
|
|
336
|
+
'''
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def isLatLon(obj, ellipsoidal=None):
|
|
341
|
+
'''Is B{C{obj}} some C{LatLon}?
|
|
342
|
+
|
|
343
|
+
@arg obj: The object (any C{type}).
|
|
344
|
+
@kwarg ellipsoidal: If C{None}, return the type of any C{LatLon},
|
|
345
|
+
if C{True}, only an ellipsoidal C{LatLon type}
|
|
346
|
+
or if C{False}, only a spherical C{LatLon type}.
|
|
347
|
+
|
|
348
|
+
@return: C{type(B{obj}} if B{C{obj}} is a C{LatLon} instance of
|
|
349
|
+
the required type, C{False} if a C{LatLon} of an other
|
|
350
|
+
type or {None} otherwise.
|
|
351
|
+
'''
|
|
352
|
+
if ellipsoidal is not None:
|
|
353
|
+
try:
|
|
354
|
+
return obj.ellipsoidalLatLon if ellipsoidal else obj.sphericalLatLon
|
|
355
|
+
except AttributeError:
|
|
356
|
+
return None
|
|
357
|
+
return isinstanceof(obj, _MODS.latlonBase.LatLonBase)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def islistuple(obj, minum=0):
|
|
361
|
+
'''Check for list or tuple C{type} with a minumal length.
|
|
362
|
+
|
|
363
|
+
@arg obj: The object (any C{type}).
|
|
364
|
+
@kwarg minum: Minimal C{len} required C({int}).
|
|
365
|
+
|
|
366
|
+
@return: C{True} if B{C{obj}} is C{list} or C{tuple} with
|
|
367
|
+
C{len} at least B{C{minum}}, C{False} otherwise.
|
|
368
|
+
'''
|
|
369
|
+
return isinstance(obj, _list_tuple_types) and len(obj) >= minum
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def isNvector(obj, ellipsoidal=None):
|
|
373
|
+
'''Is B{C{obj}} some C{Nvector}?
|
|
374
|
+
|
|
375
|
+
@arg obj: The object (any C{type}).
|
|
376
|
+
@kwarg ellipsoidal: If C{None}, return the type of any C{Nvector},
|
|
377
|
+
if C{True}, only an ellipsoidal C{Nvector type}
|
|
378
|
+
or if C{False}, only a spherical C{Nvector type}.
|
|
379
|
+
|
|
380
|
+
@return: C{type(B{obj}} if B{C{obj}} is an C{Nvector} instance of
|
|
381
|
+
the required type, C{False} if an C{Nvector} of an other
|
|
382
|
+
type or {None} otherwise.
|
|
383
|
+
'''
|
|
384
|
+
if ellipsoidal is not None:
|
|
385
|
+
try:
|
|
386
|
+
return obj.ellipsoidalNvector if ellipsoidal else obj.sphericalNvector
|
|
387
|
+
except AttributeError:
|
|
388
|
+
return None
|
|
389
|
+
return isinstanceof(obj, _MODS.nvectorBase.NvectorBase)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def isodd(x):
|
|
393
|
+
'''Is B{C{x}} odd?
|
|
394
|
+
|
|
395
|
+
@arg x: Value (C{scalar}).
|
|
396
|
+
|
|
397
|
+
@return: C{True} if B{C{x}} is odd,
|
|
398
|
+
C{False} otherwise.
|
|
399
|
+
'''
|
|
400
|
+
return bool(int(x) & 1) # == bool(int(x) % 2)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def isscalar(obj):
|
|
404
|
+
'''Check for scalar types.
|
|
405
|
+
|
|
406
|
+
@arg obj: The object (any C{type}).
|
|
407
|
+
|
|
408
|
+
@return: C{True} if B{C{obj}} is C{scalar}, C{False} otherwise.
|
|
409
|
+
'''
|
|
410
|
+
return isinstance(obj, _Scalars) and not isbool(obj)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def issequence(obj, *excls):
|
|
414
|
+
'''Check for sequence types.
|
|
415
|
+
|
|
416
|
+
@arg obj: The object (any C{type}).
|
|
417
|
+
@arg excls: Classes to exclude (C{type}), all positional.
|
|
418
|
+
|
|
419
|
+
@note: Excluding C{tuple} implies excluding C{namedtuple}.
|
|
420
|
+
|
|
421
|
+
@return: C{True} if B{C{obj}} is a sequence, C{False} otherwise.
|
|
422
|
+
'''
|
|
423
|
+
return isinstance(obj, _Seqs) and not (excls and isinstance(obj, excls))
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def isstr(obj):
|
|
427
|
+
'''Check for string types.
|
|
428
|
+
|
|
429
|
+
@arg obj: The object (any C{type}).
|
|
430
|
+
|
|
431
|
+
@return: C{True} if B{C{obj}} is C{str}, C{False} otherwise.
|
|
432
|
+
'''
|
|
433
|
+
return isinstance(obj, _Strs)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def issubclassof(Sub, *Supers):
|
|
437
|
+
'''Check whether a class is a sub-class of some other class(es).
|
|
438
|
+
|
|
439
|
+
@arg Sub: The sub-class (C{class}).
|
|
440
|
+
@arg Supers: One or more C(super) classes (C{class}).
|
|
441
|
+
|
|
442
|
+
@return: C{True} if B{C{Sub}} is a sub-class of any B{C{Supers}},
|
|
443
|
+
C{False} if not (C{bool}) or C{None} if B{C{Sub}} is not
|
|
444
|
+
a class or if no B{C{Supers}} are given or none of those
|
|
445
|
+
are a class.
|
|
446
|
+
'''
|
|
447
|
+
if isclass(Sub):
|
|
448
|
+
t = tuple(S for S in Supers if isclass(S))
|
|
449
|
+
if t:
|
|
450
|
+
return bool(issubclass(Sub, t))
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def itemsorted(adict, *items_args, **asorted_reverse):
|
|
455
|
+
'''Return the items of C{B{adict}} sorted I{alphabetically,
|
|
456
|
+
case-insensitively} and in I{ascending} order.
|
|
457
|
+
|
|
458
|
+
@arg items_args: Optional positional argument(s) for method
|
|
459
|
+
C{B{adict}.items(B*{items_args})}.
|
|
460
|
+
@kwarg asorted_reverse: Use keyword argument C{B{asorted}=False}
|
|
461
|
+
for I{alphabetical, case-sensitive} sorting and
|
|
462
|
+
C{B{reverse}=True} for sorting in C{descending}
|
|
463
|
+
order.
|
|
464
|
+
'''
|
|
465
|
+
def _ins(item):
|
|
466
|
+
return item[0].lower()
|
|
467
|
+
|
|
468
|
+
def _key_rev(asorted=True, reverse=False):
|
|
469
|
+
return (_ins if asorted else None), reverse
|
|
470
|
+
|
|
471
|
+
key, rev = _key_rev(**asorted_reverse)
|
|
472
|
+
items = adict.items(*items_args) if items_args else adict.items()
|
|
473
|
+
return sorted(items, reverse=rev, key=key)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def len2(items):
|
|
477
|
+
'''Make built-in function L{len} work for generators, iterators,
|
|
478
|
+
etc. since those can only be started exactly once.
|
|
479
|
+
|
|
480
|
+
@arg items: Generator, iterator, list, range, tuple, etc.
|
|
481
|
+
|
|
482
|
+
@return: 2-Tuple C{(n, items)} of the number of items (C{int})
|
|
483
|
+
and the items (C{list} or C{tuple}).
|
|
484
|
+
'''
|
|
485
|
+
if not isinstance(items, _Seqs): # NOT hasattr(items, '__len__'):
|
|
486
|
+
items = list(items)
|
|
487
|
+
return len(items), items
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def map1(fun1, *xs): # XXX map_
|
|
491
|
+
'''Apply a single-argument function to each B{C{xs}} and
|
|
492
|
+
return a C{tuple} of results.
|
|
493
|
+
|
|
494
|
+
@arg fun1: 1-Arg function (C{callable}).
|
|
495
|
+
@arg xs: Arguments (C{any positional}).
|
|
496
|
+
|
|
497
|
+
@return: Function results (C{tuple}).
|
|
498
|
+
'''
|
|
499
|
+
return tuple(map(fun1, xs))
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def map2(fun, *xs):
|
|
503
|
+
'''Apply a function to arguments and return a C{tuple} of results.
|
|
504
|
+
|
|
505
|
+
Unlike Python 2's built-in L{map}, Python 3+ L{map} returns a
|
|
506
|
+
L{map} object, an iterator-like object which generates the
|
|
507
|
+
results only once. Converting the L{map} object to a tuple
|
|
508
|
+
maintains the Python 2 behavior.
|
|
509
|
+
|
|
510
|
+
@arg fun: Function (C{callable}).
|
|
511
|
+
@arg xs: Arguments (C{list, tuple, ...}).
|
|
512
|
+
|
|
513
|
+
@return: Function results (C{tuple}).
|
|
514
|
+
'''
|
|
515
|
+
return tuple(map(fun, *xs))
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def neg(x, neg0=None):
|
|
519
|
+
'''Negate C{x} and optionally, negate C{0.0} and C{-0.0}.
|
|
520
|
+
|
|
521
|
+
@kwarg neg0: Defines the return value for zero C{B{x}}: if C{None}
|
|
522
|
+
return C{0.0}, if C{True} return C{NEG0 if B{x}=0.0}
|
|
523
|
+
and C{0.0 if B{x}=NEG0} or if C{False} return C{B{x}}
|
|
524
|
+
I{as-is} (C{bool} or C{None}).
|
|
525
|
+
|
|
526
|
+
@return: C{-B{x} if B{x} else 0.0, NEG0 or B{x}}.
|
|
527
|
+
'''
|
|
528
|
+
return (-x) if x else (_0_0 if neg0 is None else (x if not neg0 else
|
|
529
|
+
(_0_0 if signBit(x) else _MODS.constants.NEG0)))
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def neg_(*xs):
|
|
533
|
+
'''Negate all C{xs} with L{neg}.
|
|
534
|
+
|
|
535
|
+
@return: A C{map(neg, B{xs})}.
|
|
536
|
+
'''
|
|
537
|
+
return map(neg, xs)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _req_d_by(where, **name): # in .basics
|
|
541
|
+
'''(INTERNAL) Get the fully qualified name.
|
|
542
|
+
'''
|
|
543
|
+
m = _MODS.named.modulename(where, prefixed=True)
|
|
544
|
+
if name:
|
|
545
|
+
n = _xkwds_get(name, name=NN)
|
|
546
|
+
if n:
|
|
547
|
+
m = _DOT_(m, n)
|
|
548
|
+
return _SPACE_(_required_, _by_, m)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _reverange(n, stop=-1, step=-1):
|
|
552
|
+
'''(INTERNAL) Reversed range yielding C{n-1, n-1-step, ..., stop+1}.
|
|
553
|
+
'''
|
|
554
|
+
return range(n - 1, stop, step)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def signBit(x):
|
|
558
|
+
'''Return C{signbit(B{x})}, like C++.
|
|
559
|
+
|
|
560
|
+
@return: C{True} if C{B{x} < 0} or C{NEG0} (C{bool}).
|
|
561
|
+
'''
|
|
562
|
+
return x < 0 or _MODS.constants.isneg0(x)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _signOf(x, ref): # in .fsums
|
|
566
|
+
'''(INTERNAL) Return the sign of B{C{x}} versus B{C{ref}}.
|
|
567
|
+
'''
|
|
568
|
+
return +1 if x > ref else (-1 if x < ref else 0)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def signOf(x):
|
|
572
|
+
'''Return sign of C{x} as C{int}.
|
|
573
|
+
|
|
574
|
+
@return: -1, 0 or +1 (C{int}).
|
|
575
|
+
'''
|
|
576
|
+
try:
|
|
577
|
+
s = x.signOf() # Fsum instance?
|
|
578
|
+
except AttributeError:
|
|
579
|
+
s = _signOf(x, 0)
|
|
580
|
+
return s
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _sizeof(inst):
|
|
584
|
+
'''(INTERNAL) Recursively size an C{inst}ance.
|
|
585
|
+
|
|
586
|
+
@return: Instance' size in bytes (C{int}),
|
|
587
|
+
ignoring class attributes and
|
|
588
|
+
counting duplicates only once or
|
|
589
|
+
C{None}.
|
|
590
|
+
|
|
591
|
+
@note: With C{PyPy}, the size is always C{None}.
|
|
592
|
+
'''
|
|
593
|
+
try:
|
|
594
|
+
_zB = _sys.getsizeof
|
|
595
|
+
_zD = _zB(None) # get some default
|
|
596
|
+
except TypeError: # PyPy3.10
|
|
597
|
+
return None
|
|
598
|
+
|
|
599
|
+
def _zR(s, iterable):
|
|
600
|
+
z, _s = 0, s.add
|
|
601
|
+
for o in iterable:
|
|
602
|
+
i = id(o)
|
|
603
|
+
if i not in s:
|
|
604
|
+
_s(i)
|
|
605
|
+
z += _zB(o, _zD)
|
|
606
|
+
if isinstance(o, dict):
|
|
607
|
+
z += _zR(s, o.keys())
|
|
608
|
+
z += _zR(s, o.values())
|
|
609
|
+
elif isinstance(o, _list_tuple_set_types):
|
|
610
|
+
z += _zR(s, o)
|
|
611
|
+
else:
|
|
612
|
+
try: # size instance' attr values only
|
|
613
|
+
z += _zR(s, o.__dict__.values())
|
|
614
|
+
except AttributeError: # None, int, etc.
|
|
615
|
+
pass
|
|
616
|
+
return z
|
|
617
|
+
|
|
618
|
+
return _zR(set(), (inst,))
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def splice(iterable, n=2, **fill):
|
|
622
|
+
'''Split an iterable into C{n} slices.
|
|
623
|
+
|
|
624
|
+
@arg iterable: Items to be spliced (C{list}, C{tuple}, ...).
|
|
625
|
+
@kwarg n: Number of slices to generate (C{int}).
|
|
626
|
+
@kwarg fill: Optional fill value for missing items.
|
|
627
|
+
|
|
628
|
+
@return: A generator for each of B{C{n}} slices,
|
|
629
|
+
M{iterable[i::n] for i=0..n}.
|
|
630
|
+
|
|
631
|
+
@raise TypeError: Invalid B{C{n}}.
|
|
632
|
+
|
|
633
|
+
@note: Each generated slice is a C{tuple} or a C{list},
|
|
634
|
+
the latter only if the B{C{iterable}} is a C{list}.
|
|
635
|
+
|
|
636
|
+
@example:
|
|
637
|
+
|
|
638
|
+
>>> from pygeodesy import splice
|
|
639
|
+
|
|
640
|
+
>>> a, b = splice(range(10))
|
|
641
|
+
>>> a, b
|
|
642
|
+
((0, 2, 4, 6, 8), (1, 3, 5, 7, 9))
|
|
643
|
+
|
|
644
|
+
>>> a, b, c = splice(range(10), n=3)
|
|
645
|
+
>>> a, b, c
|
|
646
|
+
((0, 3, 6, 9), (1, 4, 7), (2, 5, 8))
|
|
647
|
+
|
|
648
|
+
>>> a, b, c = splice(range(10), n=3, fill=-1)
|
|
649
|
+
>>> a, b, c
|
|
650
|
+
((0, 3, 6, 9), (1, 4, 7, -1), (2, 5, 8, -1))
|
|
651
|
+
|
|
652
|
+
>>> tuple(splice(list(range(9)), n=5))
|
|
653
|
+
([0, 5], [1, 6], [2, 7], [3, 8], [4])
|
|
654
|
+
|
|
655
|
+
>>> splice(range(9), n=1)
|
|
656
|
+
<generator object splice at 0x0...>
|
|
657
|
+
'''
|
|
658
|
+
if not isint(n):
|
|
659
|
+
raise _TypeError(n=n)
|
|
660
|
+
|
|
661
|
+
t = iterable
|
|
662
|
+
if not isinstance(t, _list_tuple_types):
|
|
663
|
+
t = tuple(t) # force tuple, also for PyPy3
|
|
664
|
+
|
|
665
|
+
if n > 1:
|
|
666
|
+
if fill:
|
|
667
|
+
fill = _xkwds_get(fill, fill=MISSING)
|
|
668
|
+
if fill is not MISSING:
|
|
669
|
+
m = len(t) % n
|
|
670
|
+
if m > 0: # same type fill
|
|
671
|
+
t += type(t)((fill,) * (n - m))
|
|
672
|
+
for i in range(n):
|
|
673
|
+
# XXX t[i::n] chokes PyChecker
|
|
674
|
+
yield t[slice(i, None, n)]
|
|
675
|
+
else:
|
|
676
|
+
yield t
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def _splituple(strs, *sep_splits): # in .mgrs, .osgr, .webmercator
|
|
680
|
+
'''(INTERNAL) Split a C{comma}- or C{whitespace}-separated
|
|
681
|
+
string into a C{tuple} of stripped strings.
|
|
682
|
+
'''
|
|
683
|
+
t = (strs.split(*sep_splits) if sep_splits else
|
|
684
|
+
strs.replace(_COMMA_, _SPACE_).split()) if strs else ()
|
|
685
|
+
return tuple(s.strip() for s in t if s)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def unsigned0(x):
|
|
689
|
+
'''Unsign if C{0.0}.
|
|
690
|
+
|
|
691
|
+
@return: C{B{x}} if B{C{x}} else C{0.0}.
|
|
692
|
+
'''
|
|
693
|
+
return x if x else _0_0
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def _xcopy(obj, deep=False):
|
|
697
|
+
'''(INTERNAL) Copy an object, shallow or deep.
|
|
698
|
+
|
|
699
|
+
@arg obj: The object to copy (any C{type}).
|
|
700
|
+
@kwarg deep: If C{True} make a deep, otherwise
|
|
701
|
+
a shallow copy (C{bool}).
|
|
702
|
+
|
|
703
|
+
@return: The copy of B{C{obj}}.
|
|
704
|
+
'''
|
|
705
|
+
return _deepcopy(obj) if deep else _copy(obj)
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def _xdup(obj, deep=False, **items):
|
|
709
|
+
'''(INTERNAL) Duplicate an object, replacing some attributes.
|
|
710
|
+
|
|
711
|
+
@arg obj: The object to copy (any C{type}).
|
|
712
|
+
@kwarg deep: If C{True} copy deep, otherwise shallow.
|
|
713
|
+
@kwarg items: Attributes to be changed (C{any}).
|
|
714
|
+
|
|
715
|
+
@return: A duplicate of B{C{obj}} with modified
|
|
716
|
+
attributes, if any B{C{items}}.
|
|
717
|
+
|
|
718
|
+
@raise AttributeError: Some B{C{items}} invalid.
|
|
719
|
+
'''
|
|
720
|
+
d = _xcopy(obj, deep=deep)
|
|
721
|
+
for n, v in items.items():
|
|
722
|
+
if getattr(d, n, v) != v:
|
|
723
|
+
setattr(d, n, v)
|
|
724
|
+
elif not hasattr(d, n):
|
|
725
|
+
t = _MODS.named.classname(obj)
|
|
726
|
+
t = _SPACE_(_DOT_(t, n), _invalid_)
|
|
727
|
+
raise _AttributeError(txt=t, obj=obj, **items)
|
|
728
|
+
# if items:
|
|
729
|
+
# _MODS.props._update_all(d)
|
|
730
|
+
return d
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _xgeographiclib(where, *required):
|
|
734
|
+
'''(INTERNAL) Import C{geographiclib} and check required version.
|
|
735
|
+
'''
|
|
736
|
+
try:
|
|
737
|
+
_xpackage(_xgeographiclib)
|
|
738
|
+
import geographiclib
|
|
739
|
+
except ImportError as x:
|
|
740
|
+
raise _xImportError(x, where, Error=LazyImportError)
|
|
741
|
+
return _xversion(geographiclib, where, *required)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
def _xImportError(exc, where, Error=_ImportError, **name):
|
|
745
|
+
'''(INTERNAL) Embellish an C{Lazy/ImportError}.
|
|
746
|
+
'''
|
|
747
|
+
t = _req_d_by(where, **name)
|
|
748
|
+
return Error(_Xstr(exc), txt=t, cause=exc)
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def _xinstanceof(*Types, **names_values):
|
|
752
|
+
'''(INTERNAL) Check C{Types} of all C{name=value} pairs.
|
|
753
|
+
|
|
754
|
+
@arg Types: One or more classes or types (C{class}), all
|
|
755
|
+
positional.
|
|
756
|
+
@kwarg names_values: One or more C{B{name}=value} pairs
|
|
757
|
+
with the C{value} to be checked.
|
|
758
|
+
|
|
759
|
+
@raise TypeError: One B{C{names_values}} pair is not an
|
|
760
|
+
instance of any of the B{C{Types}}.
|
|
761
|
+
'''
|
|
762
|
+
if not (Types and names_values):
|
|
763
|
+
raise _xAssertionError(_xinstanceof, *Types, **names_values)
|
|
764
|
+
|
|
765
|
+
for n, v in names_values.items():
|
|
766
|
+
if not isinstance(v, Types):
|
|
767
|
+
raise _TypesError(n, v, *Types)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def _xisscalar(**names_values):
|
|
771
|
+
'''(INTERNAL) Check all C{name=value} pairs to be C{scalar}.
|
|
772
|
+
'''
|
|
773
|
+
for n, v in names_values.items():
|
|
774
|
+
if not isscalar(v):
|
|
775
|
+
raise _TypeError(n, v, txt=_not_scalar_)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _xnumpy(where, *required):
|
|
779
|
+
'''(INTERNAL) Import C{numpy} and check required version.
|
|
780
|
+
'''
|
|
781
|
+
try:
|
|
782
|
+
_xpackage(_xnumpy)
|
|
783
|
+
import numpy
|
|
784
|
+
except ImportError as x:
|
|
785
|
+
raise _xImportError(x, where)
|
|
786
|
+
return _xversion(numpy, where, *required)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def _xor(x, *xs):
|
|
790
|
+
'''(INTERNAL) Exclusive-or C{x} and C{xs}.
|
|
791
|
+
'''
|
|
792
|
+
for x_ in xs:
|
|
793
|
+
x ^= x_
|
|
794
|
+
return x
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def _xpackage(_xpkg):
|
|
798
|
+
'''(INTERNAL) Check dependency to be excluded.
|
|
799
|
+
'''
|
|
800
|
+
n = _xpkg.__name__[2:] # remove _x
|
|
801
|
+
if n in _XPACKAGES:
|
|
802
|
+
x = _SPACE_(n, _in_, _PYGEODESY_XPACKAGES_)
|
|
803
|
+
e = _enquote(_getenv(_PYGEODESY_XPACKAGES_, NN))
|
|
804
|
+
raise ImportError(_EQUAL_(x, e))
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def _xscipy(where, *required):
|
|
808
|
+
'''(INTERNAL) Import C{scipy} and check required version.
|
|
809
|
+
'''
|
|
810
|
+
try:
|
|
811
|
+
_xpackage(_xscipy)
|
|
812
|
+
import scipy
|
|
813
|
+
except ImportError as x:
|
|
814
|
+
raise _xImportError(x, where)
|
|
815
|
+
return _xversion(scipy, where, *required)
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _xsubclassof(*Classes, **names_values):
|
|
819
|
+
'''(INTERNAL) Check (super) class of all C{name=value} pairs.
|
|
820
|
+
|
|
821
|
+
@arg Classes: One or more classes or types (C{class}), all
|
|
822
|
+
positional.
|
|
823
|
+
@kwarg names_values: One or more C{B{name}=value} pairs
|
|
824
|
+
with the C{value} to be checked.
|
|
825
|
+
|
|
826
|
+
@raise TypeError: One B{C{names_values}} pair is not a
|
|
827
|
+
(sub-)class of any of the B{C{Classes}}.
|
|
828
|
+
'''
|
|
829
|
+
if not (Classes and names_values):
|
|
830
|
+
raise _xAssertionError(_xsubclassof, *Classes, **names_values)
|
|
831
|
+
|
|
832
|
+
for n, v in names_values.items():
|
|
833
|
+
if not issubclassof(v, *Classes):
|
|
834
|
+
raise _TypesError(n, v, *Classes)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def _xversion(package, where, *required, **name):
|
|
838
|
+
'''(INTERNAL) Check the C{package} version vs B{C{required}}.
|
|
839
|
+
'''
|
|
840
|
+
if required:
|
|
841
|
+
t = _version_info(package)
|
|
842
|
+
if t[:len(required)] < required:
|
|
843
|
+
t = _SPACE_(package.__name__,
|
|
844
|
+
_version_, _DOT_(*t),
|
|
845
|
+
_below_, _DOT_(*required),
|
|
846
|
+
_req_d_by(where, **name))
|
|
847
|
+
raise ImportError(t)
|
|
848
|
+
return package
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
def _xzip(*args, **strict): # PYCHOK no cover
|
|
852
|
+
'''(INTERNAL) Standard C{zip(..., strict=True)}.
|
|
853
|
+
'''
|
|
854
|
+
s = _xkwds_get(strict, strict=True)
|
|
855
|
+
if s:
|
|
856
|
+
if _zip is zip: # < (3, 10)
|
|
857
|
+
t = _MODS.streprs.unstr(_xzip, *args, strict=s)
|
|
858
|
+
raise _NotImplementedError(t, txt=None)
|
|
859
|
+
return _zip(*args)
|
|
860
|
+
return zip(*args)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
if _sys_version_info2 < (3, 10): # see .errors
|
|
864
|
+
_zip = zip # PYCHOK exported
|
|
865
|
+
else: # Python 3.10+
|
|
866
|
+
|
|
867
|
+
def _zip(*args):
|
|
868
|
+
return zip(*args, strict=True)
|
|
869
|
+
|
|
870
|
+
_XPACKAGES = _splituple(_getenv(_PYGEODESY_XPACKAGES_, NN).lower())
|
|
871
|
+
|
|
872
|
+
# **) MIT License
|
|
873
|
+
#
|
|
874
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
875
|
+
#
|
|
876
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
877
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
878
|
+
# to deal in the Software without restriction, including without limitation
|
|
879
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
880
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
881
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
882
|
+
#
|
|
883
|
+
# The above copyright notice and this permission notice shall be included
|
|
884
|
+
# in all copies or substantial portions of the Software.
|
|
885
|
+
#
|
|
886
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
887
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
888
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
889
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
890
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
891
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
892
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|