Coverage for pygeodesy/lazily.py: 96%

216 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-02-15 15:48 -0500

1 

2# -*- coding: utf-8 -*- 

3 

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>}. 

7 

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}. 

12 

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. 

20 

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. 

24 

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''' 

29 

30from pygeodesy import internals as _internals, interns as _interns, \ 

31 _isfrozen # DON'T _lazy_import2 

32# from pygeodesy.errors import _error_init, _ImmutableError, _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 

43except ImportError as x: # Python 2.6- 

44 raise ImportError(_COLONSPACE_(x, _versions())) 

45# import sys as _sys # from .interns 

46 

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 

56 

57# @module_property[_RO?] <https://GitHub.com/jtushman/proxy_tools/> <https://discuss.Python.org/t/47379> 

58isLazy = None # see @var isLazy in .__init__ 

59 

60 

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) 

66 

67 

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) 

73 

74 

75class _Dict(dict): 

76 '''(INTERNAL) Imports C{dict}. 

77 ''' 

78 _name = NN 

79 

80 def __getattr__(self, attr): 

81 try: 

82 return self[attr] 

83 except KeyError: 

84 return dict.__getattr__(self, attr) 

85 

86# def __setattr__(self, attr, value): 

87# if attr in self: 

88# self[attr] = value 

89# else: 

90# dict.__setattr__(self, attr, value) 

91 

92 def add(self, name, mod_, *subs): 

93 '''Add a C{[name] = mod_} item. 

94 

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_ 

107 

108 def _NAME(self, which): 

109 self._name = _intern(typename(which).upper()) 

110 

111 

112class _NamedEnum_RO(dict): 

113 '''(INTERNAL) C{Read_Only} enum-like C{dict} sub-class. 

114 ''' 

115# _name = NN # also first kwd, __init__(_name=...) 

116 

117 def _DOT_(self, attr): # PYCHOK no cover 

118 return _DOT_(self._name, attr) # PYCHOK _name 

119 

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_) 

126 

127 def __setattr__(self, attr, value): # PYCHOK no cover 

128 e = _ALL_MODS.errors 

129 raise e._ImmutableError(self, attr, value, 

130 Error=LazyAttributeError) 

131 

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 

144 

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 

158 

159 

160def _a(*names): 

161 '''(INTERNAL) Intern all C{names}. 

162 ''' 

163 return tuple(map(_intern, names)) if names else _a0 

164 

165 

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 

173 

174 

175_ALL_INIT = _a(_pygeodesy_abspath_, _version_) 

176 

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(), # module 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', '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', 'EcefKarney', 'EcefSudano', 'EcefUPC', 'EcefVeness', 'EcefYou', 

220 'EcefError', 'EcefMatrix', 'Ecef9Tuple'), 

221 ecefLocals=_a(), # module only 

222 elevations=_a('Elevation2Tuple', 'GeoidHeight2Tuple', 

223 'elevation2', 'geoidHeight2'), 

224 ellipses=_a('Ellipse',), 

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', 'Circle4Tuple', '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_', 'sqrt0', 'sqrt3', 'sqrt_a', 'zcrt', 'zqrt'), 

254 formy=_a('Radical2Tuple', 

255 'angle2chord', 'antipode', 'antipode_', 'bearing', 'bearing_', 

256 'chord2angle', 'compassAngle', 'cosineLaw', 'cosineLaw_', 

257 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_', 

258 'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_', 

259 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_', 

260 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_', 

261 'hartzell', 'haversine', 'haversine_', 'heightOf', 'heightOrthometric', 'horizon', 'hubeny', 'hubeny_', 

262 'intersection2', 'intersections2', 'isantipode', 'isantipode_', 'isnormal', 'isnormal_', 

263 'normal', 'normal_', 'opposing', 'opposing_', 'radical2', 

264 'thomas', 'thomas_', 'vincentys', 'vincentys_'), 

265 frechet=_a('Frechet', 'FrechetDegrees', 'FrechetError', 'FrechetRadians', 'FrechetCosineLaw', 

266 'FrechetDistanceTo', 'FrechetEquirectangular', 'FrechetEuclidean', 'FrechetExact', 

267 'FrechetFlatLocal', 'FrechetFlatPolar', 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney', 

268 'FrechetThomas', 'FrechetVincentys', 'Frechet6Tuple', 

269 'frechet_'), 

270 fstats=_a('Fcook', 'Flinear', 'Fwelford'), 

271 fsums=_a('Fsum', 'DivMod2Tuple', 'Fsum2Tuple', 'ResidualError', 

272 'f2product', 'fsum', 'fsum_', 'fsumf_', 'fsum1', 'fsum1_', 'fsum1f_', 'nonfiniterrors'), 

273 gars=_a('Garef', 'GARSError'), 

274 geodesici=_a('Intersectool', 'Intersectool5Tuple', 'Intersect7Tuple', 

275 'Intersector', 'Intersector5Tuple', 'Middle5Tuple', 'XDict'), 

276 geodesicw=_a('Geodesic', 'GeodesicLine', 'Geodesic_WGS84'), 

277 geodesicx=_a('gx', 'gxarea', 'gxbases', 'gxline', # modules 

278 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'), 

279 geodsolve=_a('GeodesicSolve', 'GeodesicLineSolve', 'GeodSolve12Tuple'), 

280 geod3solve=_a('Geodesic3Solve', 'GeodesicLine3Solve', 'Geod3Solve8Tuple', 'Geodesic3Error'), 

281 geohash=_a('Geohash', 'Geohashed', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple', 'Sizes3Tuple'), 

282 geoids=_a('GeoidError', 'GeoidEGM96', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights', 

283 'PGMError', 'GeoidHeight5Tuple'), 

284 hausdorff=_a('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians', 'HausdorffCosineLaw', 

285 'HausdorffDistanceTo', 'HausdorffEquirectangular', 'HausdorffEuclidean', 'HausdorffExact', 

286 'HausdorffFlatLocal', 'HausdorffFlatPolar', 'HausdorffHaversine', 'HausdorffHubeny', 

287 'HausdorffKarney', 'HausdorffThomas', 'HausdorffVincentys', 'Hausdorff6Tuple', 

288 'hausdorff_', 'randomrangenerator'), 

289 heights=_a('HeightCubic', 'HeightError', 'HeightIDWcosineLaw', 'HeightIDWdistanceTo', 

290 'HeightIDWequirectangular', 'HeightIDWeuclidean', 'HeightIDWexact', 'HeightIDWflatLocal', 

291 'HeightIDWflatPolar', 'HeightIDWhaversine', 'HeightIDWhubeny', 'HeightIDWkarney', 'HeightIDWthomas', 

292 'HeightIDWvincentys', 'HeightLinear', 'HeightLSQBiSpline', 'HeightSmoothBiSpline'), 

293 internals=_internals.__all__, 

294 interns=_interns.__all__, 

295 iters=_a('LatLon2PsxyIter', 'PointsIter', 'points2', 

296 'isNumpy2', 'isPoints2', 'isTuple2', 'iterNumpy2', 'iterNumpy2over'), 

297 karney=_a('Area3Tuple', 'Caps', 'Direct9Tuple', 'GDict', 'Inverse10Tuple'), 

298 ktm=_a('KTMError', 'KTransverseMercator'), 

299 latlonBase=_a('latlon2n_xyz', 'philam2n_xyz'), 

300 lazily=_a('LazyAttributeError', 'LazyImportError', 'isLazy'), 

301 lcc=_a('Conic', 'Conics', 'Lcc', 'LCCError', 'toLcc'), 

302 ltp=_a('Attitude', 'AttitudeError', 'ChLV', 'ChLVa', 'ChLVe', 'Frustum', 

303 'LocalCartesian', 'LocalError', 'Ltp', 'tyr3d'), 

304 ltpTuples=_a('Aer', 'Aer4Tuple', 'Attitude4Tuple', 

305 'ChLVEN2Tuple', 'ChLV9Tuple', 'ChLVYX2Tuple', 'ChLVyx2Tuple', 

306 'Enu', 'Enu4Tuple', 'Footprint5Tuple', 'Local9Tuple', 'Los', 

307 'Ned', 'Ned4Tuple', 'Uvw', 'Uvw3Tuple', 'XyzLocal', 'Xyz4Tuple'), 

308 mgrs=_a('Mgrs', 'parseMGRS', 'toMgrs', 'Mgrs4Tuple', 'Mgrs6Tuple'), 

309 named=_a('ADict', 

310 'callername', 'classname', 'classnaming', 'modulename', 

311 'nameof', 'notImplemented', 'notOverloaded'), 

312 namedTuples=_a('Bearing2Tuple', 'Bounds2Tuple', 'Bounds4Tuple', 

313 'Destination2Tuple', 'Destination3Tuple', 

314 'Distance2Tuple', 'Distance3Tuple', 'Distance4Tuple', 

315 'EasNor2Tuple', 'EasNor3Tuple', 'Forward4Tuple', 'Intersection3Tuple', 

316 'LatLon2Tuple', 'LatLon3Tuple', 'LatLon4Tuple', 

317 'LatLonDatum3Tuple', 'LatLonDatum5Tuple', 

318 'LatLonPrec3Tuple', 'LatLonPrec5Tuple', 

319 'NearestOn2Tuple', 'NearestOn3Tuple', 'NearestOn6Tuple', 'NearestOn8Tuple', 

320 'PhiLam2Tuple', 'PhiLam3Tuple', 'PhiLam4Tuple', 'Point3Tuple', 'Points2Tuple', 

321 'Reverse4Tuple', 'Triangle7Tuple', 'Triangle8Tuple', 'Trilaterate5Tuple', 

322 'UtmUps2Tuple', 'UtmUps5Tuple', 'UtmUps8Tuple', 'UtmUpsLatLon5Tuple', 

323 'Vector2Tuple', 'Vector3Tuple', 'Vector4Tuple'), 

324 nvectorBase=_a('NorthPole', 'SouthPole', 'n_xyz2latlon', 'n_xyz2philam'), 

325 osgr=_a('Osgr', 'OSGRError', 'parseOSGR', 'toOsgr'), 

326 points=_a('LatLon_', 'LatLon2psxy', 'Numpy2LatLon', 'Shape2Tuple', 'Tuple2LatLon', 

327 'areaOf', 'boundsOf', 'centroidOf', 'fractional', 

328 'isclockwise', 'isconvex', 'isconvex_', 'isenclosedBy', 'ispolar', 

329 'luneOf', 'nearestOn5', 'perimeterOf', 'quadOf'), 

330 props=_a('Property', 'Property_RO', 'property_doc_', 

331 'property_RO', 'property_ROnce', 'property_ROver', 

332 'deprecated_class', 'deprecated_function', 'deprecated_method', 

333 'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'), 

334 resections=_a('Collins5Tuple', 'ResectionError', 'Survey3Tuple', 'Tienstra7Tuple', 

335 'TriAngle5Tuple', 'TriSide2Tuple', 'TriSide4Tuple', 

336 'cassini', 'collins5', 'pierlot', 'pierlotx', 'tienstra7', 

337 'snellius3', 'wildberger3', 

338 'triAngle', 'triAngle5', 'triArea', 'triSide', 'triSide2', 'triSide4'), 

339 rhumb=_a(), # module only 

340 rhumb_aux_=_a('RhumbAux', 'RhumbLineAux'), 

341 rhumb_ekx=_a('Rhumb', 'RhumbLine'), 

342 rhumb_solve=_a('RhumbSolve', 'RhumbLineSolve', 'RhumbSolve7Tuple', 'Rhumb8Tuple'), # in .karney 

343 sphericalBase=_a(), # module only 

344 sphericalNvector=_a(), # module only 

345 sphericalTrigonometry=_a(), # module only 

346 simplify=_a('simplify1', 'simplifyRDP', 'simplifyRW', 'simplifyVW'), 

347 solveBase=_a(), # module only 

348 streprs=_a('anstr', 'attrs', 'enstr2', 'fstr', 'fstrzs', 'hstr', 'instr', 

349 'lrstrip', 'pairs', 'reprs', 'strs', 'unstr'), 

350 trf=_a('RefFrame', 'RefFrames', 'TransformXform', 'TRFXform', 'TRFXform7Tuple', 

351 'date2epoch', 'epoch2date', 'trfTransform0', 'trfTransforms', 'trfXform'), 

352 triaxials=_a(), # module only 

353 triaxials_bases=_a('LLK', 'TriaxialError'), 

354 triaxials_conformal3=_a('BetOmgGam5Tuple', 

355 'Conformal3', 'Conformal3B', 'Conformal3Sphere', 'Conformal5Tuple'), 

356 triaxials_triaxial3=_a('BetOmgAlp5Tuple', 'Cartesian5Tuple', 'PhiLamZet5Tuple', 

357 'Triaxial3', 'Triaxial3B', 'Triaxial3s'), 

358 triaxials_triaxial5=_a('BetaOmega2Tuple', 'BetaOmega3Tuple', 

359 'Conformal', 'ConformalSphere', 'Conformal2Tuple', 

360 'Triaxial', 'Triaxial_', 'Triaxials', 'hartzell4', 'height4'), 

361 units=_a('Azimuth', 'Band', 'Bearing', 'Bearing_', 'Bool', 

362 'Degrees', 'Degrees_', 'Degrees2', 'Distance', 'Distance_', 'Easting', 'Epoch', 

363 'Feet', 'FIx', 'Float_', 'Height', 'Height_', 'HeightX', 'Int_', 

364 'Lam', 'Lamd', 'Lat', 'Lat_', 'Lon', 'Lon_', 

365 'Meter', 'Meter_', 'Meter2', 'Meter3', 'Northing', 'Number_', 

366 'Phi', 'Phid', 'Precision_', 'Radians', 'Radians_', 'Radians2', 

367 'Radius_', 'Scalar', 'Scalar_', 'Zone'), 

368 unitsBase=_a('Float', 'Int', 'Radius', 'Str'), 

369 ups=_a('Ups', 'UPSError', 'parseUPS5', 'toUps8', 'upsZoneBand5'), 

370 utily=_a('acos1', 'acre2ha', 'acre2m2', 'agdf', 'asin1', 'atan1', 'atan1d', 'atan2', 'atan2b', 'atan2d', 

371 'chain2m', 'circle4', 'cot', 'cot_', 'cotd', 'cotd_', 

372 'degrees', 'degrees90', 'degrees180', 'degrees360', 'degrees2grades', 'degrees2m', 

373 'fathom2m', 'ft2m', 'furlong2m', # 'degrees2grades as degrees2gons', 

374 'gdf', 'grades', 'grades400', 'grades2degrees', 'grades2radians', 

375# 'grades as gons', 'grades400 as gons400', 'grades2degrees as gons2degrees', 'grades2radians as gons2radians', 

376 'ha2acre', 'ha2m2', 'hav', 'km2m', 

377 'm2acre', 'm2chain', 'm2degrees', 'm2fathom', 'm2ft', 'm2furlong', 

378 'm2ha', 'm2km', 'm2NM', 'm2radians', 'm2SM', 'm2toise', 'm2yard', 

379 'NM2m', 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m', 

380 'sincos2', 'SinCos2', 'sincos2_', 'sincos2d', 'sincos2d_', 'sincostan3', 'sincostan3d', 'SM2m', 

381 'tan', 'tan_', 'tand', 'tand_', 'tan_2', 'tanPI_2_2', 'toise2m', 'truncate', 

382 'unroll180', 'unrollPI', 

383 'wrap90', 'wrap180', 'wrap360', 'wrapPI_2', 'wrapPI', 'wrapPI2', 'wrap_normal', 

384 'yard2m'), 

385 utm=_a('Utm', 'UTMError', 'parseUTM5', 'toUtm8', 'utmZoneBand5'), 

386 utmups=_a('UtmUps', 'UTMUPSError', 'parseUTMUPS5', 'toUtmUps8', 

387 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'), 

388 utmupsBase=_a(), # module only 

389 vector2d=_a('Circin6Tuple', 'Circum3Tuple', 'Circum4Tuple', 'Meeus2Tuple', 'Radii11Tuple', 'Soddy4Tuple', 'Triaxum5Tuple', 

390 'circin6', 'circum3', 'circum4', 'circum4_', 'meeus2', 'radii11', 'soddy4', 'triaxum5', 'trilaterate2d2'), 

391 vector3d=_a('Vector3d', 'intersection3d3', 'iscolinearWith', 'nearestOn', 'nearestOn6', 'parse3d', 

392 'trilaterate3d2'), 

393 vector3dBase=_a(), # module only 

394 webmercator=_a('Wm', 'WebMercatorError', 'parseWM', 'toWm', 'EasNorRadius3Tuple'), 

395 wgrs=_a('Georef', 'WGRSError'),) 

396 

397_ALL_DEPRECATED = _NamedEnum_RO(_name='_ALL_DEPRECATED', 

398 deprecated=_a('bases', 'datum', 'nvector', # DEPRECATED modules and ... 

399 'rhumbaux', 'rhumbBase', 'rhumbsolve', 'rhumbx'), # ... names 

400 deprecated_bases=_a('LatLonHeightBase', 'points2'), 

401 deprecated_classes=_a('ClipCS3Tuple', 'EasNorExact4Tuple', 'EcefCartesian', 'Fn_rt', 

402 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert', 

403 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert', 

404 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'HeightIDWcosineAndoyerLambert', 

405 'HeightIDWcosineForsytheAndoyerLambert', 'Helmert7Tuple', 

406 'JacobiConformal', 'JacobiConformalSpherical', 'Jacobi2Tuple', 

407 'Lam_', 'LatLonExact4Tuple', 'NearestOn4Tuple', 'Ned3Tuple', 

408 'Phi_', 'RefFrameError', 'Rhumb7Tuple', 'RhumbOrder2Tuple', 

409 'Transform7Tuple', 'TriAngle4Tuple', 'UtmUps4Tuple', 'XDist'), 

410 deprecated_consterns=_a('Elliperim', 'EPS1_2', 'MANTIS', 'OK'), 

411 deprecated_datum=_a('Curvature2Tuple', 'Datum', 'Ellipsoid', 'Transform', # assert 

412 'Datums', 'Ellipsoids', 'Transforms', 

413 'R_FM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_SM', 'R_VM'), 

414 deprecated_functions=_a('anStr', 'areaof', 'atand', 'bounds', # most of the DEPRECATED functions, except ellipsoidal ... 

415 'clipCS3', 'clipDMS', 'clipStr', 'collins', 'copysign', # ... and spherical flavors 

416 'cosineAndoyerLambert', 'cosineAndoyerLambert_', 

417 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_', 

418 'decodeEPSG2', 

419 'elliperim', 'elliperim_', 'encodeEPSG', 'enStr2', 'equirectangular_', 'equirectangular3', 

420 'excessAbc', 'excessGirard', 'excessLHuilier', 

421 'false2f', 'falsed2f', 'float0', 'fStr', 'fStrzs', 'Fsum2product', 

422 'hypot3', 'inStr', 'isenclosedby', 'istuplist', 

423 'joined', 'joined_', 'nearestOn3', 'nearestOn4', 

424 'parseUTM', 'perimeterof', 'polygon', 

425 'scalar', 'simplify2', 'simplifyRDPm', 'simplifyVWm', 

426 'tienstra', 'toUtm', 'triAngle4', 

427 'unsign0', 'unStr', 'utmZoneBand2'), 

428 deprecated_nvector=_a('LatLonNvectorBase', 'Nvector', 'sumOf', 'NorthPole', 'SouthPole'),) 

429 

430 

431class _ALL_MODS(_internals._MODS_Base): 

432 '''(INTERNAL) Memoized import of any L{pygeodesy} module. 

433 ''' 

434 def __getattr__(self, name): 

435 '''Get a C{pygeodesy} module or attribute by B{C{name}}. 

436 

437 @arg name: Un/qualified module or qualified attribute name (C{str}). 

438 

439 @raise ImportError: Importing module B{C{name}} failed. 

440 

441 @raise AttributeError: No attribute named B{C{name}}. 

442 ''' 

443 try: 

444 v = _lazy_dict[name] # package.__dict__ 

445 except KeyError: 

446 v = _lazy_module(name) # package.__getattr__ 

447 if _tailof(typename(v)) != name: 

448 try: 

449 v = getattr(v, _tailof(name)) 

450 except AttributeError: 

451 pass # XXX LazyAttributeError? 

452 return v 

453 

454 def getattr(self, name, *attr_dflt): # , parent=_pygeodesy_ 

455 '''Get an attribute of/or a C{pygeodesy} module. 

456 

457 @arg name: Un/qualified module name (C{str}). 

458 @arg attr_dflt: Optional attribute name (C{str}) and 

459 optional default value (any C{type}). 

460 

461 @return: The C{pygeodesy} module's attribute value. 

462 

463 @raise ImportError: Importing module B{C{name}} failed. 

464 

465 @raise AttributeError: No attribute named B{C{attr}}. 

466 ''' 

467 v = self.getmodule(name) 

468 if attr_dflt: 

469 v = getattr(v, *attr_dflt) 

470 return v 

471 

472 def getmodule(self, name, parent=_pygeodesy_): 

473 '''Get a C{pygeodesy} module or the C{__main__}. 

474 

475 @arg name: Un/qualified module name (C{str}). 

476 

477 @return: The C{pygeodesy} module. 

478 

479 @raise ImportError: Importing module B{C{name}} failed. 

480 ''' 

481 if _headof(name) != parent and name != _DMAIN_: 

482 name = _DOT_(parent, name) 

483 try: 

484 return _sys.modules[name] 

485 except KeyError: 

486 return _getmodule(name, parent) 

487 

488 def imported(self, name): 

489 '''Return module or package C{name} if already imported. 

490 ''' 

491 return _sys.modules.get(name, None) 

492 

493 def into(self, **mod_DNAME): 

494 '''Deferred import of module C{mod} into module C{_DNAME_} 

495 and overwrite C{_DNAME_._mod} to module C{mod}, I{once} 

496 at the first access of an attribute of module C{mod}. 

497 ''' 

498 # assert len(mod_DNAME) == 1 

499 # mod, dun = mod_DNAME.popitem() 

500 

501 class _Into(object): 

502 

503 def __getattr__(unused, name): 

504 m = _getmodinto(mod_DNAME, _Into) 

505 return getattr(m, name) 

506 

507 return _Into() 

508 

509# @_Property_RO 

510# def _isBoolean(self): 

511# '''(INTERNAL) Get function C(.booleans.isBoolean}, I{once}. 

512# ''' 

513# return self.booleans.isBoolean 

514 

515 def items(self): # no module named 'items' 

516 '''Yield the modules imported so far. 

517 ''' 

518 for n, m in _sys.modules.items(): 

519 if _headof(n) == _pygeodesy_: 

520 yield n, m 

521 

522 @_Property_RO 

523 def _triaxials_triaxial5(self): 

524 '''(INTERNAL) Get module C{triaxial.triaxials}. 

525 ''' 

526 return self.triaxials.triaxial5 

527 

528_internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton 

529 

530__all__ = _ALL_LAZY.lazily 

531__version__ = '26.02.09' 

532 

533 

534def _ALL_OTHER(*objs): 

535 '''(INTERNAL) Get class and function B{C{objs}} for __all__. 

536 ''' 

537 def _interned(o): # intern'd base name 

538 n = _tailof(typename(o)) 

539 i = NN(_UNDER_, n, _UNDER_) # intern'd 

540 return getattr(_interns, i, n) 

541 

542 return tuple(map(_interned, objs)) # map2 

543 

544 

545if _FOR_DOCS: # PYCHOK no cover 

546 _ALL_DOCS = _ALL_OTHER 

547 # (INTERNAL) Only export B{C{objs.__name__}} when making the 

548 # docs to force C{epydoc} to include certain classes, methods, 

549 # functions and other names in the documentation. Using the 

550 # C{epydoc --private ...} command line option tends to include 

551 # too much internal documentation. 

552else: 

553 def _ALL_DOCS(*unused): 

554 return () 

555 

556 

557def _all_deprecates(): 

558 '''(INTERNAL) Build C{dict} of all deprecated imports and attributes. 

559 ''' 

560 D = _ALL_DEPRECATES 

561 if not D: 

562 _ALL_DEPRECATED.fill_D(D, _all_deprecates) # see _all_imports() 

563 return D 

564 

565_ALL_DEPRECATES = _Dict() # PYCHOK _ALL_DEPRECATED.imports() 

566 

567 

568def _all_enums(): 

569 '''(INTERNAL) Yield all C{(mod_, tuple)} pairs for C{__init__._all}. 

570 ''' 

571 # assert _init__all__ 

572 for mod_t in _ALL_LAZY.enums(): 

573 yield mod_t 

574 if _FOR_DOCS: 

575 for mod_t in _ALL_DEPRECATED.enums(): 

576 yield mod_t 

577 

578 

579def _all_imports(): 

580 '''(INTERNAL) Build C{dict} of all lazy imports. 

581 ''' 

582 # imports naming conventions stored below - [<key>] = <from>: 

583 # import <module> - [<module>] = <module> 

584 # from <module> import <attr> - [<attr>] = <module> 

585 # from pygeodesy import <attr> - [<attr>] = <attr> 

586 # from <module> import <attr> as <name> - [<name>] = <module>.<attr>. 

587 D = _ALL_IMPORTS 

588 if not D: 

589 _ALL_LAZY.fill_D(D, _all_imports) # see _all_deprecates() 

590 return D 

591 

592_ALL_IMPORTS = _Dict() # PYCHOK _ALL_LAZY.imports() 

593 

594 

595def _all_missing2(_all_): 

596 '''(INTERNAL) Get diffs between pygeodesy.__all__ and lazily._all_imports. 

597 ''' 

598 def _diff(one, two): 

599 return tuple(sorted(a for a in one if a not in two)) 

600 

601 _alzy = _Dict((a, a) for a in _ALL_INIT) 

602 _alzy.update(_all_imports()) # without _all_backups! 

603 return ((_DOT_(_lazily_, _all_imports.__name__), _diff(_all_, _alzy)), 

604 (_DOT_(_pygeodesy_, _DALL_), _diff(_alzy.keys(), _all_))) 

605 

606 

607def _getattras(attr_as): # test/testDeprecated 

608 '''(INTERNAL) Get the C{"as name"} or C{"name"} of a lazy entry. 

609 ''' 

610 a_, _, as_ = attr_as.partition(_asSPACED_) 

611 return as_ or a_.rstrip(_DOT_) 

612 

613 

614def _getmodattr(m, name, mod=_pygeodesy_): 

615 '''(INTERNAL) Get attr C{m.name}. 

616 ''' 

617 try: 

618 return getattr(m, name) 

619 except AttributeError: 

620 name = _DOT_(typename(m, mod), name) 

621 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76> 

622 raise LazyAttributeError(_no_(_attribute_), txt=name) 

623 

624 

625def _getmodinto(mod_DNAME, *Intos): 

626 '''(INTERNAL) Core of C{_ALL_MODS.into}. 

627 ''' 

628 _MODS = _ALL_MODS 

629 mod, dun = _MODS.errors._xkwds_item2(mod_DNAME) 

630 _mod = _UNDER_(NN, mod) 

631 d = _MODS.getmodule(dun) # '__main__' OK 

632 i = _getmodattr(d, _mod, dun) 

633 assert isinstance(i, Intos) 

634 m = _MODS.getmodule(mod) 

635 setattr(d, _mod, m) # overwrite C{d._mod} 

636 if isLazy and isLazy > 1: 

637 t = _SPACE_(_HASH_, _imported_, m.__name__) # typename(m) 

638 _hash_imported(t, _MODS.into.__name__) 

639 assert getattr(d, _mod, None) is m 

640 return m 

641 

642 

643def _getmodule(name, *parent): 

644 '''(INTERNAL) Wrapper for C{import_module}. 

645 ''' 

646 try: 

647 return import_module(name, parent) 

648 except ImportError: 

649 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76> 

650 raise LazyImportError(_no_(_module_), txt=name) 

651 

652 

653def _hash_imported(t, by_into, up=3): 

654 '''(INTERNAL) Helper for C{_lazy_import2} and C{_ALL_MODS.into}. 

655 ''' 

656 if isLazy and isLazy > 2: 

657 try: # see C{internals._caller3} 

658 _, f, s = _caller3(up) 

659 t = _SPACE_(t, by_into, f, _line_, s) 

660 except ValueError: 

661 pass 

662 printf(t) # XXX print 

663 

664 

665# def _lazy_attributes(DUNDER_name): 

666# '''(INTERNAL) Return a function to C{B{__name__}.__getattr__(attr)} 

667# on lazily imported modules and sub-modules. 

668# ''' 

669# if _unlazy: 

670# raise AssertionError(_COMMASPACE_(DUNDER_name, _not_(_DEPRECATED_))) 

671# 

672# def _getattr(attr, *dflt): 

673# try: # a module name 

674# return _ALL_MODS.getmodule(attr) 

675# except (AttributeError, ImportError): 

676# return _ALL_MODS.getattr(DUNDER_name, attr, *dflt) 

677# 

678# return _getattr 

679 

680 

681_lazy_dict = {} # PYCHOK overwritten by _lazy_import2 

682 

683 

684def _lazy_import2(pack): # MCCABE 14 

685 '''Check for and set up C{lazy import}. 

686 

687 @arg pack: The name of the package (C{str}) performing the imports, 

688 to help resolving relative imports, usually C{__package__}. 

689 

690 @return: 2-Tuple C{(package, getattr)} of the importing package for 

691 easy reference within itself and the callable to be set to 

692 C{package.__getattr__}. 

693 

694 @raise LazyAttributeError: The package, module or attribute name is 

695 invalid or does not exist. 

696 

697 @raise LazyImportError: Lazy import not supported or not enabled or 

698 an import failed. 

699 

700 @note: This is I{Brett Cannon}'s function U{modutil.lazy_import 

701 <https://GitHub.com/brettcannon/modutil/blob/master/modutil.py>} 

702 modified to handle the C{__all__} and C{__dir__} attributes and 

703 call C{importlib.import_module(<module>.<name>, ...)} without 

704 causing a C{ModuleNotFoundError}. 

705 

706 @see: The original U{modutil<https://PyPI.org/project/modutil>}, 

707 U{PEP 562<https://www.Python.org/dev/peps/pep-0562>} and the 

708 U{new way<https://Snarky.CA/lazy-importing-in-python-3-7/>}. 

709 ''' 

710 if pack != _pygeodesy_ or _unlazy: # Python 3.7+ # PYCHOK no cover 

711 t = _DOT_(pack, typename(_lazy_import2)) 

712 raise LazyImportError(_no_(t), txt=_versions()) 

713 

714 package, parent = _lazy_init2(pack) # _pygeodesy_ 

715 

716 _DPACKAGE_ = '__package__' 

717 _lazily_imported_ = _SPACE_(_HASH_, _lazily_, _imported_, parent) 

718 

719 sub_packages = set((parent, NN) + tuple( 

720 _DOT_(parent, s) for s in _SUB_PACKAGES)) 

721 imports = _all_imports() 

722 deprecates = _all_deprecates() 

723 

724 def __getattr__(name): # __getattr__ only for Python 3.7+ 

725 # only called once for each undefined pygeodesy attribute 

726 mod = imports.get(name, NN) or deprecates.get(name, NN) 

727 if mod: 

728 # importlib.import_module() implicitly sets sub-modules 

729 # on this module as appropriate for direct imports (see 

730 # note in the _lazy_import2.__doc__ above). 

731 if mod.endswith(_DOT_): # import mod[.attr] as name 

732 mod, _, attr = mod[:-1].rpartition(_DOT_) 

733 else: # from mod import name 

734 attr = name 

735 v = _getmodule(_DOT_(pack, mod), parent) 

736 t = getattr(v, _DPACKAGE_, None) 

737 if t not in sub_packages: # invalid module package 

738 raise LazyImportError(_DOT_(mod, _DPACKAGE_), t) 

739 if attr: # get mod.attr 

740 v = _getmodattr(v, attr, mod) 

741 

742 elif name in (_DALL_,): # XXX _Ddir_, _Dmembers_? 

743 v = _ALL_INIT + tuple(imports.keys()) 

744 else: # PYCHOK no cover 

745 t = _no_(_module_, _or_, _attribute_) 

746 # <https://GitHub.com/mrJean1/PyGeodesy/issues/76> 

747 raise LazyAttributeError(t, txt=_DOT_(parent, name)) 

748 

749 setattr(package, name, v) # package.__dict__[name] = val 

750 if isLazy > 1: 

751 t = _DOT_(_lazily_imported_, name) 

752 if mod and _tailof(mod) != name: 

753 t = _SPACE_(t, _from_, _DOT_(NN, mod)) 

754 _hash_imported(t, _by_) 

755 

756 return v # __getattr__ 

757 

758 global _lazy_dict, _lazy_module 

759 _lazy_dict = package.__dict__ 

760 _lazy_module = __getattr__ 

761 

762 return package, __getattr__ # _lazy_import2 

763 

764 

765# def _lazy_import_all(Dname): 

766# '''(INTERNAL) Return a function mimicking C{from B{__name__} import *}, 

767# of all items, see .deprecated.__init__ 

768# ''' 

769# if _unlazy: 

770# raise AssertionError(_COMMASPACE_(Dname, _not_(_DEPRECATED_))) 

771# 

772# _getattr = _lazy_attributes(Dname) # __name__.__getattr__ 

773# _import_start = _lazy_import_star(Dname, ALL_=_ALL_IMPORTS) 

774# 

775# def _import_all(attr, *dflt): 

776# return _import_star(Dname) if attr == _DALL_ else \ 

777# _getattr(attr, *dflt) 

778# 

779# return _import_all 

780 

781 

782def _lazy_import_as(DUNDER_name): 

783 '''(INTERNAL) Return a function to C{import B{__name__}.mod as mod} 

784 I{of modules only}, see .deprecated, .rhumb or get an attribute 

785 lazily exported by C{__name__}. 

786 ''' 

787 if _unlazy: 

788 return None 

789 

790 def _import_as(mod): 

791 try: 

792 return _ALL_MODS.getmodule(_DOT_(DUNDER_name, mod)) 

793 except ImportError: 

794 return _lazy_module(mod) 

795 

796 return _import_as 

797 

798 

799# def _lazy_import_star(DUNDER_name, ALL_=_ALL_DEPRECATES): 

800# '''(INTERNAL) Return a function to mimick C{from B{__name__} import *}, 

801# of all DEPRECATED items, see .deprecated, .testDeprecated 

802# ''' 

803# if _unlazy: 

804# raise AssertionError(_COMMASPACE_(DUNDER_name, _not_(_DEPRECATED_))) 

805# 

806# def _import_star(_into_): 

807# '''Do C{from B{__name__} import *} inside module C{B{__into__}}. 

808# ''' 

809# d = dict() 

810# nm = _tailof(DUNDER_name) 

811# _g = _ALL_MODS.getattr # pygeodesy.__getattr__ 

812# for a, m in ALL_.items(): 

813# if _headof(m) == nm: 

814# try: 

815# d[a] = _g(m, a) 

816# except (AttributeError, ImportError): 

817# pass 

818# _sys.modules[_into_].__dict__.update(d) 

819# return d.keys() # imported names 

820# 

821# return _import_star 

822 

823 

824def _lazy_init2(pack): 

825 '''(INTERNAL) Initialize lazy import and set globals C{isLazy} and C{_unLazy0}. 

826 

827 @arg pack: The name of the package (C{str}) performing the imports, 

828 to resolve relative imports, usually C{__package__}. 

829 

830 @return: 2-Tuple C{(package, parent)} with the importing C{package} 

831 for easy reference within itself and its name aka the 

832 C(package)'s C{parent}, same as B{C{pack}}. 

833 

834 @raise LazyImportError: Lazy import not supported or not enabled, 

835 an import failed or the package name is 

836 invalid or does not exist. 

837 

838 @note: Global C{isLazy} is set accordingly. 

839 ''' 

840 global isLazy, _unLazy0 

841 

842 E = _internals._PYGEODESY_ENV('LAZY_IMPORT') 

843 z = _internals._getenv(E, _interns._1_) # 1 default on 3.7+ 

844 z = z.strip() # like PYTHONVERBOSE et.al. 

845 isLazy = int(z) if z.isdigit() else (1 if z else 0) 

846 

847 _unLazy0 = _unlazy or not isLazy # pre-3.7 or w/o lazy import 

848 

849 if isLazy < 1: # invalid, not enabled 

850 raise LazyImportError(E, repr(z), txt_not_='enabled') 

851 if _sys.flags.verbose: # PYCHOK no cover 

852 isLazy += 1 

853 

854 try: # to initialize in Python 3+ 

855 package = import_module(pack) 

856 parent = package.__spec__.parent # __spec__ only in Python 3.7+ 

857 if parent != pack: # assert 

858 t = _COMMASPACE_(parent, _not_(pack)) # PYCHOK no cover 

859 raise AttributeError(_EQUALSPACED_('parent', t)) 

860 

861 except (AttributeError, ImportError) as x: 

862 isLazy = False # failed 

863 z = typename(_lazy_init2) 

864 raise LazyImportError(z, pack, cause=x) 

865 

866 return package, parent 

867 

868 

869def _lazy_module(name): # overwritten by _lazy_import2 

870 '''(INTERNAL) Get or import a C{pygeodesy} module. 

871 ''' 

872 try: # most likely ... module has been imported 

873 m = _ALL_MODS.getmodule(name) 

874 except (AttributeError, ImportError) as x: 

875 raise LazyImportError(name, cause=x) 

876 _lazy_dict[name] = m # cache 

877 return m 

878 

879 

880# def _lazy_subs(__name__, force=_FOR_DOCS, over=False): 

881# '''(INTERNAL) Return the names of a __name__ package's sub-packages 

882# and update the package's C{__dict__} accordingly. 

883# ''' 

884# sm = dict() 

885# if force and __name__ != _DMAIN_: 

886# nm = _tailof(__name__) 

887# _a = _ALL_MODS.getattr 

888# _m = _ALL_MODS.getmodule 

889# d = _a(__name__, _DDICT_, {}) 

890# for n in _a(__name__, _DALL_, ()): 

891# try: # n is a class name, get its mod name 

892# m = _a(__name__, n).__module__ 

893# n, s = m.split(_DOT_)[-2:] 

894# if n == nm and s not in sm: 

895# m = _m(m) # == import m as s 

896# sm[s] = m if over else d.get(s, m) 

897# except (AttributeError, ImportError, ValueError) as x: 

898# pass 

899# d.update(sm) 

900# 

901# return _ALL_OTHER(*sm.values()) 

902 

903 

904if __name__ == _DMAIN_: 

905 

906 def _main(): 

907 from timeit import timeit 

908 

909 def t1(): 

910 from pygeodesy.trf import RefFrame 

911 return RefFrame 

912 

913 def t2(): 

914 return _ALL_MODS.trf.RefFrame 

915 

916 assert t1() is t2() # prime each 

917 

918 t1 = timeit(t1, number=1000000) 

919 t2 = timeit(t2, number=1000000) 

920 A = typename(_ALL_MODS) 

921 v = _versions() 

922 printf('%.6f import vs %.6f %s: %.2fX, %s', t1, t2, A, (t1 / t2), v) 

923 

924 _main() 

925 

926# % python3.14 -W ignore -m pygeodesy.lazily 

927# 0.061219 import vs 0.047896 _ALL_MODS: 1.28X, pygeodesy 25.12.6 Python 3.14.0 64bit arm64 macOS 26.1 

928 

929# % python3.13 -W ignore -m pygeodesy.lazily 

930# 0.054235 import vs 0.052469 _ALL_MODS: 1.03X, pygeodesy 25.4.24 Python 3.13.3 64bit arm64 macOS 15.4 

931 

932# % python2 -m pygeodesy.lazily 

933# 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 

934 

935# % python3.13 -W ignore -m pygeodesy.lazily 

936# 0.106602 import vs 0.078136 _ALL_MODS: 1.36X, pygeodesy 24.10.24 Python 3.13.0 64bit arm64 macOS 14.6.1 

937 

938# % python3.12 -W ignore -m pygeodesy.lazily 

939# 0.138844 import vs 0.080458 _ALL_MODS: 1.73X, pygeodesy 24.10.24 Python 3.12.7 64bit arm64 macOS 14.6.1 

940 

941# % python3.11 -W ignore -m pygeodesy.lazily 

942# 0.387520 import vs 0.254229 _ALL_MODS: 1.52X, pygeodesy 24.10.24 Python 3.11.5 64bit arm64 macOS 14.6.1 

943 

944# % python3.10 -W ignore -m pygeodesy.lazily 

945# 0.371269 import vs 0.272897 _ALL_MODS: 1.36X, pygeodesy 24.10.24 Python 3.10.8 64bit arm64 macOS 14.6.1 

946 

947# % python3.8 -W ignore -m pygeodesy.lazily 

948# 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 

949 

950# % python2 -m pygeodesy.lazily 

951# 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 

952 

953# **) MIT License 

954# 

955# Copyright (C) 2018-2026 -- mrJean1 at Gmail -- All Rights Reserved. 

956# 

957# Permission is hereby granted, free of charge, to any person obtaining a 

958# copy of this software and associated documentation files (the "Software"), 

959# to deal in the Software without restriction, including without limitation 

960# the rights to use, copy, modify, merge, publish, distribute, sublicense, 

961# and/or sell copies of the Software, and to permit persons to whom the 

962# Software is furnished to do so, subject to the following conditions: 

963# 

964# The above copyright notice and this permission notice shall be included 

965# in all copies or substantial portions of the Software. 

966# 

967# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 

968# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

969# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 

970# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 

971# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 

972# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 

973# OTHER DEALINGS IN THE SOFTWARE.