Coverage for pygeodesy/lazily.py: 93%

208 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2024-06-10 14:08 -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, _xkwds_item2 # _ALL_MODS 

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

34 _headof, _osversion2, printf, _Pythonarchine, \ 

35 _tailof # _Property_RO 

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

37 _doesn_t_exist_, _DOT_, _EQUALSPACED_, _from_, \ 

38 _HASH_, _immutable_, _line_, _module_, _no_, _not_, \ 

39 _or_, _pygeodesy_abspath_, _pygeodesy_, _SPACE_, \ 

40 _SUB_PACKAGES, _UNDER_, _version_, _sys, \ 

41 _intern # function 

42# from pygeodesy.streprs import unstr # _ALL_MODS 

43 

44try: 

45 from importlib import import_module 

46except ImportError as x: # Python 2.6- 

47 raise ImportError(_COLONSPACE_(_Pythonarchine(sep=_SPACE_), x)) 

48from os import getenv as _getenv 

49# import sys as _sys # from .interns 

50 

51__as__ = ' as ' 

52_dunder_all_ = '__all__' # in .__main__ 

53_enabled_ = 'enabled' 

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

55_i0 = () # PYCHOK empty tuple 

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

57_lazily_ = 'lazily' 

58_lazily_imported_ = _SPACE_(_HASH_, _lazily_, 'imported') 

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

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

61_PYGEODESY_LAZY_IMPORT_ = 'PYGEODESY_LAZY_IMPORT' 

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

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

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

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

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

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

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

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

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

71 

72 

73class LazyAttributeError(AttributeError): 

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

75 ''' 

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

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

78 

79 

80class LazyImportError(ImportError): 

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

82 ''' 

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

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

85 

86 

87class _Dict(dict): 

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

89 ''' 

90 _name = NN 

91 

92 def __getattr__(self, attr): 

93 try: 

94 return self[attr] 

95 except KeyError: 

96 return dict.__getattr__(self, attr) 

97 

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

99# if attr in self: 

100# self[attr] = value 

101# else: 

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

103 

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

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

106 

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

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

109 ''' 

110 if name in self: 

111 sub = self[name] # duplicate OK 

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

113 t = _DOT_(self._name, name) 

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

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

116 raise AssertionError(t) 

117 else: 

118 self[name] = mod_ 

119 

120 def _NAME(self, which): 

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

122 

123 

124class _NamedEnum_RO(dict): 

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

126 ''' 

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

128 

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

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

131 

132 def __getattr__(self, attr): 

133 try: 

134 return self[attr] 

135 except KeyError: 

136 t = self._DOT_(attr) 

137 raise LazyAttributeError(t, txt=_doesn_t_exist_) 

138 

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

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

141 raise LazyAttributeError(_immutable_, txt=t) 

142 

143 def enums(self): 

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

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

146 n = m.replace(_UNDER_, _DOT_) 

147 if n != m: 

148 if m.startswith(_UNDER_): 

149 continue # skip _name= ... 

150 u = m.rstrip(_UNDER_) 

151 if u != m: 

152 u = len(u) 

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

154 yield n, t 

155 

156 def fill_D(self, _D, which): 

157 # Fill C{_Dict _D}. 

158 _D._NAME(which) 

159 _a = _D.add 

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

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

162 for a in t: 

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

164 if as_: # import attr as attr_ 

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

166 else: 

167 _a(a, m) 

168 return _D 

169 

170 

171def _i(*names): 

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

173 ''' 

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

175 

176 

177def _ALL_ATTRS(*attrs): 

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

179 ''' 

180 t = () 

181 for attr in attrs: 

182 t += tuple(map(_getattrof, attr)) 

183 return t 

184 

185 

186_ALL_INIT = _i(_pygeodesy_abspath_, _version_) 

187 

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

189_ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY', 

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

191 'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth', 

192 'AlbersError', 'Albers7Tuple'), 

193 auxilats=_i(), # module only 

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

195 'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney', 

196 'Gnomonic', 'GnomonicExact', 'GnomonicGeodSolve', 'GnomonicKarney', 

197 'LambertEqualArea', 'Orthographic', 'Stereographic', 

198 'equidistant', 'gnomonic'), 

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

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

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

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

203 'issubclassof', 'itemsorted', 

204 'len2', 'map1', 'map2', 'neg', 'neg_', 

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

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

207 'isBoolean'), 

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

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

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

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

212 'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'), 

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

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

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

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

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

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

219 'remainder'), 

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

221# deprecated=_i(), # module only 

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

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

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

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

226 'bearingDMS', 'clipDegrees', 'clipRadians', 'compassDMS', 'compassPoint', 

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

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

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

230 'EcefSudano', 'Ecef9Tuple', 'EcefVeness', 'EcefYou'), 

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

232 'elevation2', 'geoidHeight2'), 

233 ellipsoidalBase=_i(), # module only 

234 ellipsoidalBaseDI=_i(), # module only 

235 ellipsoidalExact=_i(), # module only 

236 ellipsoidalGeodSolve=_i(), # module only 

237 ellipsoidalKarney=_i(), # module only 

238 ellipsoidalNvector=_i(), # module only 

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

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

241 'Ellipsoid', 'Ellipsoid2', 'Ellipsoids', 

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

243 'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a', 

244 'e2f', 'e22f', 

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

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

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

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

249 'NumPyError', 'LenError', 'LimitError', 'MGRSError', 

250 'ParseError', 'PointsError', 'RangeError', 'RhumbError', 

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

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

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

254 'parseETM5', 'toEtm8'), 

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

256 'bqrt', 'cbrt', 'cbrt2', 'euclid', 'euclid_', 

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

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

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

260 'hypot', 'hypot_', 'hypot1', 'hypot2', 'hypot2_', 

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

262 formy=_i('Radical2Tuple', 

263 'antipode', 'antipode_', 'bearing', 'bearing_', 

264 'compassAngle', 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_', 

265 'cosineAndoyerLambert', 'cosineAndoyerLambert_', 'cosineLaw', 'cosineLaw_', 

266 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_', 

267 'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_', 

268 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_', 

269 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_', 

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

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

272 'latlon2n_xyz', 'normal', 'normal_', 'n_xyz2latlon', 'n_xyz2philam', 

273 'opposing', 'opposing_', 'philam2n_xyz', 'radical2', 

274 'thomas', 'thomas_', 'vincentys', 'vincentys_'), 

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

276 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert', 

277 'FrechetCosineLaw', 'FrechetDistanceTo', 'FrechetEquirectangular', 

278 'FrechetEuclidean', 'FrechetExact', 'FrechetFlatLocal', 'FrechetFlatPolar', 

279 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney', 'FrechetThomas', 

280 'FrechetVincentys', 'Frechet6Tuple', 

281 'frechet_'), 

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

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

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

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

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

287 geodesicx=_i('gx', 'gxarea', 'gxbases', 'gxline', # modules 

288 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'), 

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

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

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

292 'PGMError', 'GeoidHeight5Tuple'), 

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

294 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert', 

295 'HausdorffCosineLaw', 'HausdorffDistanceTo', 'HausdorffEquirectangular', 

296 'HausdorffEuclidean', 'HausdorffExact', 'HausdorffFlatLocal', 'HausdorffFlatPolar', 

297 'HausdorffHaversine', 'HausdorffHubeny', 'HausdorffKarney', 'HausdorffThomas', 

298 'HausdorffVincentys', 'Hausdorff6Tuple', 

299 'hausdorff_', 'randomrangenerator'), 

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

301 'HeightIDWcosineAndoyerLambert', 'HeightIDWcosineForsytheAndoyerLambert', 

302 'HeightIDWcosineLaw', 'HeightIDWdistanceTo', 'HeightIDWequirectangular', 

303 'HeightIDWeuclidean', 'HeightIDWexact', 'HeightIDWflatLocal', 'HeightIDWflatPolar', 

304 'HeightIDWhaversine', 'HeightIDWhubeny', 'HeightIDWkarney', 'HeightIDWthomas', 

305 'HeightIDWvincentys', 'HeightLinear', 'HeightLSQBiSpline', 'HeightSmoothBiSpline'), 

306 internals=_internals.__all__, 

307 interns=_interns.__all__, 

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

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

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

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

312 latlonBase=_i(), # module only 

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

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

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

316 'LocalCartesian', 'LocalError', 'Ltp', 'tyr3d'), 

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

318 'ChLVEN2Tuple', 'ChLV9Tuple', 'ChLVYX2Tuple', 'ChLVyx2Tuple', 

319 'Enu', 'Enu4Tuple', 'Footprint5Tuple', 'Local9Tuple', 'Los', 

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

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

322 named=_i('ADict', 

323 'callername', 'classname', 'classnaming', 'modulename', 

324 'nameof', 'notImplemented', 'notOverloaded'), 

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

326 'Destination2Tuple', 'Destination3Tuple', 

327 'Distance2Tuple', 'Distance3Tuple', 'Distance4Tuple', 

328 'EasNor2Tuple', 'EasNor3Tuple', 'Forward4Tuple', 'Intersection3Tuple', 

329 'LatLon2Tuple', 'LatLon3Tuple', 'LatLon4Tuple', 

330 'LatLonDatum3Tuple', 'LatLonDatum5Tuple', 

331 'LatLonPrec3Tuple', 'LatLonPrec5Tuple', 

332 'NearestOn2Tuple', 'NearestOn3Tuple', 'NearestOn6Tuple', 'NearestOn8Tuple', 

333 'PhiLam2Tuple', 'PhiLam3Tuple', 'PhiLam4Tuple', 'Point3Tuple', 'Points2Tuple', 

334 'Reverse4Tuple', 'Triangle7Tuple', 'Triangle8Tuple', 'Trilaterate5Tuple', 

335 'UtmUps2Tuple', 'UtmUps5Tuple', 'UtmUps8Tuple', 'UtmUpsLatLon5Tuple', 

336 'Vector2Tuple', 'Vector3Tuple', 'Vector4Tuple'), 

337 nvectorBase=_i('NorthPole', 'SouthPole'), 

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

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

340 'areaOf', 'boundsOf', 'centroidOf', 'fractional', 

341 'isclockwise', 'isconvex', 'isconvex_', 'isenclosedBy', 'ispolar', 

342 'luneOf', 'nearestOn5', 'perimeterOf', 'quadOf'), 

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

344 'deprecated_class', 'deprecated_function', 'deprecated_method', 

345 'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'), 

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

347 'TriAngle5Tuple', 'TriSide2Tuple', 'TriSide4Tuple', 

348 'cassini', 'collins5', 'pierlot', 'pierlotx', 'tienstra7', 

349 'snellius3', 'wildberger3', 

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

351 rhumb=_i(), # module only 

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

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

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

355 sphericalBase=_i(), # module only 

356 sphericalNvector=_i(), # module only 

357 sphericalTrigonometry=_i(), # module only 

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

359 solveBase=_i(), # module only 

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

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

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

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

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

365 'JacobiConformal', 'JacobiConformalSpherical', 

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

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

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

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

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

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

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

373 'Radius_', 'Scalar', 'Scalar_', 'Zone'), 

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

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

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

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

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

379# 'degrees2grades as degrees2gons', 

380 'fathom2m', 'ft2m', 'furlong2m', 

381 'grades', 'grades400', 'grades2degrees', 'grades2radians', 

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

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

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

385 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m', 

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

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

388 'unroll180', 'unrollPI', 

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

390 'yard2m'), 

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

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

393 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'), 

394 utmupsBase=_i(), # module only 

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

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

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

398 'trilaterate2d2', 'trilaterate3d2'), 

399 vector3dBase=_i(), # module only 

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

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

402 

403_ALL_DEPRECATED = _NamedEnum_RO(_name='_ALL_DEPRECATED', 

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

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

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

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

408 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'Helmert7Tuple', 

409 'LatLonExact4Tuple', 'NearestOn4Tuple', 'Ned3Tuple', 

410 'RefFrameError', 'Rhumb7Tuple', 'RhumbOrder2Tuple', 

411 'Transform7Tuple', 'TriAngle4Tuple', 'UtmUps4Tuple'), 

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

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

414 'Datums', 'Ellipsoids', 'Transforms', 

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

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

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

418 'decodeEPSG2', 'encodeEPSG', 'enStr2', 'equirectangular_', 'equirectangular3', 

419 'excessAbc', 'excessGirard', 'excessLHuilier', 

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

421 'inStr', 'isenclosedby', 'istuplist', 

422 'joined', 'joined_', 'nearestOn3', 'nearestOn4', 

423 'parseUTM', 'perimeterof', 'polygon', 'scalar', 'simplify2', 

424 'tienstra', 'toUtm', 'triAngle4', 

425 'unsign0', 'unStr', 'utmZoneBand2'), 

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

427 

428 

429class _ALL_MODS(_internals._MODS_Base): 

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

431 ''' 

432 def __getattr__(self, name): 

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

434 

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

436 

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

438 

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

440 ''' 

441 try: 

442 v = _lazy_dict[name] # package.__dict__ 

443 except KeyError: 

444 v = _lazy_module(name) # package.__getattr__ 

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

446 try: 

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

448 except AttributeError: 

449 pass # XXX LazyAttributeError? 

450 return v 

451 

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

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

454 

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

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

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

458 

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

460 

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

462 

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

464 ''' 

465 v = self.getmodule(name) 

466 if attr_dflt: 

467 v = getattr(v, *attr_dflt) 

468 return v 

469 

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

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

472 

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

474 

475 @return: The C{pygeodesy} module. 

476 

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

478 ''' 

479 if _headof(name) != parent and not _dunder_ismain(name): 

480 name = _DOT_(parent, name) 

481 try: 

482 return _sys.modules[name] 

483 except KeyError: 

484 return _getmodule(name, parent) 

485 

486 def into(self, **mod_dunder_name_): 

487 '''Lazily import module C{mod} into module C{_dunder_name_} 

488 and set C{_dunder_name_._mod} to module C{mod}, I{once}. 

489 ''' 

490 class _Into(object): 

491 

492 def __getattr__(unused, name): 

493 mod, dun = self.errors._xkwds_item2(mod_dunder_name_) 

494 _mod = _UNDER_(NN, mod) 

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

496 i = _getattribute(d, _mod, dun) 

497 assert isinstance(i, _Into) 

498 m = self.getmodule(mod) 

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

500 return getattr(m, name) 

501 

502 return _Into() 

503 

504# @_Property_RO 

505# def _isBoolean(self): 

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

507# ''' 

508# return self.booleans.isBoolean 

509 

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

511 '''Yield the modules imported so far. 

512 ''' 

513 _hd = _headof 

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

515 if _hd(n) == _pygeodesy_: 

516 yield n, m 

517 

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

519 

520__all__ = _ALL_LAZY.lazily 

521__version__ = '24.06.05' 

522 

523 

524def _ALL_OTHER(*objs): 

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

526 ''' 

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

528 n = _tailof(_dunder_nameof(o)) 

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

530 return getattr(_interns, i, n) 

531 

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

533 

534 

535if _FOR_DOCS: 

536 _ALL_DOCS = _ALL_OTHER 

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

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

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

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

541 # too much internal documentation. 

542else: 

543 def _ALL_DOCS(*unused): 

544 return () 

545 

546 

547def _all_deprecates(): 

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

549 ''' 

550 _D = _ALL_DEPRECATES 

551 if not _D: 

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

553 return _D 

554 

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

556 

557 

558def _all_imports(): 

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

560 ''' 

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

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

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

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

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

566 _D = _ALL_IMPORTS 

567 if not _D: 

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

569 return _D 

570 

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

572 

573 

574def _all_missing2(_all_): 

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

576 ''' 

577 def _diff(one, two): 

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

579 

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

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

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

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

584 

585 

586def _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 

597def _getattrof(attr_as): # .testDeprecated 

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

599 ''' 

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

601 return as_ or a_.rstrip(_DOT_) 

602 

603 

604def _getmodule(name, *parent): 

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

606 ''' 

607 try: 

608 return import_module(name, parent) 

609 except ImportError: 

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

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

612 

613 

614# def _lazy_attributes(_dunder_name_): 

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

616# on lazily imported modules and sub-modules. 

617# ''' 

618# if _unlazy: 

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

620# 

621# def _getattr(attr, *dflt): 

622# try: # a module name 

623# return _ALL_MODS.getmodule(attr) 

624# except (AttributeError, ImportError): 

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

626# 

627# return _getattr 

628 

629 

630_lazy_dict = {} # PYCHOK overwritten by _lazy_import2 

631 

632 

633def _lazy_import2(pack): # MCCABE 14 

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

635 

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

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

638 

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

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

641 C{package.__getattr__}. 

642 

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

644 invalid or does not exist. 

645 

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

647 an import failed. 

648 

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

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

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

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

653 causing a C{ModuleNotFoundError}. 

654 

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

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

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

658 ''' 

659 _DOT_ = _interns._DOT_ 

660 _SPACE_ = _interns._SPACE_ 

661 

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

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

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

665 

666 package, parent = _lazy_init2(pack) # _pygeodesy_ 

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

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

669 imports = _all_imports() 

670 deprecates = _all_deprecates() 

671 _dunder_package_ = '__package__' 

672 

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

674 # only called once for each undefined pygeodesy attribute 

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

676 if mod: 

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

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

679 # note in the _lazy_import2.__doc__ above). 

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

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

682 else: # from mod import name 

683 attr = name 

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

685 t = getattr(v, _dunder_package_, None) 

686 if t not in sub_packages: # invalid module package 

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

688 if attr: # get mod.attr 

689 v = _getattribute(v, attr, mod) 

690 

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

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

693 else: # PYCHOK no cover 

694 t = _no_(_module_, _or_, _attribute_) 

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

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

697 

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

699 if isLazy > 1: 

700 t = _SPACE_(_lazily_imported_, _DOT_(parent, name)) 

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

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

703 if isLazy > 2: 

704 try: # see C{_caller3} 

705 _, f, s = _caller3(2) 

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

707 except ValueError: 

708 pass 

709 printf(t) # XXX print 

710 

711 return v # __getattr__ 

712 

713 global _lazy_dict, _lazy_module 

714 _lazy_dict = package.__dict__ 

715 _lazy_module = __getattr__ 

716 

717 return package, __getattr__ # _lazy_import2 

718 

719 

720# def _lazy_import_all(_dunder_name_): 

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

722# of all items, see .deprecated.__init__ 

723# ''' 

724# if _unlazy: 

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

726# 

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

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

729# 

730# def _import_all(attr, *dflt): 

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

732# _getattr(attr, *dflt) 

733# 

734# return _import_all 

735 

736 

737def _lazy_import_as(_dunder_name_): 

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

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

740 lazily exported by C{__name__}. 

741 ''' 

742 if _unlazy: 

743 return None 

744 

745 def _import_as(mod): 

746 try: 

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

748 except ImportError: 

749 return _lazy_module(mod) 

750 

751 return _import_as 

752 

753 

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

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

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

757# ''' 

758# if _unlazy: 

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

760# 

761# def _import_star(_into_): 

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

763# ''' 

764# d = dict() 

765# nm = _tailof(_dunder_name_) 

766# _g = _ALL_MODS.getattr # pygeodesy.__getattr__ 

767# _h = _headof 

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

769# if _h(m) == nm: 

770# try: 

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

772# except (AttributeError, ImportError): 

773# pass 

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

775# return d.keys() # imported names 

776# 

777# return _import_star 

778 

779 

780def _lazy_init2(pack): 

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

782 

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

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

785 

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

787 for easy reference within itself and its name aka the 

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

789 

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

791 an import failed or the package name is 

792 invalid or does not exist. 

793 

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

795 ''' 

796 global isLazy, _unLazy0 

797 

798 z = _getenv(_PYGEODESY_LAZY_IMPORT_, None) 

799 if z is None: # _PYGEODESY_LAZY_IMPORT_ not set 

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

801 else: 

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

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

804 

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

806 

807 if isLazy < 1: # not enabled 

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

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

810 isLazy += 1 

811 

812 try: # to initialize in Python 3+ 

813 package = import_module(pack) 

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

815 if parent != pack: # assert 

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

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

818 

819 except (AttributeError, ImportError) as x: 

820 isLazy = False # failed 

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

822 

823 return package, parent 

824 

825 

826def _lazy_module(name): # overwritten by _lazy_import2 

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

828 ''' 

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

830 m = _ALL_MODS.getmodule(name) 

831 except (AttributeError, ImportError) as x: 

832 raise LazyImportError(name, cause=x) 

833 _lazy_dict[name] = m # cache 

834 return m 

835 

836 

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

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

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

840# ''' 

841# sm = dict() 

842# if force and not _dunder_ismain(_dunder_name_): 

843# nm = _tailof(_dunder_name_) 

844# _a = _ALL_MODS.getattr 

845# _m = _ALL_MODS.getmodule 

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

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

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

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

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

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

852# # like import m as s 

853# m = _m(m) 

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

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

856# pass 

857# d.update(sm) 

858# 

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

860 

861 

862# del _i, _i0 

863 

864# _ = _ALL_MODS.errors # force import pygeodesy.errors 

865 

866if _dunder_ismain(__name__): # PYCHOK no cover 

867 

868 from timeit import timeit 

869 

870 def t1(): 

871 from pygeodesy.trf import RefFrame 

872 return RefFrame 

873 

874 def t2(): 

875 return _ALL_MODS.trf.RefFrame 

876 

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

878 

879 t1 = timeit(t1, number=1000000) 

880 t2 = timeit(t2, number=1000000) 

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

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

883 

884# del t1, t2, timeit, v 

885 

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

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

888 

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

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

891 

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

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

894 

895# python2 -m pygeodesy.lazily 

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

897 

898# **) MIT License 

899# 

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

901# 

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

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

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

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

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

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

908# 

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

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

911# 

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

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

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

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

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

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

918# OTHER DEALINGS IN THE SOFTWARE.