Coverage for pygeodesy/lazily.py: 93%

191 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2024-06-01 11:43 -0400

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

33from pygeodesy.internals import _caller3, _dunder_nameof, _dunder_ismain, \ 

34 _headof, _osversion2, printf, _Pythonarchine, \ 

35 _tailof 

36from pygeodesy.interns import NN, _areaOf_, _attribute_, _by_, _COLONSPACE_, \ 

37 _COMMASPACE_, _doesn_t_exist_, _DOT_, _enabled_, \ 

38 _EQUALSPACED_, _from_, _HASH_, _immutable_, \ 

39 _isclockwise_, _ispolar_, _line_, _module_, \ 

40 _no_, _NorthPole_, _not_, _or_, _pygeodesy_, \ 

41 _pygeodesy_abspath_, _SouthPole_, \ 

42 _SPACE_, _sub_packages, _sys, _UNDER_, _version_, \ 

43 _intern # function 

44# from pygeodesy.streprs import unstr # _ALL_MODS 

45 

46from os import getenv as _getenv 

47try: 

48 from importlib import import_module 

49except ImportError as x: # Python 2.6- 

50 _str_x = str(x) 

51 

52 def import_module(name, *package): 

53 t = _ALL_MODS.streprs.unstr(import_module, name, *package) 

54 raise LazyImportError(t, txt=_str_x) 

55 

56__as__ = ' as ' 

57_dunder_all_ = '__all__' # in .__main__ 

58_dunder_package_ = '__package__' 

59_FOR_DOCS = _getenv('PYGEODESY_FOR_DOCS', NN) # for epydoc ... 

60_from_DOT__ = _SPACE_(NN, _from_, _DOT_) 

61_i0 = () # PYCHOK empty tuple 

62_init__all__ = _FOR_DOCS or _getenv('PYGEODESY_INIT__ALL__', _dunder_all_) == _dunder_all_ # PYCHOK exported 

63_lazily_ = 'lazily' 

64_lazily_imported__ = _SPACE_(_HASH_, _lazily_, 'imported', NN) 

65_PYGEODESY_GEOCONVERT_ = 'PYGEODESY_GEOCONVERT' # PYCHOK .mgrs, test.bases 

66_PYGEODESY_GEODSOLVE_ = 'PYGEODESY_GEODSOLVE' # PYCHOK .geodsolve, test.bases 

67_PYGEODESY_LAZY_IMPORT_ = 'PYGEODESY_LAZY_IMPORT' 

68_PYGEODESY_RHUMBSOLVE_ = 'PYGEODESY_RHUMBSOLVE' # PYCHOK .rhumb.solve, test.bases 

69_PYTHON_X_DEV = getattr(_sys, '_xoptions', {}).get('dev', # Python 3.2+ 

70 _getenv('PYTHONDEVMODE', NN)) # PYCHOK exported 

71_sys_version_info2 = _sys.version_info[:2] # in .basics, .fmath, ... 

72_unlazy = _unLazy0 = _isfrozen or _sys_version_info2 < (3, 7) # PYCHOK mod.__getattr__ 3.7+ 

73_WARNINGS_X_DEV = _getenv('PYGEODESY_WARNINGS', NN) and ( 

74 _PYTHON_X_DEV or bool(_sys.warnoptions)) # PYCHOK .props 

75# @module_property[_RO?] <https://GitHub.com/jtushman/proxy_tools/> 

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

77 

78 

79class LazyAttributeError(AttributeError): 

80 '''Raised if a C{lazily imported} attribute is missing or invalid. 

81 ''' 

82 def __init__(self, *name_value, **txt): 

83 _ALL_MODS.errors._error_init(AttributeError, self, name_value, **txt) 

84 

85 

86class LazyImportError(ImportError): 

87 '''Raised if C{lazy import} is not supported, disabled or failed some other way. 

88 ''' 

89 def __init__(self, *name_value, **txt): 

90 _ALL_MODS.errors._error_init(ImportError, self, name_value, **txt) 

91 

92 

93class _Dict(dict): 

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

95 ''' 

96 _name = NN 

97 

98 def __getattr__(self, attr): 

99 try: 

100 return self[attr] 

101 except KeyError: 

102 return dict.__getattr__(self, attr) 

103 

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

105# if attr in self: 

106# self[attr] = value 

107# else: 

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

109 

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

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

112 

113 @raise AssertionError: The B{C{name}} already exists 

114 with a different B{C{mod_}}. 

115 ''' 

116 if name in self: 

117 sub = self[name] # duplicate OK 

118 if sub != mod_ and sub not in subs: # PYCHOK no cover 

119 t = _DOT_(self._name, name) 

120 t = _COLONSPACE_(t, repr(sub)) 

121 t = _COMMASPACE_(t, _not_(repr(mod_))) 

122 raise AssertionError(t) 

123 else: 

124 self[name] = mod_ 

125 

126 def _NAME(self, which): 

127 self._name = _intern(_dunder_nameof(which).upper()) 

128 

129 

130class _NamedEnum_RO(dict): 

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

132 ''' 

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

134 

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

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

137 

138 def __getattr__(self, attr): 

139 try: 

140 return self[attr] 

141 except KeyError: 

142 t = self._DOT_(attr) 

143 raise LazyAttributeError(t, txt=_doesn_t_exist_) 

144 

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

146 t = _EQUALSPACED_(self._DOT_(attr), repr(value)) 

147 raise LazyAttributeError(_immutable_, txt=t) 

148 

149 def enums(self): 

150 # Yield all C{(mod_, tuple)} pairs 

151 for m, t in dict.items(self): 

152 n = m.replace(_UNDER_, _DOT_) 

153 if n != m: 

154 if m.startswith(_UNDER_): 

155 continue # skip _name= ... 

156 u = m.rstrip(_UNDER_) 

157 if u != m: 

158 u = len(u) 

159 n = n[:u] + m[u:] 

160 yield n, t 

161 

162 def fill_D(self, _D, which): 

163 # Fill C{_Dict _D}. 

164 _D._NAME(which) 

165 _a = _D.add 

166 for m, t in self.enums(): 

167 _a(m, _DOT_(m, NN, NN)) # import module 

168 for a in t: 

169 a, _, as_ = a.partition(__as__) 

170 if as_: # import attr as attr_ 

171 _a(as_, _DOT_(m, a, NN), *_sub_packages) 

172 else: 

173 _a(a, m) 

174 return _D 

175 

176 

177def _i(*names): 

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

179 ''' 

180 return tuple(map(_intern, names)) if names else _i0 

181 

182 

183def _ALL_ATTRS(*attrs): 

184 '''(INTERNAL) Unravel all exported module attributes. 

185 ''' 

186 t = () 

187 for attr in attrs: 

188 t += tuple(map(_attrof, attr)) 

189 return t 

190 

191 

192_ALL_INIT = _i(_pygeodesy_abspath_, _version_) 

193 

194# __all__ value for most modules, accessible as _ALL_LAZY.<module> 

195_ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY', 

196 albers=_i('AlbersEqualArea', 'AlbersEqualArea2', 'AlbersEqualArea4', 

197 'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth', 

198 'AlbersError', 'Albers7Tuple'), 

199 auxilats=_i(), # module only 

200 azimuthal=_i('AzimuthalError', 'Azimuthal7Tuple', 

201 'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney', 

202 'Gnomonic', 'GnomonicExact', 'GnomonicGeodSolve', 'GnomonicKarney', 

203 'LambertEqualArea', 'Orthographic', 'Stereographic', 

204 'equidistant', 'gnomonic'), 

205 basics=_i('clips', 'copysign0', 'copytype', 'halfs2', 

206 'int1s', 'isbool', 'isCartesian', 'isclass', 'iscomplex', 'isDEPRECATED', 'isfloat', 

207 'isidentifier', 'isinstanceof', 'isint', 'isiterable', 'isiterablen', 'iskeyword', 

208 'isLatLon', 'islistuple', 'isNvector', 'isodd', 'isscalar', 'issequence', 'isstr', 

209 'issubclassof', 'itemsorted', 

210 'len2', 'map1', 'map2', 'neg', 'neg_', 

211 'signBit', 'signOf', 'splice', 'str2ub', 'ub2str', 'unsigned0'), 

212 booleans=_i('BooleanFHP', 'BooleanGH', 'LatLonFHP', 'LatLonGH', 

213 'isBoolean'), 

214 cartesianBase=_i('RadiusThetaPhi3Tuple', 'rtp2xyz', 'rtp2xyz_', 'xyz2rtp', 'xyz2rtp_'), 

215 clipy=_i('ClipCS4Tuple', 'ClipFHP4Tuple', 'ClipGH4Tuple', 'ClipLB6Tuple', 'ClipSH3Tuple', 

216 'clipCS4', 'clipFHP4', 'clipGH4', 'clipLB6', 'clipSH', 'clipSH3'), 

217 css=_i('CassiniSoldner', 'Css', 'CSSError', 'toCss', 

218 'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'), 

219 constants=_i('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS_2', 

220 'INF', 'INT0', 'MANT_DIG', 'MAX', 'MAX_EXP', 'MIN', 'MIN_EXP', 'NAN', 'NEG0', 'NINF', 

221 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4', 

222 'R_FM', 'R_GM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_QM', 'R_SM', 'R_VM', 

223 'float_', 'float0_', 'isclose', 'isfinite', 'isinf', 'isint0', 

224 'isnan', 'isnear0', 'isnear1', 'isnear90', 'isneg0', 'isninf', 'isnon0', 

225 'remainder'), 

226 datums=_i('Datum', 'Datums', 'Transform', 'Transforms'), 

227# deprecated=_i(), # module only 

228 dms=_i('F_D', 'F_DM', 'F_DMS', 'F_DEG', 'F_MIN', 'F_SEC', 'F_D60', 'F__E', 'F__F', 'F__G', 'F_RAD', 

229 'F_D_', 'F_DM_', 'F_DMS_', 'F_DEG_', 'F_MIN_', 'F_SEC_', 'F_D60_', 'F__E_', 'F__F_', 'F__G_', 'F_RAD_', 

230 'F_D__', 'F_DM__', 'F_DMS__', 'F_DEG__', 'F_MIN__', 'F_SEC__', 'F_D60__', 'F__E__', 'F__F__', 'F__G__', 'F_RAD__', 

231 'S_DEG', 'S_MIN', 'S_SEC', 'S_DMS', 'S_RAD', 'S_SEP', 

232 'bearingDMS', 'clipDegrees', 'clipRadians', 'compassDMS', 'compassPoint', 

233 'degDMS', 'latDMS', 'latlonDMS', 'latlonDMS_', 'lonDMS', 'normDMS', 

234 'parseDDDMMSS', 'parseDMS', 'parseDMS2', 'parse3llh', 'parseRad', 'precision', 'toDMS'), 

235 ecef=_i('EcefError', 'EcefFarrell21', 'EcefFarrell22', 'EcefKarney', 'EcefMatrix', 

236 'EcefSudano', 'Ecef9Tuple', 'EcefVeness', 'EcefYou'), 

237 elevations=_i('Elevation2Tuple', 'GeoidHeight2Tuple', 

238 'elevation2', 'geoidHeight2'), 

239 ellipsoidalBase=_i(), # module only 

240 ellipsoidalBaseDI=_i(), # module only 

241 ellipsoidalExact=_i(), # module only 

242 ellipsoidalGeodSolve=_i(), # module only 

243 ellipsoidalKarney=_i(), # module only 

244 ellipsoidalNvector=_i(), # module only 

245 ellipsoidalVincenty=_i('VincentyError',), # nothing else 

246 ellipsoids=_i('a_f2Tuple', 'Circle4Tuple', 'Curvature2Tuple', 

247 'Ellipsoid', 'Ellipsoid2', 'Ellipsoids', 

248 'a_b2e', 'a_b2e2', 'a_b2e22', 'a_b2e32', 'a_b2f', 'a_b2f_', 'a_b2f2', 'a_b2n', 

249 'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a', 

250 'e2f', 'e22f', 

251 'f2e2', 'f2e22', 'f2e32', 'f_2f', 'f2f_', 'f2f2', 'f2n', 'n2e2', 'n2f', 'n2f_'), 

252 elliptic=_i('Elliptic', 'EllipticError', 'Elliptic3Tuple'), 

253 epsg=_i('Epsg', 'EPSGError'), 

254 errors=_i('AuxError', 'ClipError', 'CrossError', 'GeodesicError', 'IntersectionError', 

255 'NumPyError', 'LenError', 'LimitError', 'MGRSError', 

256 'ParseError', 'PointsError', 'RangeError', 'RhumbError', 

257 'SciPyError', 'SciPyWarning', 'TRFError', 'TriangleError', 'UnitError', 'VectorError', 

258 'crosserrors', 'exception_chaining', 'isError', 'limiterrors', 'rangerrors'), 

259 etm=_i('Etm', 'ETMError', 'ExactTransverseMercator', 

260 'parseETM5', 'toEtm8'), 

261 fmath=_i('Fdot', 'Fhorner', 'Fhypot', 'Fpolynomial', 'Fpowers', 'Fcbrt', 'Froot', 'Fsqrt', 

262 'bqrt', 'cbrt', 'cbrt2', 'euclid', 'euclid_', 

263 'facos1', 'fasin1', 'fatan', 'fatan1', 'fatan2', 'favg', 

264 'fdot', 'fdot3', 'fmean', 'fmean_', 'fhorner', 'fidw', 'fpolynomial', 

265 'fpowers', 'fprod', 'frandoms', 'frange', 'freduce', 'fremainder', 

266 'hypot', 'hypot_', 'hypot1', 'hypot2', 'hypot2_', 

267 'norm2', 'norm_', 'sqrt0', 'sqrt3', 'sqrt_a', 'zcrt', 'zqrt'), 

268 formy=_i('Radical2Tuple', 

269 'antipode', 'antipode_', 'bearing', 'bearing_', 

270 'compassAngle', 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_', 

271 'cosineAndoyerLambert', 'cosineAndoyerLambert_', 'cosineLaw', 'cosineLaw_', 

272 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_', 

273 'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_', 

274 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_', 

275 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_', 

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

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

278 'latlon2n_xyz', 'normal', 'normal_', 'n_xyz2latlon', 'n_xyz2philam', 

279 'opposing', 'opposing_', 'philam2n_xyz', 'radical2', 

280 'thomas', 'thomas_', 'vincentys', 'vincentys_'), 

281 frechet=_i('Frechet', 'FrechetDegrees', 'FrechetError', 'FrechetRadians', 

282 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert', 

283 'FrechetCosineLaw', 'FrechetDistanceTo', 'FrechetEquirectangular', 

284 'FrechetEuclidean', 'FrechetExact', 'FrechetFlatLocal', 'FrechetFlatPolar', 

285 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney', 'FrechetThomas', 

286 'FrechetVincentys', 'Frechet6Tuple', 

287 'frechet_'), 

288 fstats=_i('Fcook', 'Flinear', 'Fwelford'), 

289 fsums=_i('Fsum', 'DivMod2Tuple', 'Fsum2Tuple', 'ResidualError', 

290 'fsum', 'fsum_', 'fsumf_', 'fsum1', 'fsum1_', 'fsum1f_'), 

291 gars=_i('Garef', 'GARSError'), 

292 geodesicw=_i('Geodesic', 'GeodesicLine', 'Geodesic_WGS84'), 

293 geodesicx=_i('gx', 'gxarea', 'gxbases', 'gxline', # modules, see _sub_packages 

294 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'), 

295 geodsolve=_i('GeodesicSolve', 'GeodesicLineSolve', 'GeodSolve12Tuple'), 

296 geohash=_i('Geohash', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple'), 

297 geoids=_i('GeoidError', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights', 

298 'PGMError', 'GeoidHeight5Tuple'), 

299 hausdorff=_i('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians', 

300 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert', 

301 'HausdorffCosineLaw', 'HausdorffDistanceTo', 'HausdorffEquirectangular', 

302 'HausdorffEuclidean', 'HausdorffExact', 'HausdorffFlatLocal', 'HausdorffFlatPolar', 

303 'HausdorffHaversine', 'HausdorffHubeny', 'HausdorffKarney', 'HausdorffThomas', 

304 'HausdorffVincentys', 'Hausdorff6Tuple', 

305 'hausdorff_', 'randomrangenerator'), 

306 heights=_i('HeightCubic', 'HeightError', 

307 'HeightIDWcosineAndoyerLambert', 'HeightIDWcosineForsytheAndoyerLambert', 

308 'HeightIDWcosineLaw', 'HeightIDWdistanceTo', 'HeightIDWequirectangular', 

309 'HeightIDWeuclidean', 'HeightIDWexact', 'HeightIDWflatLocal', 'HeightIDWflatPolar', 

310 'HeightIDWhaversine', 'HeightIDWhubeny', 'HeightIDWkarney', 'HeightIDWthomas', 

311 'HeightIDWvincentys', 'HeightLinear', 'HeightLSQBiSpline', 'HeightSmoothBiSpline'), 

312 internals=_internals.__all__, 

313 interns=_interns.__all__, 

314 iters=_i('LatLon2PsxyIter', 'PointsIter', 'points2', 

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

316 karney=_i('Area3Tuple', 'Caps', 'Direct9Tuple', 'GDict', 'Inverse10Tuple', 'Rhumb8Tuple'), 

317 ktm=_i('KTMError', 'KTransverseMercator'), 

318 latlonBase=_i(), # module only 

319 lazily=_i('LazyAttributeError', 'LazyImportError', 'isLazy'), 

320 lcc=_i('Conic', 'Conics', 'Lcc', 'LCCError', 'toLcc'), 

321 ltp=_i('Attitude', 'AttitudeError', 'ChLV', 'ChLVa', 'ChLVe', 'Frustum', 

322 'LocalCartesian', 'LocalError', 'Ltp', 'tyr3d'), 

323 ltpTuples=_i('Aer', 'Aer4Tuple', 'Attitude4Tuple', 

324 'ChLVEN2Tuple', 'ChLV9Tuple', 'ChLVYX2Tuple', 'ChLVyx2Tuple', 

325 'Enu', 'Enu4Tuple', 'Footprint5Tuple', 'Local9Tuple', 'Los', 

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

327 mgrs=_i('Mgrs', 'parseMGRS', 'toMgrs', 'Mgrs4Tuple', 'Mgrs6Tuple'), 

328 named=_i('ADict', 

329 'callername', 'classname', 'classnaming', 'modulename', 

330 'nameof', 'notImplemented', 'notOverloaded'), 

331 namedTuples=_i('Bearing2Tuple', 'Bounds2Tuple', 'Bounds4Tuple', 

332 'Destination2Tuple', 'Destination3Tuple', 

333 'Distance2Tuple', 'Distance3Tuple', 'Distance4Tuple', 

334 'EasNor2Tuple', 'EasNor3Tuple', 'Forward4Tuple', 'Intersection3Tuple', 

335 'LatLon2Tuple', 'LatLon3Tuple', 'LatLon4Tuple', 

336 'LatLonDatum3Tuple', 'LatLonDatum5Tuple', 

337 'LatLonPrec3Tuple', 'LatLonPrec5Tuple', 

338 'NearestOn2Tuple', 'NearestOn3Tuple', 'NearestOn6Tuple', 'NearestOn8Tuple', 

339 'PhiLam2Tuple', 'PhiLam3Tuple', 'PhiLam4Tuple', 'Point3Tuple', 'Points2Tuple', 

340 'Reverse4Tuple', 'Triangle7Tuple', 'Triangle8Tuple', 'Trilaterate5Tuple', 

341 'UtmUps2Tuple', 'UtmUps5Tuple', 'UtmUps8Tuple', 'UtmUpsLatLon5Tuple', 

342 'Vector2Tuple', 'Vector3Tuple', 'Vector4Tuple'), 

343 nvectorBase=_i(_NorthPole_, _SouthPole_), 

344 osgr=_i('Osgr', 'OSGRError', 'parseOSGR', 'toOsgr'), 

345 points=_i('LatLon_', 'LatLon2psxy', 'Numpy2LatLon', 'Shape2Tuple', 'Tuple2LatLon', 

346 _areaOf_, 'boundsOf', 'centroidOf', 'fractional', 

347 _isclockwise_, 'isconvex', 'isconvex_', 'isenclosedBy', _ispolar_, 

348 'luneOf', 'nearestOn5', 'perimeterOf', 'quadOf'), 

349 props=_i('Property', 'Property_RO', 'property_RO', 'property_doc_', 

350 'deprecated_class', 'deprecated_function', 'deprecated_method', 

351 'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'), 

352 resections=_i('Collins5Tuple', 'ResectionError', 'Survey3Tuple', 'Tienstra7Tuple', 

353 'TriAngle5Tuple', 'TriSide2Tuple', 'TriSide4Tuple', 

354 'cassini', 'collins5', 'pierlot', 'pierlotx', 'tienstra7', 

355 'snellius3', 'wildberger3', 

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

357 rhumb=_i(), # module only 

358 rhumb_aux_=_i('RhumbAux', 'RhumbLineAux'), 

359 rhumb_ekx=_i('Rhumb', 'RhumbLine'), 

360 rhumb_solve=_i('RhumbSolve', 'RhumbLineSolve', 'RhumbSolve7Tuple'), 

361 sphericalBase=_i(), # module only 

362 sphericalNvector=_i(), # module only 

363 sphericalTrigonometry=_i(), # module only 

364 simplify=_i('simplify1', 'simplifyRDP', 'simplifyRDPm', 'simplifyRW', 'simplifyVW', 'simplifyVWm'), 

365 solveBase=_i(), # module only 

366 streprs=_i('anstr', 'attrs', 'enstr2', 'fstr', 'fstrzs', 'hstr', 'instr', 

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

368 trf=_i('RefFrame', 'RefFrames', 'TransformXform', 'TRFXform', 'TRFXform7Tuple', 

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

370 triaxials=_i('BetaOmega2Tuple', 'BetaOmega3Tuple', 'Jacobi2Tuple', 

371 'JacobiConformal', 'JacobiConformalSpherical', 

372 'Triaxial', 'Triaxial_', 'TriaxialError', 'Triaxials', 'hartzell4'), 

373 units=_i('Band', 'Bearing', 'Bearing_', 'Bool', 

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

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

376 'Lam', 'Lam_', 'Lat', 'Lat_', 'Lon', 'Lon_', 

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

378 'Phi', 'Phi_', 'Precision_', 'Radians', 'Radians_', 'Radians2', 

379 'Radius_', 'Scalar', 'Scalar_', 'Zone'), 

380 unitsBase=_i('Float', 'Int', 'Radius', 'Str'), 

381 ups=_i('Ups', 'UPSError', 'parseUPS5', 'toUps8', 'upsZoneBand5'), 

382 utily=_i('acos1', 'acre2ha', 'acre2m2', 'asin1', 'atan1', 'atan1d', 'atan2b', 'atan2d', 

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

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

385# 'degrees2grades as degrees2gons', 

386 'fathom2m', 'ft2m', 'furlong2m', 

387 'grades', 'grades400', 'grades2degrees', 'grades2radians', 

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

389 'km2m', 'm2chain', 'm2degrees', 'm2fathom', 'm2ft', 'm2furlong', 

390 'm2km', 'm2NM', 'm2radians', 'm2SM', 'm2toise', 'm2yard', 'NM2m', 

391 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m', 

392 'sincos2', 'SinCos2', 'sincos2_', 'sincos2d', 'sincos2d_', 'sincostan3', 'SM2m', 

393 'tand', 'tand_', 'tan_2', 'tanPI_2_2', 'toise2m', 'truncate', 

394 'unroll180', 'unrollPI', 

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

396 'yard2m'), 

397 utm=_i('Utm', 'UTMError', 'parseUTM5', 'toUtm8', 'utmZoneBand5'), 

398 utmups=_i('UtmUps', 'UTMUPSError', 'parseUTMUPS5', 'toUtmUps8', 

399 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'), 

400 utmupsBase=_i(), # module only 

401 vector2d=_i('Circin6Tuple', 'Circum3Tuple', 'Circum4Tuple', 'Meeus2Tuple', 'Radii11Tuple', 'Soddy4Tuple', 

402 'circin6', 'circum3', 'circum4_', 'meeus2', 'radii11', 'soddy4'), 

403 vector3d=_i('Vector3d', 'intersection3d3', 'iscolinearWith', 'nearestOn', 'nearestOn6', 'parse3d', 

404 'trilaterate2d2', 'trilaterate3d2'), 

405 vector3dBase=_i(), # module only 

406 webmercator=_i('Wm', 'WebMercatorError', 'parseWM', 'toWm', 'EasNorRadius3Tuple'), 

407 wgrs=_i('Georef', 'WGRSError'),) 

408 

409_ALL_DEPRECATED = _NamedEnum_RO(_name='_ALL_DEPRECATED', 

410 deprecated=_i('bases', 'datum', 'nvector', # DEPRECATED modules and ... 

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

412 deprecated_bases=_i('LatLonHeightBase', 'points2'), 

413 deprecated_classes=_i('ClipCS3Tuple', 'EasNorExact4Tuple', 'EcefCartesian', 'Fn_rt', 

414 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'Helmert7Tuple', 

415 'LatLonExact4Tuple', 'NearestOn4Tuple', 'Ned3Tuple', 

416 'RefFrameError', 'Rhumb7Tuple', 'RhumbOrder2Tuple', 

417 'Transform7Tuple', 'TriAngle4Tuple', 'UtmUps4Tuple'), 

418 deprecated_consterns=_i('EPS1_2', 'MANTIS', 'OK'), 

419 deprecated_datum=_i('Curvature2Tuple', 'Datum', 'Ellipsoid', 'Transform', # assert 

420 'Datums', 'Ellipsoids', 'Transforms', 

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

422 deprecated_functions=_i('anStr', 'areaof', 'atand', 'bounds', # most of the DEPRECATED functions, except ... 

423 'clipCS3', 'clipDMS', 'clipStr', 'collins', 'copysign', # ... ellipsoidal, spherical flavors 

424 'decodeEPSG2', 'encodeEPSG', 'enStr2', 'equirectangular_', 'equirectangular3', 

425 'excessAbc', 'excessGirard', 'excessLHuilier', 

426 'false2f', 'falsed2f', 'float0', 'fStr', 'fStrzs', 'hypot3', 

427 'inStr', 'isenclosedby', 'istuplist', 

428 'joined', 'joined_', 'nearestOn3', 'nearestOn4', 

429 'parseUTM', 'perimeterof', 'polygon', 'scalar', 'simplify2', 

430 'tienstra', 'toUtm', 'triAngle4', 

431 'unsign0', 'unStr', 'utmZoneBand2'), 

432 deprecated_nvector=_i('LatLonNvectorBase', 'Nvector', 'sumOf', 'NorthPole', 'SouthPole'),) 

433 

434 

435class _ALL_MODS(_internals._ALL_MODS_Base): 

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

437 ''' 

438 def __getattr__(self, name): 

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

440 

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

442 

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

444 

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

446 ''' 

447 try: 

448 v = _lazy_dict[name] # package.__dict__ 

449 except KeyError: 

450 v = _lazy_module(name) # package.__getattr__ 

451 if _tailof(_dunder_nameof(v)) != name: 

452 try: 

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

454 except AttributeError: 

455 pass # XXX LazyAttributeError? 

456 return v 

457 

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

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

460 

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

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

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

464 

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

466 

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

468 

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

470 ''' 

471 v = self.getmodule(name) 

472 if attr_dflt: 

473 v = getattr(v, *attr_dflt) 

474 return v 

475 

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

477 '''Get a C{pygeodesy} module. 

478 

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

480 

481 @return: The C{pygeodesy} module. 

482 

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

484 ''' 

485 if _headof(name) != parent: 

486 name = _DOT_(parent, name) 

487 try: 

488 return _sys.modules[name] 

489 except KeyError: 

490 return import_module(name, parent) 

491 

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

493 '''Yield the modules imported so far. 

494 ''' 

495 _hd = _headof 

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

497 if _hd(n) == _pygeodesy_: 

498 yield n, m 

499 

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

501 

502__all__ = _ALL_LAZY.lazily 

503__version__ = '24.05.15' 

504 

505 

506def _ALL_OTHER(*objs): 

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

508 ''' 

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

510 n = _tailof(_dunder_nameof(o)) 

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

512 return getattr(_interns, i, n) 

513 

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

515 

516 

517if _FOR_DOCS: 

518 _ALL_DOCS = _ALL_OTHER 

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

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

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

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

523 # too much internal documentation. 

524else: 

525 def _ALL_DOCS(*unused): 

526 return () 

527 

528 

529def _all_deprecates(): 

530 '''(INTERNAL) Build C{dict} of all deprecated imports. 

531 ''' 

532 _D = _ALL_DEPRECATES 

533 if not _D: 

534 _ALL_DEPRECATED.fill_D(_D, _all_deprecates) # see _all_imports() 

535 return _D 

536 

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

538 

539 

540def _all_imports(): 

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

542 ''' 

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

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

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

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

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

548 _D = _ALL_IMPORTS 

549 if not _D: 

550 _ALL_LAZY.fill_D(_D, _all_imports) # see _all_deprecates() 

551 return _D 

552 

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

554 

555 

556def _all_missing2(_all_): 

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

558 ''' 

559 def _diff(one, two): 

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

561 

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

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

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

565 (_DOT_(_pygeodesy_, _dunder_all_), _diff(_alzy.keys(), _all_))) 

566 

567 

568def _attrof(attr_as): # .testDeprecated 

569 a_, _, as_ = attr_as.partition(__as__) 

570 return as_ or a_.rstrip(_DOT_) 

571 

572 

573# def _lazy_attributes(_dunder_name_): 

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

575# on lazily imported modules and sub-modules. 

576# ''' 

577# if _unlazy: 

578# raise AssertionError(_COMMASPACE_(_dunder_name_, _not_(_DEPRECATED_))) 

579# 

580# def _getattr(attr, *dflt): 

581# try: # a module name 

582# return _ALL_MODS.getmodule(attr) 

583# except (AttributeError, ImportError): 

584# return _ALL_MODS.getattr(_dunder_name_, attr, *dflt) 

585# 

586# return _getattr 

587 

588 

589_lazy_dict = {} # PYCHOK overwritten by _lazy_import2 

590 

591 

592def _lazy_import2(pack): # MCCABE 14 

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

594 

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

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

597 

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

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

600 C{package.__getattr__}. 

601 

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

603 invalid or does not exist. 

604 

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

606 an import failed. 

607 

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

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

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

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

612 causing a C{ModuleNotFoundError}. 

613 

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

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

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

617 ''' 

618 if pack != _pygeodesy_ or _unlazy: # Python 3.7+ 

619 t = _no_(_DOT_(pack, _dunder_nameof(_lazy_import2))) # PYCHOK no cover 

620 raise LazyImportError(t, txt=_Pythonarchine(sep=_SPACE_)) 

621 

622 package, parent = _lazy_init2(pack) # _pygeodesy_ 

623 

624 subpacks = set((parent, NN) + tuple( 

625 _DOT_(parent, s) for s in _sub_packages)) 

626 MISSING = object() # DON'T interns.MISSING! 

627 imports = _all_imports() 

628 deprecates = _all_deprecates() 

629 

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

631 # only called once for each undefined pygeodesy attribute 

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

633 if mod: 

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

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

636 # note in the _lazy_import2.__doc__ above). 

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

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

639 else: # from mod import name 

640 attr = name 

641 try: 

642 t = _DOT_(pack, mod) 

643 v = import_module(t, parent) 

644 except ImportError: 

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

646 raise LazyImportError(_no_(_module_), txt=t) 

647 t = getattr(v, _dunder_package_, None) 

648 if t not in subpacks: # invalid module package 

649 raise LazyImportError(_DOT_(mod, _dunder_package_), t) 

650 if attr: # get the attribute 

651 v = getattr(v, attr, MISSING) 

652 if v is MISSING: # PYCHOK no cover 

653 t = _DOT_(mod, attr) 

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

655 raise LazyAttributeError(_no_(_attribute_), txt=t) 

656 

657 elif name in (_dunder_all_,): # XXX _dunder_dir_, _dunder_members_? 

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

659 else: # PYCHOK no cover 

660 t = _no_(_module_, _or_, _attribute_) 

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

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

663 

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

665 if isLazy > 1: 

666 t = NN(_lazily_imported__, _DOT_(parent, name)) 

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

668 t = NN(t, _from_DOT__, mod) 

669 if isLazy > 2: 

670 try: # see C{_caller3} 

671 _, f, s = _caller3(2) 

672 t = _SPACE_(t, _by_, f, _line_, s) 

673 except ValueError: 

674 pass 

675 printf(t) # XXX print 

676 

677 return v # __getattr__ 

678 

679 global _lazy_dict, _lazy_module 

680 _lazy_dict = package.__dict__ 

681 _lazy_module = __getattr__ 

682 

683 return package, __getattr__ # _lazy_import2 

684 

685 

686# def _lazy_import_all(_dunder_name_): 

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

688# of all items, see .deprecated.__init__ 

689# ''' 

690# if _unlazy: 

691# raise AssertionError(_COMMASPACE_(_dunder_name_, _not_(_DEPRECATED_))) 

692# 

693# _getattr = _lazy_attributes(_dunder_name_) # __name__.__getattr__ 

694# _import_start = _lazy_import_star(_dunder_name_, ALL_=_ALL_IMPORTS) 

695# 

696# def _import_all(attr, *dflt): 

697# return _import_star(_dunder_name_) if attr == _dunder_all_ else \ 

698# _getattr(attr, *dflt) 

699# 

700# return _import_all 

701 

702 

703def _lazy_import_as(_dunder_name_): 

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

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

706 lazily exported by C{__name__}. 

707 ''' 

708 if _unlazy: 

709 return None 

710 

711 def _import_as(mod): 

712 try: 

713 return _ALL_MODS.getmodule(_DOT_(_dunder_name_, mod)) 

714 except ImportError: 

715 return _lazy_module(mod) 

716 

717 return _import_as 

718 

719 

720# def _lazy_import_star(_dunder_name_, ALL_=_ALL_DEPRECATES): 

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

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

723# ''' 

724# if _unlazy: 

725# raise AssertionError(_COMMASPACE_(_dunder_name_, _not_(_DEPRECATED_))) 

726# 

727# def _import_star(_into_): 

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

729# ''' 

730# d = dict() 

731# nm = _tailof(_dunder_name_) 

732# _g = _ALL_MODS.getattr # pygeodesy.__getattr__ 

733# _h = _headof 

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

735# if _h(m) == nm: 

736# try: 

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

738# except (AttributeError, ImportError): 

739# pass 

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

741# return d.keys() # imported names 

742# 

743# return _import_star 

744 

745 

746def _lazy_init2(pack): 

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

748 

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

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

751 

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

753 for easy reference within itself and its name aka the 

754 C{parent}, same as B{C{pack}}. 

755 

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

757 an import failed or the package name is 

758 invalid or does not exist. 

759 

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

761 ''' 

762 global isLazy, _unLazy0 

763 

764 z = _getenv(_PYGEODESY_LAZY_IMPORT_, None) 

765 if z is None: # _PYGEODESY_LAZY_IMPORT_ not set 

766 isLazy = 1 # ... but only by default on 3.7 

767 else: 

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

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

770 

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

772 

773 if isLazy < 1: # not enabled 

774 raise LazyImportError(_PYGEODESY_LAZY_IMPORT_, repr(z), txt_not_=_enabled_) 

775 if _getenv('PYTHONVERBOSE', None): # PYCHOK no cover 

776 isLazy += 1 

777 

778 try: # to initialize in Python 3+ 

779 package = import_module(pack) 

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

781 if parent != pack: # assert 

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

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

784 

785 except (AttributeError, ImportError) as x: 

786 isLazy = False # failed 

787 raise LazyImportError(_lazy_init2.__name__, pack, cause=x) 

788 

789 return package, parent 

790 

791 

792def _lazy_module(name): # overwritten by _lazy_import2 

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

794 ''' 

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

796 m = _ALL_MODS.getmodule(name) 

797 except (AttributeError, ImportError) as x: 

798 raise LazyImportError(name, cause=x) 

799 _lazy_dict[name] = m # cache 

800 return m 

801 

802 

803# def _lazy_subs(_dunder_name_, force=_FOR_DOCS, over=False): 

804# '''(INTERNAL) Return the names of a package's sub-packages and 

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

806# ''' 

807# sm = dict() 

808# if force and not _dunder_ismain(_dunder_name_): 

809# nm = _tailof(_dunder_name_) 

810# _a = _ALL_MODS.getattr 

811# _m = _ALL_MODS.getmodule 

812# d = _a(_dunder_name_, _dunder_dict_, {}) 

813# for n in _a(_dunder_name_, _dunder_all_, ()): 

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

815# m = _a(_dunder_name_, n).__module__ 

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

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

818# # like import m as s 

819# m = _m(m) 

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

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

822# pass 

823# d.update(sm) 

824# 

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

826 

827 

828# del _i, _i0 

829 

830if _dunder_ismain(__name__): # PYCHOK no cover 

831 

832 from timeit import timeit 

833 

834 def t1(): 

835 from pygeodesy.trf import RefFrame 

836 return RefFrame 

837 

838 def t2(): 

839 return _ALL_MODS.trf.RefFrame 

840 

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

842 

843 t1 = timeit(t1, number=1000000) 

844 t2 = timeit(t2, number=1000000) 

845 v = _SPACE_.join(_Pythonarchine() + _osversion2()) 

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

847 

848# del t1, t2, timeit, v 

849 

850# python3.12 -W ignore -m pygeodesy.lazily 

851# 0.145177 import vs 0.075402 _ALL_MODS: 1.93X, Python 3.12.3 64bit arm64 macOS 14.4.1 

852 

853# python3.11 -W ignore -m pygeodesy.lazily 

854# 0.381723 import vs 0.251589 _ALL_MODS: 1.52X, Python 3.11.5 64bit arm64 macOS 14.4.1 

855 

856# python3.10 -W ignore -m pygeodesy.lazily 

857# 0.378293 import vs 0.266507 _ALL_MODS: 1.42X, Python 3.10.8 64bit arm64 macOS 14.4.1 

858 

859# python2 -m pygeodesy.lazily 

860# 1.213805 import vs 0.474075 _ALL_MODS: 2.56X, Python 2.7.18 64bit arm64_x86_64 macOS 10.16 

861 

862# **) MIT License 

863# 

864# Copyright (C) 2018-2024 -- mrJean1 at Gmail -- All Rights Reserved. 

865# 

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

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

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

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

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

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

872# 

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

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

875# 

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

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

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

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

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

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

882# OTHER DEALINGS IN THE SOFTWARE.