1

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

3

4 u'''Lazily import C{pygeodesy} modules and attributes, based on

5 U{lazy_import<https://modutil.ReadTheDocs.io/en/latest/#lazy_import>}

6 from I{Brett Cannon}'s U{modutil<https://PyPI.org/project/modutil>}.

7

8 C{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

10 default} in U{PyGeodesy 18.11.10<https://PyPI.org/project/PyGeodesy>}

11 I{and newer}.

12

13 To I{enable} C{lazy import}, set C{env} variable C{PYGEODESY_LAZY_IMPORT}

14 to C{1}, C{2}, C{3} or higher prior to C{import pygeodesy}. To I{disable}

15 C{lazy import}, set C{env} variable C{PYGEODESY_LAZY_IMPORT} to C{0} or

16 an empty string. Use C{2} or higher to print a message for each lazily

17 imported module and attribute, similar to C{env} variable C{PYTHONVERBOSE}

18 showing imports. Using C{3} or higher also shows the importing file name

19 and 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

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

31 _isfrozen # DON'T _lazy_import2

32# from pygeodesy.errors import _error_init, _xkwds_item2 # _ALL_MODS

33 from pygeodesy.internals import _caller3, _DUNDER_nameof, _getPYGEODESY, _headof, \

34 _is_DUNDER_main, printf, _tailof, _versions

35 from pygeodesy.interns import NN, _attribute_, _by_, _COLONSPACE_, _COMMASPACE_, \

36 _doesn_t_exist_, _DOT_, _DUNDER_all_, _EQUALSPACED_, \

37 _from_, _HASH_, _immutable_, _line_, _module_, _no_, \

38 _not_, _or_, _pygeodesy_abspath_, _pygeodesy_, _sys, \

39 _SUB_PACKAGES, _UNDER_, _version_, _intern # function

40 try:

41 from importlib import import_module

42 except ImportError as x: # Python 2.6-

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

44# import sys as _sys # from .interns

45

46_a0 = () # PYCHOK empty tuple

47_asSPACED_ = ' as '

48_FOR_DOCS = _getPYGEODESY('FOR_DOCS') # for epydoc ...

49_init__all__ = _FOR_DOCS or _getPYGEODESY('_init__all__', _DUNDER_all_) == _DUNDER_all_ # PYCHOK exported

50_lazily_ = 'lazily'

51_PYTHON_X_DEV = getattr(_sys.flags, 'dev_mode', False) # PYCHOK Python 3.2+

52_unlazy = _unLazy0 = _isfrozen or _internals._MODS.sys_version_info2 < (3, 7) # PYCHOK mod.__getattr__ 3.7+

53_WARNINGS_X_DEV = _getPYGEODESY('WARNINGS') and (_PYTHON_X_DEV or bool(_sys.warnoptions)) # PYCHOK .props

54

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

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

57

58

59 class LazyAttributeError(AttributeError):

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

61 '''

62 def __init__(self, *args, **kwds):

63 _ALL_MODS.errors._error_init(AttributeError, self, args, **kwds)

64

65

66 class LazyImportError(ImportError):

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

68 '''

69 def __init__(self, *args, **kwds):

70 _ALL_MODS.errors._error_init(ImportError, self, args, **kwds)

71

72

73 class _Dict(dict):

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

75 '''

76 _name = NN

77

78 def __getattr__(self, attr):

79 try:

80 return self[attr]

81 except KeyError:

82 return dict.__getattr__(self, attr)

83

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

85# if attr in self:

86# self[attr] = value

87# else:

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

89

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

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

92

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

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

95 '''

96 if name in self: # PYCHOK no cover

97 sub = self[name] # duplicate OK

98 if sub != mod_ and sub not in subs:

99 t = _DOT_(self._name, name)

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

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

102 raise AssertionError(t)

103 else:

104 self[name] = mod_

105

106 def _NAME(self, which):

107 self._name = _intern(_DUNDER_nameof(which).upper())

108

109

110 class _NamedEnum_RO(dict):

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

112 '''

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

114

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

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

117

118 def __getattr__(self, attr):

119 try:

120 return self[attr]

121 except KeyError:

122 t = self._DOT_(attr)

123 raise LazyAttributeError(t, txt=_doesn_t_exist_)

124

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

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

127 raise LazyAttributeError(_immutable_, txt=t)

128

129 def enums(self):

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

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

132 n = m.replace(_UNDER_, _DOT_)

133 if n != m:

134 if m.startswith(_UNDER_):

135 continue # skip _name= ...

136 u = m.rstrip(_UNDER_)

137 if u != m:

138 u = len(u)

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

140 yield n, t

141

142 def fill_D(self, _D, which):

143 # Fill C{_Dict _D}.

144 _D._NAME(which)

145 _a = _D.add

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

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

148 for a in t:

149 a, _, as_ = a.partition(_asSPACED_)

150 if as_: # import attr as attr_

151 _a(as_, _DOT_(m, a, NN), *_SUB_PACKAGES)

152 else:

153 _a(a, m)

154 return _D

155

156

157 def _a(*names):

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

159 '''

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

161

162

163 def _ALL_ATTRS(*attrs):

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

165 '''

166 t = ()

167 for attr in attrs:

168 t += tuple(map(_getattras, attr))

169 return t

170

171

172_ALL_INIT = _a(_pygeodesy_abspath_, _version_)

173

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

175_ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',

176 albers=_a('AlbersEqualArea', 'AlbersEqualArea2', 'AlbersEqualArea4',

177 'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth',

178 'AlbersError', 'Albers7Tuple'),

179 auxilats=_a(), # module only

180 azimuthal=_a('AzimuthalError', 'Azimuthal7Tuple',

181 'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney',

182 'Gnomonic', 'GnomonicExact', 'GnomonicGeodSolve', 'GnomonicKarney',

183 'LambertEqualArea', 'Orthographic', 'Stereographic',

184 'equidistant', 'gnomonic'),

185 basics=_a('clips', 'copysign0', 'copytype', 'halfs2',

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

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

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

189 'issubclassof', 'itemsorted',

190 'len2', 'map1', 'map2', 'neg', 'neg_',

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

192 booleans=_a('BooleanFHP', 'BooleanGH', 'LatLonFHP', 'LatLonGH',

193 'isBoolean'),

194 cartesianBase=_a('RadiusThetaPhi3Tuple', 'rtp2xyz', 'rtp2xyz_', 'xyz2rtp', 'xyz2rtp_'),

195 clipy=_a('ClipCS4Tuple', 'ClipFHP4Tuple', 'ClipGH4Tuple', 'ClipLB6Tuple', 'ClipSH3Tuple',

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

197 css=_a('CassiniSoldner', 'Css', 'CSSError', 'toCss',

198 'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'),

199 constants=_a('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS_2',

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

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

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

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

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

205 'remainder'),

206 datums=_a('Datum', 'Datums', 'Transform', 'Transforms'),

207# deprecated=_a(), # module only

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

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

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

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

212 'bearingDMS', 'clipDegrees', 'clipRadians', 'compassDMS', 'compassPoint',

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

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

215 ecef=_a('EcefError', 'EcefFarrell21', 'EcefFarrell22', 'EcefKarney', 'EcefMatrix',

216 'EcefSudano', 'Ecef9Tuple', 'EcefVeness', 'EcefYou'),

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

218 'elevation2', 'geoidHeight2'),

219 ellipsoidalBase=_a(), # module only

220 ellipsoidalBaseDI=_a(), # module only

221 ellipsoidalExact=_a(), # module only

222 ellipsoidalGeodSolve=_a(), # module only

223 ellipsoidalKarney=_a(), # module only

224 ellipsoidalNvector=_a(), # module only

225 ellipsoidalVincenty=_a('VincentyError',), # nothing else

226 ellipsoids=_a('a_f2Tuple', 'Circle4Tuple', 'Curvature2Tuple',

227 'Ellipsoid', 'Ellipsoid2', 'Ellipsoids',

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

229 'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a',

230 'e2f', 'e22f',

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

232 elliptic=_a('Elliptic', 'EllipticError', 'Elliptic3Tuple'),

233 epsg=_a('Epsg', 'EPSGError'),

234 errors=_a('AuxError', 'ClipError', 'CrossError', 'GeodesicError', 'IntersectionError',

235 'NumPyError', 'LenError', 'LimitError', 'MGRSError',

236 'ParseError', 'PointsError', 'RangeError', 'RhumbError',

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

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

239 etm=_a('Etm', 'ETMError', 'ExactTransverseMercator',

240 'parseETM5', 'toEtm8'),

241 fmath=_a('Fdot', 'Fhorner', 'Fhypot', 'Fpolynomial', 'Fpowers', 'Fcbrt', 'Froot', 'Fsqrt',

242 'bqrt', 'cbrt', 'cbrt2', 'euclid', 'euclid_',

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

244 'fdot', 'fdot_', 'fdot3', 'fma', 'fmean', 'fmean_', 'fhorner', 'fidw', 'f2mul_',

245 'fpolynomial', 'fpowers', 'fprod', 'frandoms', 'frange', 'freduce', 'fremainder',

246 'hypot', 'hypot_', 'hypot1', 'hypot2', 'hypot2_',

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

248 formy=_a('Radical2Tuple',

249 'angle2chord', 'antipode', 'antipode_', 'bearing', 'bearing_', 'chord2angle',

250 'compassAngle', 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_',

251 'cosineAndoyerLambert', 'cosineAndoyerLambert_', 'cosineLaw', 'cosineLaw_',

252 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',

253 'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_',

254 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_',

255 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_',

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

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

258 'normal', 'normal_', 'opposing', 'opposing_', 'radical2',

259 'thomas', 'thomas_', 'vincentys', 'vincentys_'),

260 frechet=_a('Frechet', 'FrechetDegrees', 'FrechetError', 'FrechetRadians',

261 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert',

262 'FrechetCosineLaw', 'FrechetDistanceTo', 'FrechetEquirectangular',

263 'FrechetEuclidean', 'FrechetExact', 'FrechetFlatLocal', 'FrechetFlatPolar',

264 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney', 'FrechetThomas',

265 'FrechetVincentys', 'Frechet6Tuple',

266 'frechet_'),

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

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

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

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

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

272 'Intersector', 'Intersector5Tuple', 'Middle5Tuple', 'XDict'),

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

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

275 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'),

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

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

278 geoids=_a('GeoidError', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights',

279 'PGMError', 'GeoidHeight5Tuple'),

280 hausdorff=_a('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians',

281 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert',

282 'HausdorffCosineLaw', 'HausdorffDistanceTo', 'HausdorffEquirectangular',

283 'HausdorffEuclidean', 'HausdorffExact', 'HausdorffFlatLocal', 'HausdorffFlatPolar',

284 'HausdorffHaversine', 'HausdorffHubeny', 'HausdorffKarney', 'HausdorffThomas',

285 'HausdorffVincentys', 'Hausdorff6Tuple',

286 'hausdorff_', 'randomrangenerator'),

287 heights=_a('HeightCubic', 'HeightError',

288 'HeightIDWcosineAndoyerLambert', 'HeightIDWcosineForsytheAndoyerLambert',

289 'HeightIDWcosineLaw', 'HeightIDWdistanceTo', 'HeightIDWequirectangular',

290 'HeightIDWeuclidean', 'HeightIDWexact', 'HeightIDWflatLocal', 'HeightIDWflatPolar',

291 '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', 'Rhumb8Tuple'),

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

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('BetaOmega2Tuple', 'BetaOmega3Tuple', 'Jacobi2Tuple',

353 'JacobiConformal', 'JacobiConformalSpherical',

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

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

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

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

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

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

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

361 'Radius_', 'Scalar', 'Scalar_', 'Zone'),

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

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

364 utily=_a('acos1', 'acre2ha', 'acre2m2', 'asin1', 'atan1', 'atan1d', 'atan2b', 'atan2d',

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

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

367# 'degrees2grades as degrees2gons',

368 'fathom2m', 'ft2m', 'furlong2m',

369 'grades', 'grades400', 'grades2degrees', 'grades2radians',

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

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

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

373 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m',

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

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

376 'unroll180', 'unrollPI',

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

378 'yard2m'),

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

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

381 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'),

382 utmupsBase=_a(), # module only

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

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

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

386 'trilaterate3d2'),

387 vector3dBase=_a(), # module only

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

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

390

391_ALL_DEPRECATED = _NamedEnum_RO(_name='_ALL_DEPRECATED',

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

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

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

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

396 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'Helmert7Tuple',

397 'Lam_', 'LatLonExact4Tuple', 'NearestOn4Tuple', 'Ned3Tuple',

398 'Phi_', 'RefFrameError', 'Rhumb7Tuple', 'RhumbOrder2Tuple',

399 'Transform7Tuple', 'TriAngle4Tuple', 'UtmUps4Tuple', 'XDist'),

400 deprecated_consterns=_a('EPS1_2', 'MANTIS', 'OK'),

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

402 'Datums', 'Ellipsoids', 'Transforms',

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

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

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

406 'decodeEPSG2', 'encodeEPSG', 'enStr2', 'equirectangular_', 'equirectangular3',

407 'excessAbc', 'excessGirard', 'excessLHuilier',

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

409 'hypot3', 'inStr', 'isenclosedby', 'istuplist',

410 'joined', 'joined_', 'nearestOn3', 'nearestOn4',

411 'parseUTM', 'perimeterof', 'polygon',

412 'scalar', 'simplify2', 'simplifyRDPm', 'simplifyVWm',

413 'tienstra', 'toUtm', 'triAngle4',

414 'unsign0', 'unStr', 'utmZoneBand2'),

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

416

417

418 class _ALL_MODS(_internals._MODS_Base):

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

420 '''

421 def __getattr__(self, name):

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

423

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

425

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

427

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

429 '''

430 try:

431 v = _lazy_dict[name] # package.__dict__

432 except KeyError:

433 v = _lazy_module(name) # package.__getattr__

434 if _tailof(_DUNDER_nameof(v)) != name:

435 try:

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

437 except AttributeError:

438 pass # XXX LazyAttributeError?

439 return v

440

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

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

443

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

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

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

447

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

449

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

451

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

453 '''

454 v = self.getmodule(name)

455 if attr_dflt:

456 v = getattr(v, *attr_dflt)

457 return v

458

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

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

461

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

463

464 @return: The C{pygeodesy} module.

465

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

467 '''

468 if _headof(name) != parent and not _is_DUNDER_main(name):

469 name = _DOT_(parent, name)

470 try:

471 return _sys.modules[name]

472 except KeyError:

473 return _getmodule(name, parent)

474

475 def imported(self, name):

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

477 '''

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

479

480 def into(self, **mod_DUNDER_name):

481 '''Lazily import module C{mod} into module C{DUNDER_name}

482 and set C{DUNDER_name._mod} to module C{mod}, I{once}.

483 '''

484 class _Into(object):

485

486 def __getattr__(unused, name):

487 mod, dun = self.errors._xkwds_item2(mod_DUNDER_name)

488 _mod = _UNDER_(NN, mod)

489 d = self.getmodule(dun) # '__main__' OK

490 i = _getattribute(d, _mod, dun)

491 assert isinstance(i, _Into)

492 m = self.getmodule(mod)

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

494 return getattr(m, name)

495

496 return _Into()

497

498# @_Property_RO

499# def _isBoolean(self):

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

501# '''

502# return self.booleans.isBoolean

503

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

505 '''Yield the modules imported so far.

506 '''

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

508 if _headof(n) == _pygeodesy_:

509 yield n, m

510

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

512

513__all__ = _ALL_LAZY.lazily

514__version__ = '24.11.28'

515

516

517 def _ALL_OTHER(*objs):

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

519 '''

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

521 n = _tailof(_DUNDER_nameof(o))

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

523 return getattr(_interns, i, n)

524

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

526

527

528 if _FOR_DOCS: # PYCHOK no cover

529 _ALL_DOCS = _ALL_OTHER

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

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

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

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

534 # too much internal documentation.

535 else:

536 def _ALL_DOCS(*unused):

537 return ()

538

539

540 def _all_deprecates():

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

542 '''

543 _D = _ALL_DEPRECATES

544 if not _D:

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

546 return _D

547

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

549

550

551 def _all_imports():

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

553 '''

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

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

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

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

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

559 _D = _ALL_IMPORTS

560 if not _D:

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

562 return _D

563

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

565

566

567 def _all_missing2(_all_):

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

569 '''

570 def _diff(one, two):

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

572

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

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

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

576 (_DOT_(_pygeodesy_, _DUNDER_all_), _diff(_alzy.keys(), _all_)))

577

578

579 def _getattras(attr_as): # test/testDeprecated

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

581 '''

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

583 return as_ or a_.rstrip(_DOT_)

584

585

586 def _getattribute(m, name, mod=_pygeodesy_):

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

588 '''

589 try:

590 return getattr(m, name)

591 except AttributeError:

592 name = _DOT_(mod, name)

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

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

595

596

597 def _getmodule(name, *parent):

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

599 '''

600 try:

601 return import_module(name, parent)

602 except ImportError:

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

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

605

606

607# def _lazy_attributes(DUNDER_name):

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

609# on lazily imported modules and sub-modules.

610# '''

611# if _unlazy:

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

613#

614# def _getattr(attr, *dflt):

615# try: # a module name

616# return _ALL_MODS.getmodule(attr)

617# except (AttributeError, ImportError):

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

619#

620# return _getattr

621

622

623_lazy_dict = {} # PYCHOK overwritten by _lazy_import2

624

625

626 def _lazy_import2(pack): # MCCABE 14

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

628

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

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

631

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

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

634 C{package.__getattr__}.

635

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

637 invalid or does not exist.

638

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

640 an import failed.

641

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

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

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

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

646 causing a C{ModuleNotFoundError}.

647

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

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

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

651 '''

652 _DOT_ = _interns._DOT_

653 _SPACE_ = _interns._SPACE_

654

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

656 t = _DOT_(pack, _DUNDER_nameof(_lazy_import2))

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

658

659 package, parent = _lazy_init2(pack) # _pygeodesy_

660

661 _DUNDER_package_ = '__package__'

662 _lazily_imported_ = _SPACE_(_HASH_, _lazily_, 'imported', parent)

663

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

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

666 imports = _all_imports()

667 deprecates = _all_deprecates()

668

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

670 # only called once for each undefined pygeodesy attribute

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

672 if mod:

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

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

675 # note in the _lazy_import2.__doc__ above).

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

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

678 else: # from mod import name

679 attr = name

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

681 t = getattr(v, _DUNDER_package_, None)

682 if t not in sub_packages: # invalid module package

683 raise LazyImportError(_DOT_(mod, _DUNDER_package_), t)

684 if attr: # get mod.attr

685 v = _getattribute(v, attr, mod)

686

687 elif name in (_DUNDER_all_,): # XXX _DUNDER_dir_, _DUNDER_members_?

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

689 else: # PYCHOK no cover

690 t = _no_(_module_, _or_, _attribute_)

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

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

693

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

695 if isLazy > 1:

696 t = _DOT_(_lazily_imported_, name)

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

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

699 if isLazy > 2:

700 try: # see C{_caller3}

701 _, f, s = _caller3(2)

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

703 except ValueError:

704 pass

705 printf(t) # XXX print

706

707 return v # __getattr__

708

709 global _lazy_dict, _lazy_module

710 _lazy_dict = package.__dict__

711 _lazy_module = __getattr__

712

713 return package, __getattr__ # _lazy_import2

714

715

716# def _lazy_import_all(DUNDER_name):

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

718# of all items, see .deprecated.__init__

719# '''

720# if _unlazy:

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

722#

723# _getattr = _lazy_attributes(DUNDER_name) # __name__.__getattr__

724# _import_start = _lazy_import_star(DUNDER_name, ALL_=_ALL_IMPORTS)

725#

726# def _import_all(attr, *dflt):

727# return _import_star(DUNDER_name) if attr == _DUNDER_all_ else \

728# _getattr(attr, *dflt)

729#

730# return _import_all

731

732

733 def _lazy_import_as(DUNDER_name):

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

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

736 lazily exported by C{__name__}.

737 '''

738 if _unlazy:

739 return None

740

741 def _import_as(mod):

742 try:

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

744 except ImportError:

745 return _lazy_module(mod)

746

747 return _import_as

748

749

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

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

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

753# '''

754# if _unlazy:

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

756#

757# def _import_star(_into_):

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

759# '''

760# d = dict()

761# nm = _tailof(DUNDER_name)

762# _g = _ALL_MODS.getattr # pygeodesy.__getattr__

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

764# if _headof(m) == nm:

765# try:

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

767# except (AttributeError, ImportError):

768# pass

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

770# return d.keys() # imported names

771#

772# return _import_star

773

774

775 def _lazy_init2(pack):

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

777

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

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

780

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

782 for easy reference within itself and its name aka the

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

784

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

786 an import failed or the package name is

787 invalid or does not exist.

788

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

790 '''

791 global isLazy, _unLazy0

792

793 z = _getPYGEODESY('LAZY_IMPORT', None)

794 if z is None: # not set, but ...

795 isLazy = 1 # ... default on 3.7+

796 else:

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

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

799

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

801

802 if isLazy < 1: # invalid, not enabled

803 e = _internals._PYGEODESY('LAZY_IMPORT')

804 raise LazyImportError(e, repr(z), txt_not_='enabled')

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

806 isLazy += 1

807

808 try: # to initialize in Python 3+

809 package = import_module(pack)

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

811 if parent != pack: # assert

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

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

814

815 except (AttributeError, ImportError) as x:

816 isLazy = False # failed

817 z = _DUNDER_nameof(_lazy_init2)

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

819

820 return package, parent

821

822

823 def _lazy_module(name): # overwritten by _lazy_import2

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

825 '''

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

827 m = _ALL_MODS.getmodule(name)

828 except (AttributeError, ImportError) as x:

829 raise LazyImportError(name, cause=x)

830 _lazy_dict[name] = m # cache

831 return m

832

833

834# def _lazy_subs(DUNDER_name, force=_FOR_DOCS, over=False):

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

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

837# '''

838# sm = dict()

839# if force and not _is_DUNDER_main(DUNDER_name):

840# nm = _tailof(DUNDER_name)

841# _a = _ALL_MODS.getattr

842# _m = _ALL_MODS.getmodule

843# d = _a(DUNDER_name, _DUNDER_dict_, {})

844# for n in _a(DUNDER_name, _DUNDER_all_, ()):

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

846# m = _a(DUNDER_name, n).__module__

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

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

849# # like import m as s

850# m = _m(m)

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

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

853# pass

854# d.update(sm)

855#

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

857

858# del _a, _a0

859

860 if _is_DUNDER_main(__name__): # PYCHOK no cover

861

862 def _main():

863 from timeit import timeit

864

865 def t1():

866 from pygeodesy.trf import RefFrame

867 return RefFrame

868

869 def t2():

870 return _ALL_MODS.trf.RefFrame

871

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

873

874 t1 = timeit(t1, number=1000000)

875 t2 = timeit(t2, number=1000000)

876 A = _DUNDER_nameof(_ALL_MODS.__class__)

877 v = _versions()

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

879

880 _main()

881

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

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

884

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

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

887

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

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

890

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

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

893

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

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

896

897# % python2 -m pygeodesy.lazily

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

899

900# **) MIT License

901#

902# Copyright (C) 2018-2025 -- mrJean1 at Gmail -- All Rights Reserved.

903#

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

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

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

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

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

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

910#

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

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

913#

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

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

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

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

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

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

920# OTHER DEALINGS IN THE SOFTWARE.