Coverage for pygeodesy/lazily.py: 96%
216 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-26 09:04 -0400
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-26 09:04 -0400
2# -*- coding: utf-8 -*-
4u'''Lazily import C{pygeodesy} modules and attributes, based on
5U{lazy_import<https://modutil.ReadTheDocs.io/en/latest/#lazy_import>}
6from I{Brett Cannon}'s U{modutil<https://PyPI.org/project/modutil>}.
8C{Lazy import} is I{supported only for }U{Python 3.7+
9<https://Snarky.Ca/lazy-importing-in-python-3-7>} and is I{enabled by
10default} in U{PyGeodesy 18.11.10<https://PyPI.org/project/PyGeodesy>}
11I{and newer}.
13To I{enable} C{lazy import}, set C{env} variable C{PYGEODESY_LAZY_IMPORT}
14to C{1}, C{2}, C{3} or higher prior to C{import pygeodesy}. To I{disable}
15C{lazy import}, set C{env} variable C{PYGEODESY_LAZY_IMPORT} to C{0} or
16an empty string. Use C{2} or higher to print a message for each lazily
17imported module and attribute, similar to C{env} variable C{PYTHONVERBOSE}
18showing imports. Using C{3} or higher also shows the importing file name
19and line number.
21@note: C{Lazy import} applies only to top-level modules of C{pygeodesy}.
22 The C{lazy import} of a top-level module invariably loads all
23 sub-modules imported by that top-level module.
25@note: C{Lazy import} raises a L{LazyAttributeError} or L{LazyImportError}
26 depending on the cause of the error and such errors can occur late,
27 after all initial imports.
28'''
30from pygeodesy import internals as _internals, interns as _interns, \
31 _isfrozen # DON'T _lazy_import2
32# from pygeodesy.errors import _error_init, _ImmutableError, _xkwds_get, _xkwds_item2 # _ALL_MODS
33from pygeodesy.internals import _caller3, _envPYGEODESY, _headof, printf, _Property_RO, \
34 _tailof, typename, _versions # _getenv, _PYGEODESY_ENV, \
35# _MODS_Base, _MODS.sys_version_info2
36from pygeodesy.interns import _attribute_, _by_, _COLONSPACE_, _COMMASPACE_, _DALL_, \
37 _DMAIN_, _doesn_t_exist_, _DOT_, _EQUALSPACED_, _from_, \
38 _HASH_, _line_, _module_, NN, _no_, _not_, _pygeodesy_, \
39 _pygeodesy_abspath_, _SPACE_, _SUB_PACKAGES, _or_, \
40 _UNDER_, _version_, _sys, _intern # function, _1_
41try:
42 from importlib import import_module # in .rdnap._v_grid
43except ImportError as x: # Python 2.6-
44 raise ImportError(_COLONSPACE_(x, _versions()))
45# import sys as _sys # from .interns
47_a0 = () # PYCHOK empty tuple
48_asSPACED_ = ' as '
49_FOR_DOCS = _envPYGEODESY('FOR_DOCS') # for epydoc ...
50_imported_ = 'imported'
51_init__all__ = _FOR_DOCS or _envPYGEODESY('_init__all__', _DALL_) == _DALL_ # PYCHOK exported
52_lazily_ = 'lazily'
53_PYTHON_X_DEV = getattr(_sys.flags, 'dev_mode', False) # PYCHOK Python 3.2+
54_unlazy = _unLazy0 = _isfrozen or _internals._MODS.sys_version_info2 < (3, 7) # PYCHOK mod.__getattr__ 3.7+
55_WARNINGS_X_DEV = _envPYGEODESY('WARNINGS') and (_PYTHON_X_DEV or bool(_sys.warnoptions)) # PYCHOK .props
57# @module_property[_RO?] <https://GitHub.com/jtushman/proxy_tools/> <https://discuss.Python.org/t/47379>
58isLazy = None # see @var isLazy in .__init__
61class LazyAttributeError(AttributeError):
62 '''Raised if a C{lazily imported} attribute is missing or invalid.
63 '''
64 def __init__(self, *args, **kwds):
65 _ALL_MODS.errors._error_init(AttributeError, self, args, **kwds)
68class LazyImportError(ImportError):
69 '''Raised if C{lazy import} is not supported, disabled or failed.
70 '''
71 def __init__(self, *args, **kwds):
72 _ALL_MODS.errors._error_init(ImportError, self, args, **kwds)
75class _Dict(dict):
76 '''(INTERNAL) Imports C{dict}.
77 '''
78 _name = NN
80 def __getattr__(self, attr):
81 try:
82 return self[attr]
83 except KeyError:
84 return dict.__getattr__(self, attr) # PYLINT E1101
86# def __setattr__(self, attr, value):
87# if attr in self:
88# self[attr] = value
89# else:
90# dict.__setattr__(self, attr, value)
92 def add(self, name, mod_, *subs):
93 '''Add a C{[name] = mod_} item.
95 @raise AssertionError: The B{C{name}} already exists
96 with a different B{C{mod_}}.
97 '''
98 try:
99 sub = self[name] # duplicate OK
100 if sub != mod_ and sub not in subs:
101 t = _DOT_(self._name, name)
102 t = _COLONSPACE_(t, repr(sub))
103 t = _COMMASPACE_(t, _not_(repr(mod_)))
104 raise AssertionError(t)
105 except KeyError:
106 self[name] = mod_
108 def _NAME(self, which):
109 self._name = _intern(typename(which).upper())
112class _NamedEnum_RO(dict):
113 '''(INTERNAL) C{Read_Only} enum-like C{dict} sub-class.
114 '''
115# _name = NN # also first kwd, __init__(_name=...)
117 def _DOT_(self, attr): # PYCHOK no cover
118 return _DOT_(self._name, attr) # PYCHOK _name
120 def __getattr__(self, attr):
121 try:
122 return self[attr]
123 except KeyError:
124 t = self._DOT_(attr)
125 raise LazyAttributeError(t, txt=_doesn_t_exist_)
127 def __setattr__(self, attr, value): # PYCHOK no cover
128 e = _ALL_MODS.errors
129 raise e._ImmutableError(self, attr, value,
130 Error=LazyAttributeError)
132 def enums(self):
133 # Yield all C{(mod_, tuple)} pairs
134 for m, t in dict.items(self):
135 n = m.replace(_UNDER_, _DOT_)
136 if n != m:
137 if m.startswith(_UNDER_):
138 continue # skip _name= ...
139 u = m.rstrip(_UNDER_)
140 if u != m:
141 u = len(u)
142 n = n[:u] + m[u:]
143 yield n, t
145 def fill_D(self, _D, which):
146 # Fill C{_Dict _D}.
147 _D._NAME(which)
148 _a = _D.add
149 for m, t in self.enums():
150 _a(m, _DOT_(m, NN, NN)) # import module
151 for a in t:
152 a, _, as_ = a.partition(_asSPACED_)
153 if as_: # import attr as attr_
154 _a(as_, _DOT_(m, a, NN), *_SUB_PACKAGES)
155 else:
156 _a(a, m)
157 return _D
160def _a(*names):
161 '''(INTERNAL) Intern all C{names}.
162 '''
163 return tuple(map(_intern, names)) if names else _a0
166def _ALL_ATTRS(*attrs):
167 '''(INTERNAL) Unravel all exported module attributes.
168 '''
169 t = ()
170 for attr in attrs:
171 t += tuple(map(_getattras, attr))
172 return t
175_ALL_INIT = _a(_pygeodesy_abspath_, _version_)
177# __all__ value for most modules, accessible as _ALL_LAZY.<module>
178_ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
179 albers=_a('AlbersEqualArea', 'AlbersEqualArea2', 'AlbersEqualArea4',
180 'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth',
181 'AlbersError', 'Albers7Tuple'),
182 angles=_a('Ang', 'Deg', 'Lambertian', 'Rad', 'isAng'),
183 auxilats=_a(), # package only
184 azimuthal=_a('AzimuthalError', 'Azimuthal7Tuple',
185 'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney',
186 'Gnomonic', 'GnomonicExact', 'GnomonicGeodSolve', 'GnomonicKarney',
187 'LambertEqualArea', 'Orthographic', 'Stereographic',
188 'equidistant', 'gnomonic'),
189 basics=_a('clips', 'copysign0', 'copytype', 'halfs2',
190 'int1s', 'isbool', 'isCartesian', 'isclass', 'iscomplex', 'isDEPRECATED', 'isfloat',
191 'isidentifier', 'isinstanceof', 'isint', 'isiterable', 'isiterablen', 'isiterabletype',
192 'iskeyword', 'isLatLon', 'islistuple', 'isNvector', 'isodd',
193 'isscalar', 'issequence', 'isstr', 'issubclassof', 'itemsorted',
194 'len2', 'map1', 'map2', 'max2', 'min2', 'neg', 'neg_',
195 'signBit', 'signOf', 'splice', 'str2ub', 'ub2str', 'unsigned0'),
196 booleans=_a('BooleanFHP', 'BooleanGH', 'LatLonFHP', 'LatLonGH',
197 'isBoolean'),
198 cartesianBase=_a('RadiusThetaPhi3Tuple', 'rtp2xyz', 'rtp2xyz_', 'xyz2rtp', 'xyz2rtp_'),
199 clipy=_a('ClipCS4Tuple', 'ClipFHP4Tuple', 'ClipGH4Tuple', 'ClipLB6Tuple', 'ClipSH3Tuple',
200 'clipCS4', 'clipFHP4', 'clipGH4', 'clipLB6', 'clipSH', 'clipSH3'),
201 css=_a('CassiniSoldner', 'Css', 'CSSError', 'toCss',
202 'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'),
203 constants=_a('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS8', 'EPS_2',
204 'INF', 'INT0', 'MANT_DIG', 'MAX', 'MAX_EXP', 'MIN', 'MIN_EXP', 'NAN', 'NEG0', 'NINF',
205 'OVERFLOW', 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4', 'PI_6',
206 'R_FM', 'R_GM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_QM', 'R_SM', 'R_VM',
207 'float_', 'float0_', 'floats_', 'isclose', 'isfinite', 'isinf', 'isint0',
208 'isnan', 'isnear0', 'isnear1', 'isnear90', 'isneg', 'isneg0', 'isninf', 'isnon0',
209 'remainder'),
210 datums=_a('Datum', 'Datums', 'Similarity', 'Transform', 'Transforms'),
211# deprecated=_a(), # module only
212 dms=_a('F_D', 'F_DM', 'F_DMS', 'F_DEG', 'F_MIN', 'F_SEC', 'F_D60', 'F__E', 'F__F', 'F__G', 'F_RAD',
213 'F_D_', 'F_DM_', 'F_DMS_', 'F_DEG_', 'F_MIN_', 'F_SEC_', 'F_D60_', 'F__E_', 'F__F_', 'F__G_', 'F_RAD_',
214 'F_D__', 'F_DM__', 'F_DMS__', 'F_DEG__', 'F_MIN__', 'F_SEC__', 'F_D60__', 'F__E__', 'F__F__', 'F__G__', 'F_RAD__',
215 'S_DEG', 'S_MIN', 'S_SEC', 'S_DMS', 'S_RAD', 'S_SEP',
216 'bearingDMS', 'clipDegrees', 'clipRadians', 'compassDMS', 'compassPoint',
217 'degDMS', 'latDMS', 'latlonDMS', 'latlonDMS_', 'lonDMS', 'normDMS',
218 'parseDDDMMSS', 'parseDMS', 'parseDMS2', 'parse3llh', 'parseRad', 'precision', 'toDMS'),
219 ecef=_a('EcefFarrell21', 'EcefFarrell22', 'EcefFukushima', 'EcefKarney', 'EcefOlson', 'EcefOsen',
220 'EcefSudano', 'EcefUPC', 'EcefVeness', 'EcefYou', 'EcefError', 'EcefMatrix', 'Ecef9Tuple'),
221 ecefLocals=_a(), # module only
222 elevations=_a('Elevation2Tuple', 'GeoidHeight2Tuple',
223 'elevation2', 'geoidHeight2'),
224 ellipses=_a('Ellipse', 'EllipseError'),
225 ellipsoidalBase=_a(), # module only
226 ellipsoidalBaseDI=_a(), # module only
227 ellipsoidalExact=_a(), # module only
228 ellipsoidalGeodSolve=_a(), # module only
229 ellipsoidalKarney=_a(), # module only
230 ellipsoidalNvector=_a(), # module only
231 ellipsoidalVincenty=_a('VincentyError',), # nothing else
232 ellipsoids=_a('a_f2Tuple', 'Curvature2Tuple',
233 'Ellipsoid', 'Ellipsoid2', 'Ellipsoids',
234 'a_b2e', 'a_b2e2', 'a_b2e22', 'a_b2e32', 'a_b2f', 'a_b2f_', 'a_b2f2', 'a_b2n',
235 'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a',
236 'e2f', 'e22f',
237 'f2e2', 'f2e22', 'f2e32', 'f_2f', 'f2f_', 'f2f2', 'f2n', 'n2e2', 'n2f', 'n2f_'),
238 elliptic=_a('Elliptic', 'EllipticError', 'Elliptic3Tuple'),
239 epsg=_a('Epsg', 'EPSGError'),
240 errors=_a('AuxError', 'ClipError', 'CrossError', 'GeodesicError', 'IntersectionError',
241 'NumPyError', 'LenError', 'LimitError', 'MGRSError',
242 'ParseError', 'PointsError', 'RangeError', 'RhumbError',
243 'SciPyError', 'SciPyWarning', 'TRFError', 'TriangleError', 'UnitError', 'VectorError',
244 'crosserrors', 'exception_chaining', 'isError', 'limiterrors', 'rangerrors'),
245 etm=_a('Etm', 'ETMError', 'ExactTransverseMercator',
246 'parseETM5', 'toEtm8'),
247 fmath=_a('Fdot', 'Fdot_', 'Fhorner', 'Fhypot', 'Fpolynomial', 'Fpowers', 'Fcbrt', 'Froot', 'Fsqrt',
248 'bqrt', 'cbrt', 'cbrt2', 'euclid', 'euclid_',
249 'facos1', 'fasin1', 'fatan', 'fatan1', 'fatan2', 'favg',
250 'fdot', 'fdot_', 'fdot3', 'fma', 'fmean', 'fmean_', 'fhorner', 'fidw', 'f2mul_',
251 'fpolynomial', 'fpowers', 'fprod', 'frandoms', 'frange', 'freduce', 'fremainder',
252 'hypot', 'hypot_', 'hypot1', 'hypot2', 'hypot2_',
253 'norm2', 'norm_', 'polar2', 'polar2d',
254 'sqrt0', 'sqrt3', 'sqrt_a', 'zcrt', 'zqrt'),
255 formy=_a('Radical2Tuple',
256 'angle2chord', 'antipode', 'antipode_', 'bearing', 'bearing_',
257 'chord2angle', 'compassAngle', 'cosineLaw', 'cosineLaw_',
258 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',
259 'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_',
260 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_',
261 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_',
262 'hartzell', 'haversine', 'haversine_', 'heightOf', 'heightOrthometric', 'horizon', 'hubeny', 'hubeny_',
263 'intersection2', 'intersections2', 'isantipode', 'isantipode_', 'isnormal', 'isnormal_',
264 'normal', 'normal_', 'opposing', 'opposing_', 'radical2',
265 'thomas', 'thomas_', 'vincentys', 'vincentys_'),
266 frechet=_a('Frechet', 'FrechetDegrees', 'FrechetError', 'FrechetRadians', 'FrechetCosineLaw',
267 'FrechetDistanceTo', 'FrechetEquirectangular', 'FrechetEuclidean', 'FrechetExact',
268 'FrechetFlatLocal', 'FrechetFlatPolar', 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney',
269 'FrechetThomas', 'FrechetVincentys', 'Frechet6Tuple',
270 'frechet_'),
271 fstats=_a('Fcook', 'Flinear', 'Fwelford'),
272 fsums=_a('Fsum', 'DivMod2Tuple', 'Fsum2Tuple', 'ResidualError',
273 'f2product', 'fsum', 'fsum_', 'fsumf_', 'fsum1', 'fsum1_', 'fsum1f_', 'nonfiniterrors'),
274 gars=_a('Garef', 'GARSError'),
275 geodesici=_a('Intersectool', 'Intersectool5Tuple', 'Intersect7Tuple',
276 'Intersector', 'Intersector5Tuple', 'Middle5Tuple', 'XDict'),
277 geodesicw=_a('Geodesic', 'GeodesicLine', 'Geodesic_WGS84'),
278 geodesicx=_a('gx', 'gxarea', 'gxbases', 'gxline', # modules
279 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'),
280 geodsolve=_a('GeodesicSolve', 'GeodesicLineSolve', 'GeodSolve12Tuple'),
281 geod3solve=_a('Geodesic3Solve', 'GeodesicLine3Solve', 'Geod3Solve8Tuple', 'Geodesic3Error'),
282 geohash=_a('Geohash', 'Geohashed', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple', 'Sizes3Tuple'),
283 geoids=_a('GeoidEGM96', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'GeoidQuasi',
284 'GeoidError', 'GeoidHeight5Tuple', 'PGMError', 'egmGeoidHeights'),
285 hausdorff=_a('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians', 'HausdorffCosineLaw',
286 'HausdorffDistanceTo', 'HausdorffEquirectangular', 'HausdorffEuclidean', 'HausdorffExact',
287 'HausdorffFlatLocal', 'HausdorffFlatPolar', 'HausdorffHaversine', 'HausdorffHubeny',
288 'HausdorffKarney', 'HausdorffThomas', 'HausdorffVincentys', 'Hausdorff6Tuple',
289 'hausdorff_', 'randomrangenerator'),
290 heights=_a('HeightCubic', 'HeightError', 'HeightIDWcosineLaw', 'HeightIDWdistanceTo',
291 'HeightIDWequirectangular', 'HeightIDWeuclidean', 'HeightIDWexact', 'HeightIDWflatLocal',
292 'HeightIDWflatPolar', 'HeightIDWhaversine', 'HeightIDWhubeny', 'HeightIDWkarney', 'HeightIDWthomas',
293 'HeightIDWvincentys', 'HeightLinear', 'HeightLSQBiSpline', 'HeightSmoothBiSpline'),
294 hom=_a('HotineError', 'HotineOblique1', 'HotineOblique2', 'HotineOblique3',
295 'HotineObliqueA', 'HotineObliqueB', 'Hotine8Tuple', 'HotineAB14Tuple'),
296 internals=_internals.__all__,
297 interns=_interns.__all__,
298 iters=_a('LatLon2PsxyIter', 'PointsIter', 'points2',
299 'isNumpy2', 'isPoints2', 'isTuple2', 'iterNumpy2', 'iterNumpy2over'),
300 karney=_a('Area3Tuple', 'Caps', 'Direct9Tuple', 'GDict', 'Inverse10Tuple'),
301 ktm=_a('KTMError', 'KTransverseMercator'),
302 latlonBase=_a('latlon2n_xyz', 'philam2n_xyz'),
303 lazily=_a('LazyAttributeError', 'LazyImportError', 'isLazy'),
304 lcc=_a('Conic', 'Conics', 'Lcc', 'LCCError', 'toLcc'),
305 lom=_a('LabordeError', 'LabordeOblique', 'Laborde8Tuple'),
306 ltp=_a('Attitude', 'AttitudeError', 'Frustum',
307 'LocalCartesian', 'LocalError', 'Ltp', 'LqRD', 'tyr3d'),
308 ltpTuples=_a('Aer', 'Aer4Tuple', 'Attitude4Tuple',
309 'Enu', 'Enu4Tuple', 'Footprint5Tuple', 'Local9Tuple', 'Los',
310 'Ned', 'Ned4Tuple', 'Uvw', 'Uvw3Tuple', 'XyzLocal', 'Xyz4Tuple'),
311 mgrs=_a('Mgrs', 'parseMGRS', 'toMgrs', 'Mgrs4Tuple', 'Mgrs6Tuple'),
312 named=_a('ADict',
313 'callername', 'classname', 'classnaming', 'modulename',
314 'nameof', 'notImplemented', 'notOverloaded'),
315 namedTuples=_a('Bearing2Tuple', 'Bounds2Tuple', 'Bounds4Tuple',
316 'Circle4Tuple', 'Destination2Tuple', 'Destination3Tuple',
317 'Distance2Tuple', 'Distance3Tuple', 'Distance4Tuple',
318 'EasNor2Tuple', 'EasNor3Tuple', 'Ellipse5Tuple',
319 'Forward4Tuple', 'Intersection3Tuple',
320 'LatLon2Tuple', 'LatLon3Tuple', 'LatLon4Tuple',
321 'LatLonDatum3Tuple', 'LatLonDatum5Tuple', 'LatLonHeight3Tuple', 'LatLonNgeoid3Tuple',
322 'LatLonPrec3Tuple', 'LatLonPrec5Tuple',
323 'NearestOn2Tuple', 'NearestOn3Tuple', 'NearestOn6Tuple', 'NearestOn8Tuple',
324 'PhiLam2Tuple', 'PhiLam3Tuple', 'PhiLam4Tuple', 'PhiLamHeight3Tuple', 'PhiLamNgeoid3Tuple',
325 'Point3Tuple', 'Points2Tuple',
326 'RD4Tuple', 'Reverse4Tuple', 'Triangle7Tuple', 'Triangle8Tuple', 'Trilaterate5Tuple',
327 'UtmUps2Tuple', 'UtmUps5Tuple', 'UtmUps8Tuple', 'UtmUpsLatLon5Tuple',
328 'Vector2Tuple', 'Vector3Tuple', 'Vector4Tuple'),
329 nvectorBase=_a('NorthPole', 'SouthPole', 'n_xyz2latlon', 'n_xyz2philam'),
330 osgr=_a('Osgr', 'OSGRError', 'parseOSGR', 'toOsgr'),
331 points=_a('LatLon_', 'LatLon2psxy', 'Numpy2LatLon', 'Shape2Tuple', 'Tuple2LatLon',
332 'areaOf', 'boundsOf', 'centroidOf', 'fractional',
333 'isclockwise', 'isconvex', 'isconvex_', 'isenclosedBy', 'ispolar',
334 'luneOf', 'nearestOn5', 'perimeterOf', 'quadOf'),
335 props=_a('Property', 'Property_RO', 'property_doc_',
336 'property_RO', 'property_ROnce', 'property_ROver',
337 'deprecated_class', 'deprecated_function', 'deprecated_method',
338 'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'),
339 resections=_a('Collins5Tuple', 'ResectionError', 'Survey3Tuple', 'Tienstra7Tuple',
340 'TriAngle5Tuple', 'TriSide2Tuple', 'TriSide4Tuple',
341 'cassini', 'collins5', 'pierlot', 'pierlotx', 'tienstra7',
342 'snellius3', 'wildberger3',
343 'triAngle', 'triAngle5', 'triArea', 'triSide', 'triSide2', 'triSide4'),
344 rhumb=_a(), # package only
345 rhumb_aux_=_a('RhumbAux', 'RhumbLineAux'),
346 rhumb_ekx=_a('Rhumb', 'RhumbLine'),
347 rhumb_solve=_a('RhumbSolve', 'RhumbLineSolve', 'RhumbSolve7Tuple', 'Rhumb8Tuple'), # in .karney
348 sphericalBase=_a(), # module only
349 sphericalNvector=_a(), # module only
350 sphericalTrigonometry=_a(), # module only
351 simplify=_a('simplify1', 'simplifyRDP', 'simplifyRW', 'simplifyVW'),
352 solveBase=_a(), # module only
353 streprs=_a('anstr', 'attrs', 'enstr2', 'fstr', 'fstrzs', 'hstr', 'instr',
354 'lrstrip', 'pairs', 'reprs', 'strs', 'unstr'),
355 trf=_a('RefFrame', 'RefFrames', 'TransformXform', 'TRFXform', 'TRFXform7Tuple',
356 'date2epoch', 'epoch2date', 'trfTransform0', 'trfTransforms', 'trfXform'),
357 triaxials=_a(), # package only
358 triaxials_bases=_a('LLK', 'TriaxialError'),
359 triaxials_conformal3=_a('BetOmgGam5Tuple',
360 'Conformal3', 'Conformal3B', 'Conformal3Sphere', 'Conformal5Tuple'),
361 triaxials_triaxial3=_a('BetOmgAlp5Tuple', 'Cartesian5Tuple', 'LatLonAzi5Tuple', 'PhiLamZet5Tuple',
362 'Triaxial3', 'Triaxial3B', 'Triaxial3s'),
363 triaxials_triaxial5=_a('BetaOmega2Tuple', 'BetaOmega3Tuple',
364 'Conformal', 'ConformalSphere', 'Conformal2Tuple',
365 'Triaxial', 'Triaxial_', 'Triaxials', 'hartzell4', 'height4'),
366 units=_a('Azimuth', 'Band', 'Bearing', 'Bearing_', 'Bool',
367 'Degrees', 'Degrees_', 'Degrees2', 'Distance', 'Distance_', 'Easting', 'Epoch',
368 'Feet', 'FIx', 'Float_', 'Height', 'Height_', 'HeightX', 'Int_',
369 'Lam', 'Lamd', 'Lat', 'Lat_', 'Lon', 'Lon_',
370 'Meter', 'Meter_', 'Meter2', 'Meter3', 'Northing', 'Number_',
371 'Phi', 'Phid', 'Precision_', 'Radians', 'Radians_', 'Radians2',
372 'Radius_', 'Scalar', 'Scalar_', 'Zone'),
373 unitsBase=_a('Float', 'Int', 'Radius', 'Str'),
374 ups=_a('Ups', 'UPSError', 'parseUPS5', 'toUps8', 'upsZoneBand5'),
375 utily=_a('acos1', 'acre2ha', 'acre2m2', 'agdf', 'asin1',
376 'atan1', 'atan1d', 'atan2', 'atan2b', 'atan2d', 'atan2p',
377 'chain2m', 'circle4', 'cot', 'cot_', 'cotd', 'cotd_',
378 'degrees', 'degrees90', 'degrees180', 'degrees360', 'degrees2grades', 'degrees2m',
379 'fathom2m', 'ft2m', 'furlong2m', # 'degrees2grades as degrees2gons',
380 'gdf', 'grades', 'grades400', 'grades2degrees', 'grades2radians',
381# 'grades as gons', 'grades400 as gons400', 'grades2degrees as gons2degrees', 'grades2radians as gons2radians',
382 'ha2acre', 'ha2m2', 'hav', 'km2m',
383 'm2acre', 'm2chain', 'm2degrees', 'm2fathom', 'm2ft', 'm2furlong',
384 'm2ha', 'm2km', 'm2NM', 'm2radians', 'm2SM', 'm2toise', 'm2yard',
385 'NM2m', 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m',
386 'sincos2', 'SinCos2', 'sincos2_', 'sincos2d', 'sincos2d_', 'sincostan3', 'sincostan3d', 'SM2m',
387 'tan', 'tan_', 'tand', 'tand_', 'tan_2', 'tanPI_2_2', 'toise2m', 'truncate',
388 'unroll180', 'unrollPI',
389 'wrap90', 'wrap180', 'wrap360', 'wrapPI_2', 'wrapPI', 'wrapPI2', 'wrap_normal',
390 'yard2m'),
391 utm=_a('Utm', 'UTMError', 'parseUTM5', 'toUtm8', 'utmZoneBand5'),
392 utmups=_a('UtmUps', 'UTMUPSError', 'parseUTMUPS5', 'toUtmUps8',
393 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'),
394 utmupsBase=_a(), # module only
395 vector2d=_a('Circin6Tuple', 'Circum3Tuple', 'Circum4Tuple', 'Meeus2Tuple', 'Radii11Tuple', 'Soddy4Tuple', 'Triaxum5Tuple',
396 'circin6', 'circum3', 'circum4', 'circum4_', 'meeus2', 'radii11', 'soddy4', 'triaxum5', 'trilaterate2d2'),
397 vector3d=_a('Vector3d', 'intersection3d3', 'iscolinearWith', 'nearestOn', 'nearestOn6', 'parse3d',
398 'trilaterate3d2'),
399 vector3dBase=_a(), # module only
400 webmercator=_a('Wm', 'WebMercatorError', 'parseWM', 'toWm', 'EasNorRadius3Tuple'),
401 wgrs=_a('Georef', 'WGRSError'),)
403_ALL_DEPRECATED = _NamedEnum_RO(_name='_ALL_DEPRECATED',
404 deprecated=_a('bases', 'datum', 'nvector', # DEPRECATED modules and ...
405 'rhumbaux', 'rhumbBase', 'rhumbsolve', 'rhumbx'), # ... names
406 deprecated_bases=_a('LatLonHeightBase', 'points2'),
407 deprecated_classes=_a('ChLV', 'ChLVa', 'ChLVe', 'ChLVEN2Tuple', 'ChLV9Tuple', 'ChLVYX2Tuple', 'ChLVyx2Tuple',
408 'ClipCS3Tuple', 'EasNorExact4Tuple', 'EcefCartesian', 'Fn_rt',
409 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert',
410 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert',
411 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'HeightIDWcosineAndoyerLambert',
412 'HeightIDWcosineForsytheAndoyerLambert', 'Helmert7Tuple',
413 'JacobiConformal', 'JacobiConformalSpherical', 'Jacobi2Tuple',
414 'Lam_', 'LatLonExact4Tuple', 'NearestOn4Tuple', 'Ned3Tuple',
415 'Phi_', 'RefFrameError', 'Rhumb7Tuple', 'RhumbOrder2Tuple',
416 'Transform7Tuple', 'TriAngle4Tuple', 'UtmUps4Tuple', 'XDist'),
417 deprecated_consterns=_a('Elliperim', 'EPS1_2', 'MANTIS', 'OK'),
418 deprecated_datum=_a('Curvature2Tuple', 'Datum', 'Ellipsoid', 'Transform', # assert
419 'Datums', 'Ellipsoids', 'Transforms',
420 'R_FM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_SM', 'R_VM'),
421 deprecated_functions=_a('anStr', 'areaof', 'atand', 'bounds', # most of the DEPRECATED functions, except ellipsoidal ...
422 'clipCS3', 'clipDMS', 'clipStr', 'collins', 'copysign', # ... and spherical flavors
423 'cosineAndoyerLambert', 'cosineAndoyerLambert_',
424 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_',
425 'decodeEPSG2',
426 'elliperim', 'elliperim_', 'encodeEPSG', 'enStr2', 'equirectangular_', 'equirectangular3',
427 'excessAbc', 'excessGirard', 'excessLHuilier',
428 'false2f', 'falsed2f', 'float0', 'fStr', 'fStrzs', 'Fsum2product',
429 'hypot3', 'inStr', 'isenclosedby', 'istuplist',
430 'joined', 'joined_', 'nearestOn3', 'nearestOn4',
431 'parseUTM', 'perimeterof', 'polygon',
432 'scalar', 'simplify2', 'simplifyRDPm', 'simplifyVWm',
433 'tienstra', 'toUtm', 'triAngle4',
434 'unsign0', 'unStr', 'utmZoneBand2'),
435 deprecated_nvector=_a('LatLonNvectorBase', 'Nvector', 'sumOf', 'NorthPole', 'SouthPole'),)
438class _ALL_MODS(_internals._MODS_Base):
439 '''(INTERNAL) Memoized import of any L{pygeodesy} module.
440 '''
441 def __getattr__(self, name):
442 '''Get a C{pygeodesy} module or attribute by B{C{name}}.
444 @arg name: Un/qualified module or qualified attribute name (C{str}).
446 @raise ImportError: Importing module B{C{name}} failed.
448 @raise AttributeError: No attribute named B{C{name}}.
449 '''
450 try:
451 v = _lazy_dict[name] # package.__dict__
452 except KeyError:
453 v = _lazy_module(name) # package.__getattr__
454 if _tailof(typename(v)) != name:
455 try:
456 v = getattr(v, _tailof(name))
457 except AttributeError:
458 pass # XXX LazyAttributeError?
459 return v
461 def getattr(self, name, *attr_dflt): # , parent=_pygeodesy_
462 '''Get an attribute of/or a C{pygeodesy} module.
464 @arg name: Un/qualified module name (C{str}).
465 @arg attr_dflt: Optional attribute name (C{str}) and
466 optional default value (any C{type}).
468 @return: The C{pygeodesy} module's attribute value.
470 @raise ImportError: Importing module B{C{name}} failed.
472 @raise AttributeError: No attribute named B{C{attr}}.
473 '''
474 v = self.getmodule(name)
475 if attr_dflt:
476 v = getattr(v, *attr_dflt)
477 return v
479 def getmodule(self, name, parent=_pygeodesy_):
480 '''Get a C{pygeodesy} module or the C{__main__}.
482 @arg name: Un/qualified module name (C{str}).
484 @return: The C{pygeodesy} module.
486 @raise ImportError: Importing module B{C{name}} failed.
487 '''
488 if _headof(name) != parent and name != _DMAIN_:
489 name = _DOT_(parent, name)
490 try:
491 return _sys.modules[name]
492 except KeyError:
493 return _getmodule(name, parent)
495 def imported(self, name):
496 '''Return module or package C{name} if already imported.
497 '''
498 return _sys.modules.get(name, None)
500 def into(self, **mod_DNAME):
501 '''Deferred import of module C{mod} into module C{_DNAME_}
502 and overwrite C{_DNAME_._mod} to module C{mod}, I{once}
503 at the first access of an attribute of module C{mod}.
504 '''
505 # assert len(mod_DNAME) == 1
506 # mod, dun = mod_DNAME.popitem()
508 class _Into(object):
510 def __getattr__(unused, name): # PYLINT E0213
511 m = _getmodinto(mod_DNAME, _Into)
512 return getattr(m, name)
514 return _Into()
516# @_Property_RO
517# def _isBoolean(self):
518# '''(INTERNAL) Get function C(.booleans.isBoolean}, I{once}.
519# '''
520# return self.booleans.isBoolean
522 def items(self): # no module named 'items'
523 '''Yield the modules imported so far.
524 '''
525 for n, m in _sys.modules.items():
526 if _headof(n) == _pygeodesy_:
527 yield n, m
529 @_Property_RO
530 def _triaxials_triaxial5(self):
531 '''(INTERNAL) Get module C{triaxial.triaxials}.
532 '''
533 return self.triaxials.triaxial5
535_internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
538def _ALL_OTHER(*objs):
539 '''(INTERNAL) Get class and function B{C{objs}} for __all__.
540 '''
541 def _interned(o): # intern'd base name
542 n = _tailof(typename(o))
543 i = NN(_UNDER_, n, _UNDER_) # intern'd
544 return getattr(_interns, i, n)
546 return tuple(map(_interned, objs)) # map2
549def _ALL_STAR(pack, *mods, **abspath): # PYCHOK no cover
550 '''(INTERNAL) Mimick "for m in mods: from m import *" inside pack.__init__
551 returning a tuple of __all__ names collected, sorted and extended with
552 "pack_abspath" for use in packs pyaxqg, pybelbg, pychlv and pyrdnap.
553 '''
554 p = import_module(pack) # sys.modules[pack]
555 d = p.__dict__
556 t = []
557 for m in mods:
558 for n in m.__all__:
559 x = getattr(m, n)
560 if d.get(n, x) is x:
561 d[n] = x
562 t.append(n)
563 else:
564 t = '%r vs %r' % (d[n], x)
565 raise LazyAttributeError(duplicate=n, txt=t)
566 if abspath.get('abspath', True):
567 t.append(_UNDER_(pack, 'abspath'))
568 return tuple(sorted(t))
571if _FOR_DOCS: # PYCHOK no cover
572 _ALL_DOCS = _ALL_OTHER
573 # (INTERNAL) Only export B{C{objs.__name__}} when making the
574 # docs to force C{epydoc} to include certain classes, methods,
575 # functions and other names in the documentation. Using the
576 # C{epydoc --private ...} command line option tends to include
577 # too much internal documentation.
578else:
579 def _ALL_DOCS(*unused):
580 return ()
583def _all_deprecates():
584 '''(INTERNAL) Build C{dict} of all deprecated imports and attributes.
585 '''
586 D = _ALL_DEPRECATES
587 if not D:
588 _ALL_DEPRECATED.fill_D(D, _all_deprecates) # see _all_imports()
589 return D
591_ALL_DEPRECATES = _Dict() # PYCHOK _ALL_DEPRECATED.imports()
594def _all_enums():
595 '''(INTERNAL) Yield all C{(mod_, tuple)} pairs for C{__init__._all}.
596 '''
597 # assert _init__all__
598 for mod_t in _ALL_LAZY.enums():
599 yield mod_t
600 if _FOR_DOCS:
601 for mod_t in _ALL_DEPRECATED.enums():
602 yield mod_t
605def _all_imports():
606 '''(INTERNAL) Build C{dict} of all lazy imports.
607 '''
608 # imports naming conventions stored below - [<key>] = <from>:
609 # import <module> - [<module>] = <module>
610 # from <module> import <attr> - [<attr>] = <module>
611 # from pygeodesy import <attr> - [<attr>] = <attr>
612 # from <module> import <attr> as <name> - [<name>] = <module>.<attr>.
613 D = _ALL_IMPORTS
614 if not D:
615 _ALL_LAZY.fill_D(D, _all_imports) # see _all_deprecates()
616 return D
618_ALL_IMPORTS = _Dict() # PYCHOK _ALL_LAZY.imports()
621def _all_missing2(_all_):
622 '''(INTERNAL) Get diffs between pygeodesy.__all__ and lazily._all_imports.
623 '''
624 def _diff(one, two):
625 return tuple(sorted(a for a in one if a not in two))
627 _alzy = _Dict((a, a) for a in _ALL_INIT)
628 _alzy.update(_all_imports()) # without _all_backups!
629 return ((_DOT_(_lazily_, _all_imports.__name__), _diff(_all_, _alzy)),
630 (_DOT_(_pygeodesy_, _DALL_), _diff(_alzy.keys(), _all_)))
633def _getattras(attr_as): # test/testDeprecated
634 '''(INTERNAL) Get the C{"as name"} or C{"name"} of a lazy entry.
635 '''
636 a_, _, as_ = attr_as.partition(_asSPACED_)
637 return as_ or a_.rstrip(_DOT_)
640def _getmodattr(m, name, mod=_pygeodesy_):
641 '''(INTERNAL) Get attr C{m.name}.
642 '''
643 try:
644 return getattr(m, name)
645 except AttributeError:
646 name = _DOT_(typename(m, mod), name)
647 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76>
648 raise LazyAttributeError(_no_(_attribute_), txt=name)
651def _getmodinto(mod_DNAME, *Intos):
652 '''(INTERNAL) Core of C{_ALL_MODS.into}.
653 '''
654 _MODS = _ALL_MODS
655 mod, dun = _MODS.errors._xkwds_item2(mod_DNAME)
656 _mod = _UNDER_(NN, mod)
657 d = _MODS.getmodule(dun) # '__main__' OK
658 i = _getmodattr(d, _mod, dun)
659 assert isinstance(i, Intos)
660 m = _MODS.getmodule(mod)
661 setattr(d, _mod, m) # overwrite C{d._mod}
662 if isLazy and isLazy > 1:
663 t = _SPACE_(_HASH_, _imported_, m.__name__) # typename(m)
664 _hash_imported(t, _MODS.into.__name__)
665 assert getattr(d, _mod, None) is m
666 return m
669def _getmodule(name, *parent):
670 '''(INTERNAL) Wrapper for C{import_module}.
671 '''
672 try:
673 return import_module(name, parent)
674 except ImportError:
675 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76>
676 raise LazyImportError(_no_(_module_), txt=name)
679def _hash_imported(t, by_into, up=3):
680 '''(INTERNAL) Helper for C{_lazy_import2} and C{_ALL_MODS.into}.
681 '''
682 if isLazy and isLazy > 2:
683 try: # see C{internals._caller3}
684 _, f, s = _caller3(up)
685 t = _SPACE_(t, by_into, f, _line_, s)
686 except ValueError:
687 pass
688 printf(t) # XXX print
691# def _lazy_attributes(DUNDER_name):
692# '''(INTERNAL) Return a function to C{B{__name__}.__getattr__(attr)}
693# on lazily imported modules and sub-modules.
694# '''
695# if _unlazy:
696# raise AssertionError(_COMMASPACE_(DUNDER_name, _not_(_DEPRECATED_)))
697#
698# def _getattr(attr, *dflt):
699# try: # a module name
700# return _ALL_MODS.getmodule(attr)
701# except (AttributeError, ImportError):
702# return _ALL_MODS.getattr(DUNDER_name, attr, *dflt)
703#
704# return _getattr
707_lazy_dict = {} # PYCHOK overwritten by _lazy_import2
710def _lazy_import2(pack): # MCCABE 14
711 '''Check for and set up C{lazy import}.
713 @arg pack: The name of the package (C{str}) performing the imports,
714 to help resolving relative imports, usually C{__package__}.
716 @return: 2-Tuple C{(package, getattr)} of the importing package for
717 easy reference within itself and the callable to be set to
718 C{package.__getattr__}.
720 @raise LazyAttributeError: The package, module or attribute name is
721 invalid or does not exist.
723 @raise LazyImportError: Lazy import not supported or not enabled or
724 an import failed.
726 @note: This is I{Brett Cannon}'s function U{modutil.lazy_import
727 <https://GitHub.com/brettcannon/modutil/blob/master/modutil.py>}
728 modified to handle the C{__all__} and C{__dir__} attributes and
729 call C{importlib.import_module(<module>.<name>, ...)} without
730 causing a C{ModuleNotFoundError}.
732 @see: The original U{modutil<https://PyPI.org/project/modutil>},
733 U{PEP 562<https://www.Python.org/dev/peps/pep-0562>} and the
734 U{new way<https://Snarky.CA/lazy-importing-in-python-3-7/>}.
735 '''
736 if pack != _pygeodesy_ or _unlazy: # Python 3.7+ # PYCHOK no cover
737 t = _DOT_(pack, typename(_lazy_import2))
738 raise LazyImportError(_no_(t), txt=_versions())
740 package, parent = _lazy_init2(pack) # _pygeodesy_
742 _DPACKAGE_ = '__package__'
743 _lazily_imported_ = _SPACE_(_HASH_, _lazily_, _imported_, parent)
745 sub_packages = set((parent, NN) + tuple(
746 _DOT_(parent, s) for s in _SUB_PACKAGES))
747 imports = _all_imports()
748 deprecates = _all_deprecates()
750 def __getattr__(name): # __getattr__ only for Python 3.7+
751 # only called once for each undefined pygeodesy attribute
752 mod = imports.get(name, NN) or deprecates.get(name, NN)
753 if mod:
754 # importlib.import_module() implicitly sets sub-modules
755 # on this module as appropriate for direct imports (see
756 # note in the _lazy_import2.__doc__ above).
757 if mod.endswith(_DOT_): # import mod[.attr] as name
758 mod, _, attr = mod[:-1].rpartition(_DOT_)
759 else: # from mod import name
760 attr = name
761 v = _getmodule(_DOT_(pack, mod), parent)
762 t = getattr(v, _DPACKAGE_, None)
763 if t not in sub_packages: # invalid module package
764 raise LazyImportError(_DOT_(mod, _DPACKAGE_), t)
765 if attr: # get mod.attr
766 v = _getmodattr(v, attr, mod)
768 elif name in (_DALL_,): # XXX _Ddir_, _Dmembers_?
769 v = _ALL_INIT + tuple(imports.keys())
770 else: # PYCHOK no cover
771 t = _no_(_module_, _or_, _attribute_)
772 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76>
773 raise LazyAttributeError(t, txt=_DOT_(parent, name))
775 setattr(package, name, v) # package.__dict__[name] = val
776 if isLazy > 1:
777 t = _DOT_(_lazily_imported_, name)
778 if mod and _tailof(mod) != name:
779 t = _SPACE_(t, _from_, _DOT_(NN, mod))
780 _hash_imported(t, _by_)
782 return v # __getattr__
784 global _lazy_dict, _lazy_module # PYLINT W0603
785 _lazy_dict = package.__dict__
786 _lazy_module = __getattr__
788 return package, __getattr__ # _lazy_import2
791# def _lazy_import_all(Dname):
792# '''(INTERNAL) Return a function mimicking C{from B{__name__} import *},
793# of all items, see .deprecated.__init__
794# '''
795# if _unlazy:
796# raise AssertionError(_COMMASPACE_(Dname, _not_(_DEPRECATED_)))
797#
798# _getattr = _lazy_attributes(Dname) # __name__.__getattr__
799# _import_start = _lazy_import_star(Dname, ALL_=_ALL_IMPORTS)
800#
801# def _import_all(attr, *dflt):
802# return _import_star(Dname) if attr == _DALL_ else \
803# _getattr(attr, *dflt)
804#
805# return _import_all
808def _lazy_import_as(DUNDER_name):
809 '''(INTERNAL) Return a function to C{import B{__name__}.mod as mod}
810 I{of modules only}, see .deprecated, .rhumb or get an attribute
811 lazily exported by C{__name__}.
812 '''
813 if _unlazy:
814 return None
816 def _import_as(mod):
817 try:
818 return _ALL_MODS.getmodule(_DOT_(DUNDER_name, mod))
819 except ImportError:
820 return _lazy_module(mod)
822 return _import_as
825# def _lazy_import_star(DUNDER_name, ALL_=_ALL_DEPRECATES):
826# '''(INTERNAL) Return a function to mimick C{from B{__name__} import *},
827# of all DEPRECATED items, see .deprecated, .testDeprecated
828# '''
829# if _unlazy:
830# raise AssertionError(_COMMASPACE_(DUNDER_name, _not_(_DEPRECATED_)))
831#
832# def _import_star(_into_):
833# '''Do C{from B{__name__} import *} inside module C{B{__into__}}.
834# '''
835# d = dict()
836# nm = _tailof(DUNDER_name)
837# _g = _ALL_MODS.getattr # pygeodesy.__getattr__
838# for a, m in ALL_.items():
839# if _headof(m) == nm:
840# try:
841# d[a] = _g(m, a)
842# except (AttributeError, ImportError):
843# pass
844# _sys.modules[_into_].__dict__.update(d)
845# return d.keys() # imported names
846#
847# return _import_star
850def _lazy_init2(pack):
851 '''(INTERNAL) Initialize lazy import and set globals C{isLazy} and C{_unLazy0}.
853 @arg pack: The name of the package (C{str}) performing the imports,
854 to resolve relative imports, usually C{__package__}.
856 @return: 2-Tuple C{(package, parent)} with the importing C{package}
857 for easy reference within itself and its name aka the
858 C(package)'s C{parent}, same as B{C{pack}}.
860 @raise LazyImportError: Lazy import not supported or not enabled,
861 an import failed or the package name is
862 invalid or does not exist.
864 @note: Global C{isLazy} is set accordingly.
865 '''
866 global isLazy, _unLazy0 # PYLINT W0603
868 E = _internals._PYGEODESY_ENV('LAZY_IMPORT')
869 z = _internals._getenv(E, _interns._1_) # 1 default on 3.7+
870 z = z.strip() # like PYTHONVERBOSE et.al.
871 isLazy = int(z) if z.isdigit() else (1 if z else 0)
873 _unLazy0 = _unlazy or not isLazy # pre-3.7 or w/o lazy import
875 if isLazy < 1: # invalid, not enabled
876 raise LazyImportError(E, repr(z), txt_not_='enabled')
877 if _sys.flags.verbose: # PYCHOK no cover
878 isLazy += 1
880 try: # to initialize in Python 3+
881 package = import_module(pack)
882 parent = package.__spec__.parent # __spec__ only in Python 3.7+
883 if parent != pack: # assert
884 t = _COMMASPACE_(parent, _not_(pack)) # PYCHOK no cover
885 raise AttributeError(_EQUALSPACED_('parent', t))
887 except (AttributeError, ImportError) as x:
888 isLazy = False # failed
889 z = typename(_lazy_init2)
890 raise LazyImportError(z, pack, cause=x)
892 return package, parent
895def _lazy_module(name): # overwritten by _lazy_import2
896 '''(INTERNAL) Get or import a C{pygeodesy} module.
897 '''
898 try: # most likely ... module has been imported
899 m = _ALL_MODS.getmodule(name)
900 except (AttributeError, ImportError) as x:
901 raise LazyImportError(name, cause=x)
902 _lazy_dict[name] = m # cache
903 return m
906# def _lazy_subs(__name__, force=_FOR_DOCS, over=False):
907# '''(INTERNAL) Return the names of a __name__ package's sub-packages
908# and update the package's C{__dict__} accordingly.
909# '''
910# sm = dict()
911# if force and __name__ != _DMAIN_:
912# nm = _tailof(__name__)
913# _a = _ALL_MODS.getattr
914# _m = _ALL_MODS.getmodule
915# d = _a(__name__, _DDICT_, {})
916# for n in _a(__name__, _DALL_, ()):
917# try: # n is a class name, get its mod name
918# m = _a(__name__, n).__module__
919# n, s = m.split(_DOT_)[-2:]
920# if n == nm and s not in sm:
921# m = _m(m) # == import m as s
922# sm[s] = m if over else d.get(s, m)
923# except (AttributeError, ImportError, ValueError) as x:
924# pass
925# d.update(sm)
926#
927# return _ALL_OTHER(*sm.values())
930__all__ = _ALL_LAZY.lazily
931__version__ = '26.10.18'
933if __name__ == _DMAIN_:
935 def _main():
936 from timeit import timeit
938 def t1():
939 from pygeodesy.trf import RefFrame
940 return RefFrame
942 def t2():
943 return _ALL_MODS.trf.RefFrame
945 assert t1() is t2() # prime each
947 t1 = timeit(t1, number=1000000)
948 t2 = timeit(t2, number=1000000)
949 A = typename(_ALL_MODS)
950 v = _versions()
951 printf('%.6f import vs %.6f %s: %.2fX, %s', t1, t2, A, (t1 / t2), v)
953 _main()
955# % python3.15 -W ignore -m pygeodesy.lazily
956# 0.078365 import vs 0.052132 _ALL_MODS: 1.50X, pygeodesy 26.8.18 Python 3.15.0rc1 64bit arm64 macOS 26.5.2
958# n% python3.14 -W ignore -m pygeodesy.lazily
959# 0.064455 import vs 0.046083 _ALL_MODS: 1.40X, pygeodesy 26.8.18 Python 3.14.7 64bit arm64 macOS 26.5.2
961# % python2 -W ignore -m pygeodesy.lazily
962# 0.685969 import vs 0.321554 _ALL_MODS: 2.13X, pygeodesy 26.8.18 Python 2.7.18 64bit arm64_x86_64 macOS 26.5.2
964# % python3.14 -W ignore -m pygeodesy.lazily
965# 0.061219 import vs 0.047896 _ALL_MODS: 1.28X, pygeodesy 25.12.6 Python 3.14.0 64bit arm64 macOS 26.1
967# % python3.13 -W ignore -m pygeodesy.lazily
968# 0.054235 import vs 0.052469 _ALL_MODS: 1.03X, pygeodesy 25.4.24 Python 3.13.3 64bit arm64 macOS 15.4
970# % python2 -m pygeodesy.lazily
971# 0.653715 import vs 0.321318 _ALL_MODS: 2.03X, pygeodesy 25.4.24 Python 2.7.18 64bit arm64_x86_64 macOS 10.16
973# % python3.13 -W ignore -m pygeodesy.lazily
974# 0.106602 import vs 0.078136 _ALL_MODS: 1.36X, pygeodesy 24.10.24 Python 3.13.0 64bit arm64 macOS 14.6.1
976# % python3.12 -W ignore -m pygeodesy.lazily
977# 0.138844 import vs 0.080458 _ALL_MODS: 1.73X, pygeodesy 24.10.24 Python 3.12.7 64bit arm64 macOS 14.6.1
979# % python3.11 -W ignore -m pygeodesy.lazily
980# 0.387520 import vs 0.254229 _ALL_MODS: 1.52X, pygeodesy 24.10.24 Python 3.11.5 64bit arm64 macOS 14.6.1
982# % python3.10 -W ignore -m pygeodesy.lazily
983# 0.371269 import vs 0.272897 _ALL_MODS: 1.36X, pygeodesy 24.10.24 Python 3.10.8 64bit arm64 macOS 14.6.1
985# % python3.8 -W ignore -m pygeodesy.lazily
986# 0.555572 import vs 0.370304 _ALL_MODS: 1.50X, pygeodesy 24.10.24 Python 3.8.10 64bit arm64_x86_64 macOS 10.16
988# % python2 -m pygeodesy.lazily
989# 1.160292 import vs 0.490279 _ALL_MODS: 2.37X, pygeodesy 24.10.24 Python 2.7.18 64bit arm64_x86_64 macOS 10.16
991# **) MIT License
992#
993# Copyright (C) 2018-2026 -- mrJean1 at Gmail -- All Rights Reserved.
994#
995# Permission is hereby granted, free of charge, to any person obtaining a
996# copy of this software and associated documentation files (the "Software"),
997# to deal in the Software without restriction, including without limitation
998# the rights to use, copy, modify, merge, publish, distribute, sublicense,
999# and/or sell copies of the Software, and to permit persons to whom the
1000# Software is furnished to do so, subject to the following conditions:
1001#
1002# The above copyright notice and this permission notice shall be included
1003# in all copies or substantial portions of the Software.
1004#
1005# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1006# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1007# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1008# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1009# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1010# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1011# OTHER DEALINGS IN THE SOFTWARE.