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 later}. 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}. 22A C{lazy import} of a top-level module inherently loads all sub-modules 23imported by that top-level module. 24''' 25from pygeodesy.interns import MISSING, NN, __all__ as _interns_a_l_l_, \ 26 _areaOf_, _attribute_, _COMMASPACE_, \ 27 _doesn_t_exist_, _DOT_, _dunder_name, \ 28 _enabled_, _EQUALSPACED_, _immutable_, \ 29 _isclockwise_, _ispolar_, _module_, \ 30 _NL_, _no_, _not_, _or_, _perimeterOf_, \ 31 _Python_, _pygeodesy_abspath_, _sep_, \ 32 _SPACE_, _UNDER_, _version_ 33 34from os import getenv as _getenv # in .errors, .geodsolve, .props, .units 35from os.path import basename as _basename 36import sys as _sys # in .props 37try: 38 from importlib import import_module 39except ImportError: # no import_module in Python 2.6- 40 41 def import_module(name, package=None): # PYCHOK 42 raise LazyImportError(name=name, package=package, 43 txt=_no_(import_module.__name__)) 44 45_a_l_l_ = '__all__' 46_FOR_DOCS = _getenv('PYGEODESY_FOR_DOCS', NN) # for epydoc ... 47_imports_ = 'imports' 48_p_a_c_k_a_g_e_ = '__package__' 49_PYGEODESY_LAZY_IMPORT_ = 'PYGEODESY_LAZY_IMPORT' 50_PYTHON_X_DEV = getattr(_sys, '_xoptions', {}).get('dev', # Python 3.2 51 _getenv('PYTHONDEVMODE', NN)) # PYCHOK exported 52_sub_packages = 'deprecated' , 'geodesicx' 53_sys_version_info2 = _sys.version_info[:2] # in .fmath, .geodsolve 54 55# @module_property[_RO?] <https://GitHub.com/jtushman/proxy_tools/> 56isLazy = None # see @var isLazy 57 58 59class LazyImportError(ImportError): 60 '''Raised if C{lazy import} is not supported, disabled or failed some other way. 61 ''' 62 def __init__(self, *name_value, **txt): 63 from pygeodesy.errors import _error_init 64 _error_init(ImportError, self, name_value, **txt) 65 66 67class _Dict(dict): 68 '''(INTERNAL) Imports C{dict}. 69 ''' 70 def add(self, key, value, *values): 71 '''Add C{[key] = value}, typically C{[attr] = mod}. 72 73 @raise AssertionError: The B{C{key}} already 74 exists with different 75 B{C{value}}. 76 ''' 77 if key in self: 78 val = self[key] # duplicate OK 79 if val != value and val not in values: # PYCHOK no cover 80 from pygeodesy.streprs import Fmt as _Fmt 81 t = _Fmt.SQUARE(_imports_, key), val, value 82 raise AssertionError('%s: %r, not %r' % t) 83 else: 84 self[key] = value 85 86 87class _NamedEnum_RO(dict): 88 '''(INTERNAL) C{Read_Only} enum-like C{dict} sub-class. 89 ''' 90# _name = NN # also first kwd, __init__(_name=...) 91 92 def _DOT_(self, attr): 93 return _DOT_(self._name, attr) # PYCHOK _name 94 95 def __getattr__(self, attr): 96 try: 97 return self[attr] 98 except KeyError: 99 from pygeodesy.errors import _AttributeError 100 raise _AttributeError(self._DOT_(attr), txt=_doesn_t_exist_) 101 102 def __setattr__(self, attr, value): # PYCHOK no cover 103 from pygeodesy.errors import _TypeError 104 t = _EQUALSPACED_(self._DOT_(attr), value) 105 raise _TypeError(t, txt=_immutable_) 106 107 def enums(self): 108 for k, v in dict.items(self): 109 if not k.startswith(_UNDER_): # skip _name 110 yield k, v 111 112 113_ALL_INIT = _pygeodesy_abspath_, _version_ 114 115# __all__ value for most modules, accessible as _ALL_LAZY.<module> 116_ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY', 117 albers=('AlbersEqualArea', 'AlbersEqualArea2', 'AlbersEqualArea4', 118 'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth', 119 'AlbersError', 'Albers7Tuple'), 120 azimuthal=('AzimuthalError', 'Azimuthal7Tuple', 121 'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney', 122 'Gnomonic', 'GnomonicExact', 'GnomonicGeodSolve', 'GnomonicKarney', 123 'LambertEqualArea', 'Orthographic', 'Stereographic', 124 'equidistant', 'gnomonic'), 125 basics=('clips', 'copysign0', 'copytype', 'halfs2', 126 'isbool', 'isclass', 'isfinite', 'isidentifier', 'isinf', 'isint', 'iskeyword', 127 'isnan', 'isnear0', 'isneg0', 'isnon0', 'isodd', 'isscalar', 'issequence', 'isstr', 'issubclassof', 128 'len2', 'map1', 'map2', 'neg', 'neg_', 129 'signOf', 'splice', 'ub2str', 'unsign0'), 130 clipy=('ClipError', 131 'ClipCS4Tuple', 'ClipLB6Tuple', 'ClipSH3Tuple', 132 'clipCS4', 'clipLB6', 'clipSH', 'clipSH3'), 133 css=('CassiniSoldner', 'Css', 'CSSError', 'toCss', 134 'EasNorAziRk4Tuple', 'LatLonAziRk4Tuple'), 135 datums=('Datum', 'Datums', 'Transform', 'Transforms'), 136 deprecated=('OK', # DEPRECATED constants 137 'bases', 'datum', 'nvector', # DEPRECATED modules 138 'ClipCS3Tuple', 'EcefCartesian', 'HeightIDW', 'HeightIDW2', 'HeightIDW3', 'RefFrameError', 'UtmUps4Tuple', # DEPRECATED classes 139 'anStr', 'areaof', 'bounds', 'clipCS3', 'clipDMS', 'clipStr', 'decodeEPSG2', 'encodeEPSG', # most of the DEPRECATED functions, ... 140 'equirectangular3', 'enStr2', 'false2f', 'falsed2f', 'fStr', 'fStrzs', # ... except ellipsoidal, spherical flavors 141 'hypot3', 'inStr', 'isenclosedby', 'joined', 'joined_', 142 'nearestOn3', 'nearestOn4', 'parseUTM', 'perimeterof', 'polygon', 143 'scalar', 'simplify2', 'toUtm', 'unStr', 'utmZoneBand2'), 144 dms=('F_D', 'F_DM', 'F_DMS', 'F_DEG', 'F_MIN', 'F_SEC', 'F__E', 'F__F', 'F__G', 'F_RAD', 145 'F_D_', 'F_DM_', 'F_DMS_', 'F_DEG_', 'F_MIN_', 'F_SEC_', 'F__E_', 'F__F_', 'F__G_', 'F_RAD_', 146 'F_D__', 'F_DM__', 'F_DMS__', 'F_DEG__', 'F_MIN__', 'F_SEC__', 'F__E__', 'F__F__', 'F__G__', 'F_RAD__', 147 'S_DEG', 'S_MIN', 'S_SEC', 'S_RAD', 'S_SEP', 'ParseError', 148 'bearingDMS', 'clipDegrees', 'clipRadians', 'compassDMS', 'compassPoint', 149 'degDMS', 'latDMS', 'latlonDMS', 'lonDMS', 'normDMS', 150 'parseDDDMMSS', 'parseDMS', 'parseDMS2', 'parse3llh', 'parseRad', 'precision', 'toDMS'), 151 ecef=('EcefError', 'EcefFarrell21', 'EcefFarrell22', 'EcefKarney', 'EcefMatrix', 'EcefSudano', 'Ecef9Tuple', 'EcefVeness', 'EcefYou'), 152 elevations=('elevation2', 'geoidHeight2', 153 'Elevation2Tuple', 'GeoidHeight2Tuple'), 154 ellipsoidalExact=(), # module only 155 ellipsoidalGeodSolve=(), # module only 156 ellipsoidalKarney=(), # module only 157 ellipsoidalNvector=('Ned3Tuple',), # nothing else 158 ellipsoidalVincenty=('VincentyError',), # nothing else 159 ellipsoids=('R_M', 'R_MA', 'R_MB', 'R_KM', 'R_NM', 'R_SM', 'R_FM', 'R_GM', 'R_VM', 160 'a_f2Tuple', 'Circle4Tuple', 'Curvature2Tuple', 161 'Ellipsoid', 'Ellipsoid2', 'Ellipsoids', 162 'a_b2e', 'a_b2e2', 'a_b2e22', 'a_b2e32', 'a_b2f', 'a_b2f_', 'a_b2f2', 'a_b2n', 163 'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a', 164 'f2e2', 'f2e22', 'f2e32', 'f_2f', 'f2f_', 'f2f2', 'f2n', 'n2e2', 'n2f', 'n2f_'), 165 elliptic=('Elliptic', 'EllipticError', 'Elliptic3Tuple'), 166 epsg=('Epsg', 'EPSGError'), 167 errors=('CrossError', 'IntersectionError', 'NumPyError', 'LenError', 'LimitError', 'PointsError', 168 'RangeError', 'SciPyError', 'SciPyWarning', 'TRFError', 'UnitError', 'VectorError', 169 'crosserrors', 'exception_chaining', 'limiterrors', 'rangerrors'), 170 etm=('Etm', 'ETMError', 'ExactTransverseMercator', 171 'EasNorExact4Tuple', 'LatLonExact4Tuple', 172 'parseETM5', 'toEtm8'), 173 fmath=('Fdot', 'Fhorner', 'Fpolynomial', 'Fsum', 174 'cbrt', 'cbrt2', 'euclid', 'euclid_', 175 'facos1', 'fasin1', 'fatan', 'fatan1', 'fatan2', 'favg', 176 'fdot', 'fdot3', 'fmean', 'fmean_', 'fhorner', 'fidw', 'fpolynomial', 177 'fpowers', 'fprod', 'frange', 'freduce', 'fsum', 'fsum_', 'fsum1_', 178 'hypot', 'hypot_', 'hypot1', 'hypot2', 'hypot2_', 179 'norm2', 'norm_', 'sqrt0', 'sqrt3'), 180 formy=('antipode', 'antipode_', 'bearing', 'bearing_', 181 'compassAngle', 'cosineForsytheAndoyerLambert', 'cosineForsytheAndoyerLambert_', 182 'cosineAndoyerLambert', 'cosineAndoyerLambert_', 'cosineLaw', 'cosineLaw_', 183 'equirectangular', 'equirectangular_', 'euclidean', 'euclidean_', 184 'excessAbc', 'excessGirard', 'excessLHuilier', 185 'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_', 186 'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_', 187 'hartzell', 'haversine', 'haversine_', 'heightOf', 'horizon', 'hubeny', 'hubeny_', 188 'intersections2', 'isantipode', 'isantipode_', 189 'latlon2n_xyz', 'n_xyz2latlon', 'n_xyz2philam', 190 'opposing', 'opposing_', 'philam2n_xyz', 191 'radical2', 'thomas', 'thomas_', 'vincentys', 'vincentys_', 192 'Radical2Tuple'), 193 frechet=('Frechet', 'FrechetDegrees', 'FrechetError', 'FrechetRadians', 194 'FrechetCosineAndoyerLambert', 'FrechetCosineForsytheAndoyerLambert', 195 'FrechetCosineLaw', 'FrechetDistanceTo', 'FrechetEquirectangular', 196 'FrechetEuclidean', 'FrechetExact', 'FrechetFlatLocal', 'FrechetFlatPolar', 197 'FrechetHaversine', 'FrechetHubeny', 'FrechetKarney', 'FrechetThomas', 198 'FrechetVincentys', 'Frechet6Tuple', 199 'frechet_'), 200 gars=('Garef', 'GARSError'), 201 geodesicx=('gx', 'gxarea', 'gxline', # modules 202 'Caps', 'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'), 203 geodsolve=('GeodesicSolve', 'GeodesicLineSolve'), 204 geohash=('Geohash', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple'), 205 geoids=('GeoidError', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights', 206 'PGMError', 'GeoidHeight5Tuple'), 207 hausdorff=('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians', 208 'HausdorffCosineAndoyerLambert', 'HausdorffCosineForsytheAndoyerLambert', 209 'HausdorffCosineLaw', 'HausdorffDistanceTo', 'HausdorffEquirectangular', 210 'HausdorffEuclidean', 'HausdorffExact', 'HausdorffFlatLocal', 'HausdorffFlatPolar', 211 'HausdorffHaversine', 'HausdorffHubeny', 'HausdorffKarney', 'HausdorffThomas', 212 'HausdorffVincentys', 'Hausdorff6Tuple', 213 'hausdorff_', 'randomrangenerator'), 214 heights=('HeightError', 215 'HeightIDWcosineAndoyerLambert', 'HeightIDWcosineForsytheAndoyerLambert', 216 'HeightIDWcosineLaw', 'HeightIDWdistanceTo', 'HeightIDWequirectangular', 217 'HeightIDWeuclidean', 'HeightIDWflatLocal', 'HeightIDWflatPolar', 'HeightIDWhaversine', 218 'HeightIDWhubeny', 'HeightIDWkarney', 'HeightIDWthomas', 'HeightIDWvincentys', 219 'HeightCubic', 'HeightLinear', 'HeightLSQBiSpline', 'HeightSmoothBiSpline'), 220 interns=_interns_a_l_l_, 221 iters=('LatLon2PsxyIter', 'PointsIter', 'points2', 222 'isNumpy2', 'isPoints2', 'isTuple2', 'iterNumpy2', 'iterNumpy2over'), 223 karney=('Direct9Tuple', 'GDict', 'GeodesicError', 'GeodSolve12Tuple', 'Inverse10Tuple'), 224 lazily=('LazyImportError', 'isLazy', 'print_', 'printf'), 225 lcc=('Conic', 'Conics', 'Lcc', 'LCCError', 'toLcc'), 226 ltp=('Frustum', 'LocalCartesian', 'LocalError', 'Ltp'), 227 ltpTuples=('Aer', 'Aer4Tuple', 'Enu', 'Enu4Tuple', 'Footprint5Tuple', 'Local9Tuple', 228 'Ned', 'Ned4Tuple', 'XyzLocal', 'Xyz4Tuple'), 229 mgrs=('Mgrs', 'MGRSError', 'parseMGRS', 'toMgrs', 'Mgrs4Tuple', 'Mgrs6Tuple'), 230 named=('callername', 'classname', 'classnaming', 'modulename', 'nameof', 'notImplemented', 'notOverloaded'), 231 namedTuples=('Bearing2Tuple', 'Bounds2Tuple', 'Bounds4Tuple', 232 'Destination2Tuple', 'Destination3Tuple', 233 'Distance2Tuple', 'Distance3Tuple', 'Distance4Tuple', 234 'EasNor2Tuple', 'EasNor3Tuple', 'Intersection3Tuple', 235 'LatLon2Tuple', 'LatLon3Tuple', 'LatLon4Tuple', 236 'LatLonDatum3Tuple', 'LatLonDatum5Tuple', 237 'LatLonPrec3Tuple', 'LatLonPrec5Tuple', 238 'NearestOn3Tuple', 239 'PhiLam2Tuple', 'PhiLam3Tuple', 'PhiLam4Tuple', 'Point3Tuple', 'Points2Tuple', 240 'Triangle7Tuple', 'Triangle8Tuple', 'Trilaterate5Tuple', 241 'UtmUps2Tuple', 'UtmUps5Tuple', 'UtmUps8Tuple', 'UtmUpsLatLon5Tuple', 242 'Vector2Tuple', 'Vector3Tuple', 'Vector4Tuple'), 243 osgr=('Osgr', 'OSGRError', 'parseOSGR', 'toOsgr'), 244 points=('LatLon_', 'LatLon2psxy', 'NearestOn5Tuple', 'Numpy2LatLon', 'Shape2Tuple', 'Tuple2LatLon', 245 _areaOf_, 'boundsOf', 'centroidOf', 'fractional', 246 _isclockwise_, 'isconvex', 'isconvex_', 'isenclosedBy', _ispolar_, 247 'luneOf', 'nearestOn5', _perimeterOf_, 'quadOf'), 248 props=('Property', 'Property_RO', 'property_RO', 'property_doc_', 249 'deprecated_class', 'deprecated_function', 'deprecated_method', 250 'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'), 251 sphericalNvector=(), # module only 252 sphericalTrigonometry=(), # module only 253 simplify=('simplify1', 'simplifyRDP', 'simplifyRDPm', 'simplifyRW', 'simplifyVW', 'simplifyVWm'), 254 streprs=('anstr', 'attrs', 'enstr2', 'fstr', 'fstrzs', 'hstr', 'instr', 'pairs', 'reprs', 'strs', 'unstr'), 255 trf=('RefFrame', 'RefFrames', 'Transform7Tuple', 256 'date2epoch', 'epoch2date', 'trfXform'), 257 units=('Band', 'Bearing', 'Bearing_', 'Bool', 258 'Degrees', 'Degrees_', 'Degrees2', 'Distance', 'Distance_', 'Easting', 'Epoch', 259 'Feet', 'FIx', 'Float', 'Float_', 'Height', 'Int', 'Int_', 260 'Lam', 'Lam_', 'Lat', 'Lat_', 'Lon', 'Lon_', 261 'Meter', 'Meter_', 'Meter2', 'Meter3', 'Northing', 'Number_', 262 'Phi', 'Phi_', 'Precision_', 'Radians', 'Radians_', 'Radians2', 263 'Radius', 'Radius_', 'Scalar', 'Scalar_', 'Str', 'Zone'), 264 ups=('Ups', 'UPSError', 'parseUPS5', 'toUps8', 'upsZoneBand5'), 265 utily=('acos1', 'acre2ha', 'acre2m2', 'asin1', 'atand', 'atan2b', 'atan2d', 266 'chain2m', 'circle4', 'cotd', 'cotd_', 267 'degrees', 'degrees90', 'degrees180', 'degrees360', 'degrees2grades', 'degrees2m', 268 'fathom2m', 'ft2m', 'furlong2m', 269 'grades', 'grades400', 'grades2degrees', 'grades2radians', 270 'm2degrees', 'm2ft', 'm2km', 'm2NM', 'm2radians', 'm2SM', 'm2yard', 271 'radians', 'radiansPI', 'radiansPI2', 'radiansPI_2', 'radians2m', 272 'sincos2', 'sincos2_', 'sincos2d', 'sincos2d_', 'tand', 'tand_', 'tan_2', 'tanPI_2_2', 273 'unroll180', 'unrollPI', 274 'wrap90', 'wrap180', 'wrap360', 'wrapPI_2','wrapPI', 'wrapPI2', 275 'yard2m'), 276 utm=('Utm', 'UTMError', 'parseUTM5', 'toUtm8', 'utmZoneBand5'), 277 utmups=('UtmUps', 'UTMUPSError', 'parseUTMUPS5', 'toUtmUps8', 278 'utmupsValidate', 'utmupsValidateOK', 'utmupsZoneBand5'), 279 vector2d=('Circin6Tuple', 'Circum3Tuple', 'Circum4Tuple', 'Meeus2Tuple', 'Radii11Tuple', 'Soddy4Tuple', 280 'circin6', 'circum3', 'circum4_', 'meeus2', 'radii11', 'soddy4'), 281 vector3d=('Vector3d', 'intersection3d3', 'iscolinearWith', 'parse3d', 'trilaterate2d2', 'trilaterate3d2'), 282 webmercator=('Wm', 'WebMercatorError', 'parseWM', 'toWm', 'EasNorRadius3Tuple'), 283 wgrs=('Georef', 'WGRSError')) 284 285# DEPRECATED __all__ names overloading those in _ALL_LAZY.deprecated where 286# the new name is fully backward compatible in signature and return value 287_ALL_OVERRIDDEN = _NamedEnum_RO(_name='_ALL_OVERRIDING', # all DEPRECATED 288 basics=('clips as clipStr',), 289 fmath=('hypot_ as hypot3',), 290 formy=('points2 as polygon',), 291 heights=('HeightIDWequirectangular as HeightIDW2', 'HeightIDWeuclidean as HeightIDW', 292 'HeightIDWhaversine as HeightIDW3'), 293 points=('areaOf as areaof', 294 'isenclosedBy as isenclosedby', 'perimeterOf as perimeterof'), 295 simplify=('simplifyRW as simplify2',), 296 streprs=('anstr as anStr', 'enstr2 as enStr2', 'fstr as fStr', 'fstrzs as fStrzs', 297 'instr as inStr', 'unstr as unStr')) 298 299__all__ = _ALL_LAZY.lazily 300__version__ = '21.09.14' 301 302 303def _ALL_OTHER(*objs): 304 '''(INTERNAL) List local objects for __all__. 305 ''' 306 from pygeodesy import interns # PYCHOK import 307 308 def _dun(o): 309 n = _dunder_name(o).rsplit(_DOT_, 1)[-1] 310 u = NN(_UNDER_, n, _UNDER_) 311 return getattr(interns, u, n) 312 313 return tuple(map(_dun, objs)) 314 315 316if _FOR_DOCS: 317 _ALL_DOCS = _ALL_OTHER 318 # (INTERNAL) Only export B{C{objs.__name__}} when making the 319 # docs to force C{epydoc} to include certain classes, methods, 320 # functions and other names in the documentation. Using the 321 # C{epydoc --private ...} command line option tends to include 322 # too much internal documentation. 323else: 324 def _ALL_DOCS(*unused): 325 return () 326 327 328def _all_imports(**more): 329 '''(INTERNAL) Build C{dict} of all lazy imports. 330 ''' 331 # imports naming conventions stored below - [<key>] = <from>: 332 # import <module> - [<module>] = <module> 333 # from <module> import <attr> - [<attr>] = <module> 334 # from pygeodesy import <attr> - [<attr>] = <attr> 335 # from <module> import <attr> as <name> - [<name>] = <module>.<attr> 336 imports = _Dict() 337 imports_add = imports.add 338 339 for ALL in (_ALL_LAZY, _ALL_OVERRIDDEN, more): 340 for mod, attrs in ALL.items(): 341 if isinstance(attrs, tuple) and not mod.startswith(_UNDER_): 342 imports_add(mod, mod) 343 for attr in attrs: 344 attr, _, as_attr = attr.partition(' as ') 345 if as_attr: 346 imports_add(as_attr, _DOT_(mod, attr), *_sub_packages) 347 else: 348 imports_add(attr, mod) 349 return imports 350 351 352def _all_missing2(_all_): 353 '''(INTERNAL) Get diffs between pygeodesy.__all__ and lazily._all_imports. 354 ''' 355 _alzy = _all_imports(**_NamedEnum_RO((a, ()) for a in _ALL_INIT)) 356 return ((_DOT_('lazily', _all_imports.__name__), _COMMASPACE_.join(a for a in _all_ if a not in _alzy)), 357 (_DOT_('pygeodesy', _a_l_l_), _COMMASPACE_.join(a for a in _alzy if a not in _all_))) 358 359 360def _caller3(up): # in .named 361 '''(INTERNAL) Get 3-tuple C{(caller name, file name, line number)} 362 for the caller B{C{up}} stack frames in the Python call stack. 363 ''' 364 # sys._getframe(1) ... 'importlib._bootstrap' line 1032, 365 # may throw a ValueError('call stack not deep enough') 366 f = _sys._getframe(up + 1) 367 return (f.f_code.co_name, # caller name 368 _basename(f.f_code.co_filename), # file name 369 f.f_lineno) # line number 370 371 372def _lazy_import2(_pygeodesy_): # MCCABE 15 373 '''Check for and set up C{lazy import}. 374 375 @arg _pygeodesy_: The name of the package (C{str}) performing 376 the imports, to help facilitate resolving 377 relative imports, usually C{__package__}. 378 379 @return: 2-Tuple C{(package, getattr)} of the importing package 380 for easy reference within itself and the callable to 381 be set to `__getattr__`. 382 383 @raise LazyImportError: Lazy import not supported or not enabled, 384 an import failed or the package name or 385 module name or attribute name is invalid 386 or does not exist. 387 388 @note: This is the original function U{modutil.lazy_import 389 <https://GitHub.com/brettcannon/modutil/blob/master/modutil.py>} 390 modified to handle the C{__all__} and C{__dir__} attributes 391 and call C{importlib.import_module(<module>.<name>, ...)} 392 without causing a C{ModuleNotFoundError}. 393 394 @see: The original U{modutil<https://PyPi.org/project/modutil>}, 395 U{PEP 562<https://www.Python.org/dev/peps/pep-0562>} and the 396 U{new way<https://Snarky.CA/lazy-importing-in-python-3-7/>}. 397 ''' 398 if _sys_version_info2 < (3, 7): # not supported before 3.7 399 t = _no_(_DOT_(_pygeodesy_, _lazy_import2.__name__)) 400 raise LazyImportError(t, txt=_Python_(_sys)) 401 402 package, parent = _lazy_init2(_pygeodesy_) 403 404 packages = (parent, '__main__', NN) + tuple( 405 _DOT_(parent, s) for s in _sub_packages) 406 imports = _all_imports() 407 408 def __getattr__(name): # __getattr__ only for Python 3.7+ 409 # only called once for each undefined pygeodesy attribute 410 if name in imports: 411 # importlib.import_module() implicitly sets sub-modules 412 # on this module as appropriate for direct imports (see 413 # note in the _lazy_import.__doc__ above). 414 mod, _, attr = imports[name].partition(_DOT_) 415 if mod not in imports: 416 raise LazyImportError(_no_(_module_), txt=_DOT_(parent, mod)) 417 imported = import_module(_DOT_(_pygeodesy_, mod), parent) 418 pkg = getattr(imported, _p_a_c_k_a_g_e_, None) 419 if pkg not in packages: # invalid package 420 raise LazyImportError(_DOT_(mod, _p_a_c_k_a_g_e_), pkg) 421 # import the module or module attribute 422 if attr: 423 imported = getattr(imported, attr, MISSING) 424 elif name != mod: 425 imported = getattr(imported, name, MISSING) 426 if imported is MISSING: 427 raise LazyImportError(_no_(_attribute_), 428 txt=_DOT_(mod, attr or name)) 429 430 elif name in (_a_l_l_,): # XXX '_d_i_r_', '_m_e_m_b_e_r_s_'? 431 imported = _ALL_INIT + tuple(imports.keys()) 432 mod = NN 433 else: 434 raise LazyImportError(_no_(_module_, _or_, _attribute_), 435 txt=_DOT_(parent, name)) 436 437 setattr(package, name, imported) 438 if isLazy > 1: 439 z = NN 440 if mod and mod != name: 441 z = ' from .%s' % (mod,) 442 if isLazy > 2: 443 try: # see C{_caller3} 444 _, f, s = _caller3(2) 445 z = '%s by %s line %d' % (z, f, s) 446 except ValueError: 447 pass 448 printf('# lazily imported %s%s', _DOT_(parent, name), z) 449 450 return imported # __getattr__ 451 452 return package, __getattr__ # _lazy_import2 453 454 455def _lazy_init2(_pygeodesy_): 456 '''(INTERNAL) Try to initialize lazy import. 457 458 @arg _pygeodesy_: The name of the package (C{str}) performing 459 the imports, to help facilitate resolving 460 relative imports, usually C{__package__}. 461 462 @return: 3-Tuple C{(import_module, package, parent)} of module 463 C{importlib.import_module}, the importing C{package} 464 for easy reference within itself, always C{pygeodesy} 465 and the package name, aka the C{parent}, always 466 C{'pygeodesy'}. 467 468 @raise LazyImportError: Lazy import not supported or not enabled, 469 an import failed or the package name is 470 invalid or does not exist. 471 472 @note: Global C{isLazy} is set accordingly. 473 ''' 474 global isLazy 475 476 z = _getenv(_PYGEODESY_LAZY_IMPORT_, None) 477 if z is None: # _PYGEODESY_LAZY_IMPORT_ not set 478 isLazy = 1 # ... but on by default on 3.7 479 else: 480 z = z.strip() # like PYTHONVERBOSE et.al. 481 isLazy = int(z) if z.isdigit() else (1 if z else 0) 482 if isLazy < 1: # not enabled 483 raise LazyImportError(_PYGEODESY_LAZY_IMPORT_, repr(z), txt=_not_(_enabled_)) 484 if _getenv('PYTHONVERBOSE', None): # PYCHOK no cover 485 isLazy += 1 486 487 try: # to initialize in Python 3+ 488 package = import_module(_pygeodesy_) 489 parent = package.__spec__.parent # __spec__ only in Python 3.7+ 490 if parent != _pygeodesy_: # assert 491 t = _COMMASPACE_(parent, _not_(_pygeodesy_)) 492 raise AttributeError(_EQUALSPACED_('parent', t)) 493 494 except (AttributeError, ImportError) as x: 495 isLazy = False # failed 496 raise LazyImportError(_lazy_init2.__name__, _pygeodesy_, txt=str(x)) 497 498 return package, parent 499 500 501def print_(*args, **nl_nt_prefix_end_file_flush_sep): 502 '''Python 3-style C{print} function. 503 504 @arg args: Values to be converted to C{str} and 505 concatenated (C{any} types). 506 @kwarg nl=0: Number of leading blank lines (C{int}). 507 @kwarg nt=0: Number of additional , trailing blank lines (C{int}). 508 @kwarg prefix=NN: To be inserted before the formatted text (C{str}). 509 510 @note: Python 3+ keyword arguments C{end}, C{file} and C{flush} 511 are silently ignored. 512 ''' 513 sep = nl_nt_prefix_end_file_flush_sep.get(_sep_, _SPACE_) 514 txt = sep.join(map(str, args)) 515 printf(txt, **nl_nt_prefix_end_file_flush_sep) 516 517 518def printf(fmt, *args, **nl_nt_prefix_end_file_flush_sep): 519 '''C-style C{printf} function. 520 521 @arg fmt: C-style formating text (C{str}). 522 @arg args: Values to be formatted (C{any} types). 523 @kwarg nl=0: Number of leading blank lines (C{int}). 524 @kwarg nt=0: Number of additional , trailing blank lines (C{int}). 525 @kwarg prefix=NN: To be inserted before the formatted text (C{str}). 526 527 @note: Python 3+ keyword arguments C{end}, C{file}, C{flush} 528 and C{sep} are silently ignored. 529 ''' 530 def _kwds(nl=0, nt=0, prefix=NN, **kwds): # XXX end? 531 nl = (_NL_ * nl) if nl else NN 532 nt = (_NL_ * nt) if nt else NN 533 return nl, nt, prefix, kwds 534 535 nl, nt, prefix, _ = _kwds(**nl_nt_prefix_end_file_flush_sep) 536 if args: 537 fmt %= args 538# elif kwds: 539# fmt %= kwds 540 print(NN(nl, prefix, fmt, nt)) 541 542 543# **) MIT License 544# 545# Copyright (C) 2018-2021 -- mrJean1 at Gmail -- All Rights Reserved. 546# 547# Permission is hereby granted, free of charge, to any person obtaining a 548# copy of this software and associated documentation files (the "Software"), 549# to deal in the Software without restriction, including without limitation 550# the rights to use, copy, modify, merge, publish, distribute, sublicense, 551# and/or sell copies of the Software, and to permit persons to whom the 552# Software is furnished to do so, subject to the following conditions: 553# 554# The above copyright notice and this permission notice shall be included 555# in all copies or substantial portions of the Software. 556# 557# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 558# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 559# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 560# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 561# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 562# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 563# OTHER DEALINGS IN THE SOFTWARE. 564