Coverage for pygeodesy/internals.py: 87%

267 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-09-30 14:00 -0400

1# -*- coding: utf-8 -*- 

2 

3u'''Mostly INTERNAL functions, except L{machine}, L{print_} and L{printf}. 

4''' 

5# from pygeodesy.basics import isiterablen # _MODS 

6# from pygeodesy.errors import _AttributeError, _error_init, _UnexpectedError, _xError2 # _MODS 

7from pygeodesy.interns import NN, _BAR_, _COLON_, _DASH_, _DOT_, _ELLIPSIS_, _EQUALSPACED_, \ 

8 _immutable_, _NL_, _pygeodesy_, _PyPy__, _python_, _QUOTE1_, \ 

9 _QUOTE2_, _s_, _SPACE_, _sys, _UNDER_, _utf_8_ 

10from pygeodesy.interns import _COMMA_, _Python_ # PYCHOK used! 

11# from pygeodesy.streprs import anstr, pairs, unstr # _MODS 

12 

13import os as _os # in .lazily, ... 

14import os.path as _os_path 

15# import sys as _sys # from .interns 

16 

17_0_0 = 0.0 # PYCHOK in .basics, .constants 

18_arm64_ = 'arm64' 

19_iOS_ = 'iOS' 

20_macOS_ = 'macOS' 

21_SIsecs = 'fs', 'ps', 'ns', 'us', 'ms', 'sec' # reversed 

22_Windows_ = 'Windows' 

23 

24 

25def _dunder_nameof(inst, *dflt): 

26 '''(INTERNAL) Get the double_underscore __name__ attr. 

27 ''' 

28 try: 

29 return inst.__name__ 

30 except AttributeError: 

31 pass 

32 return dflt[0] if dflt else inst.__class__.__name__ 

33 

34 

35def _dunder_nameof_(*names__): # in .errors._IsnotError 

36 '''(INTERNAL) Yield the _dunder_nameof or name. 

37 ''' 

38 return map(_dunder_nameof, names__, names__) 

39 

40 

41def _Property_RO(method): 

42 '''(INTERNAL) Can't I{recursively} import L{props.property_RO}. 

43 ''' 

44 name = _dunder_nameof(method) 

45 

46 def _del(inst, attr): # PYCHOK no cover 

47 delattr(inst, attr) # force error 

48 

49 def _get(inst, **unused): # PYCHOK 2 vs 3 args 

50 try: # to get the cached value immediately 

51 v = inst.__dict__[name] 

52 except (AttributeError, KeyError): 

53 # cache the value in the instance' __dict__ 

54 inst.__dict__[name] = v = method(inst) 

55 return v 

56 

57 def _set(inst, val): # PYCHOK no cover 

58 setattr(inst, name, val) # force error 

59 

60 return property(_get, _set, _del) 

61 

62 

63class _MODS_Base(object): 

64 '''(INTERNAL) Base-class for C{lazily._ALL_MODS}. 

65 ''' 

66 def __delattr__(self, attr): # PYCHOK no cover 

67 self.__dict__.pop(attr, None) 

68 

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

70 m = _MODS.errors 

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

72 raise m._AttributeError(_immutable_, txt=t) 

73 

74 @_Property_RO 

75 def bits_machine2(self): 

76 '''Get platform 2-list C{[bits, machine]}, I{once}. 

77 ''' 

78 import platform as p 

79 

80 m = p.machine() # ARM64, arm64, x86_64, iPhone13,2, etc. 

81 m = m.replace(_COMMA_, _UNDER_) 

82 if m.lower() == 'x86_64': # PYCHOK on Intel or Rosetta2 ... 

83 v = p.mac_ver()[0] # ... and only on macOS ... 

84 if v and _version2(v) > (10, 15): # ... 11+ aka 10.16 

85 # <https://Developer.Apple.com/forums/thread/659846> 

86 # _sysctl_uint('hw.optional.arm64') and \ 

87 if _sysctl_uint('sysctl.proc_translated'): 

88 m = _UNDER_(_arm64_, m) # Apple Si emulating Intel x86-64 

89 return [p.architecture()[0], # bits 

90 m] # arm64, arm64_x86_64, x86_64, etc. 

91 

92 @_Property_RO 

93 def ctypes3(self): 

94 '''Get 3-tuple C{(ctypes.CDLL, ._dlopen, .util.findlibrary)}, I{once}. 

95 ''' 

96 if _ismacOS(): 

97 from ctypes import CDLL, DEFAULT_MODE, _dlopen 

98 

99 def dlopen(name): 

100 return _dlopen(name, DEFAULT_MODE) 

101 else: # PYCHOK no cover 

102 from ctypes import CDLL 

103 dlopen = _passarg 

104 

105 from ctypes.util import find_library 

106 return CDLL, dlopen, find_library 

107 

108 @_Property_RO 

109 def ctypes5(self): 

110 '''Get 5-tuple C{(ctypes.byref, .c_char_p, .c_size_t, .c_uint, .sizeof)}, I{once}. 

111 ''' 

112 from ctypes import byref, c_char_p, c_size_t, c_uint, sizeof # get_errno 

113 return byref, c_char_p, c_size_t, c_uint, sizeof 

114 

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

116 return _DOT_(self.name, name) 

117 

118 @_Property_RO 

119 def errors(self): 

120 '''Get module C{pygeodesy.errors}, I{once}. 

121 ''' 

122 from pygeodesy import errors # DON'T _lazy_import2 

123 return errors 

124 

125 def ios_ver(self): 

126 '''Mimick C{platform.xxx_ver} for C{iOS}. 

127 ''' 

128 try: # Pythonista only 

129 from platform import iOS_ver 

130 return iOS_ver() 

131 except (AttributeError, ImportError): 

132 return NN, (NN, NN, NN), NN 

133 

134 @_Property_RO 

135 def libc(self): 

136 '''Load C{libc.dll|dylib}, I{once}. 

137 ''' 

138 return _load_lib('libc') 

139 

140 @_Property_RO 

141 def name(self): 

142 '''Get this name (C{str}). 

143 ''' 

144 return _dunder_nameof(self.__class__) 

145 

146 @_Property_RO 

147 def nix2(self): # PYCHOK no cover 

148 '''Get Linux 2-list C{[distro, version]}, I{once}. 

149 ''' 

150 import platform as p 

151 

152 n, v = p.uname()[0], NN 

153 if n.lower() == 'linux': 

154 try: # use distro only for Linux, not macOS, etc. 

155 import distro # <https://PyPI.org/project/distro> 

156 _a = _MODS.streprs.anstr 

157 v = _a(distro.version()) # first 

158 n = _a(distro.id()) # .name()? 

159 except (AttributeError, ImportError): 

160 pass # v = str(_0_0) 

161 n = n.capitalize() 

162 return n, v 

163 

164 def nix_ver(self): # PYCHOK no cover 

165 '''Mimick C{platform.xxx_ver} for C{*nix}. 

166 ''' 

167 _, v = _MODS.nix2 

168 t = _version2(v, n=3) if v else (NN, NN, NN) 

169 return v, t, machine() 

170 

171 @_Property_RO 

172 def osversion2(self): 

173 '''Get 2-list C{[OS, release]}, I{once}. 

174 ''' 

175 import platform as p 

176 

177 _Nix, _ = _MODS.nix2 

178 # - mac_ver() returns ('10.12.5', ..., 'x86_64') on 

179 # macOS and ('10.3.3', ..., 'iPad4,2') on iOS 

180 # - win32_ver is ('XP', ..., 'SP3', ...) on Windows XP SP3 

181 # - platform() returns 'Darwin-16.6.0-x86_64-i386-64bit' 

182 # on macOS and 'Darwin-16.6.0-iPad4,2-64bit' on iOS 

183 # - sys.platform is 'darwin' on macOS, 'ios' on iOS, 

184 # 'win32' on Windows and 'cygwin' on Windows/Gygwin 

185 # - distro.id() and .name() return 'Darwin' on macOS 

186 for n, v in ((_iOS_, _MODS.ios_ver), 

187 (_macOS_, p.mac_ver), 

188 (_Windows_, p.win32_ver), 

189 (_Nix, _MODS.nix_ver), 

190 ('Java', p.java_ver), 

191 ('uname', p.uname)): 

192 v = v()[0] 

193 if v and n: 

194 break 

195 else: 

196 n = v = NN # XXX AssertioError? 

197 return [n, v] 

198 

199 @_Property_RO 

200 def Pythonarchine(self): 

201 '''Get 3- or 4-list C{[PyPy, Python, bits, machine]}, I{once}. 

202 ''' 

203 v = _sys.version 

204 l3 = [_Python_(v)] + self.bits_machine2 

205 pypy = _PyPy__(v) 

206 if pypy: # PYCHOK no cover 

207 l3.insert(0, pypy) 

208 return l3 

209 

210 @_Property_RO 

211 def _Str_Bytes(self): 

212 '''Get all C{str} and C{bytes} types. 

213 ''' 

214 import pygeodesy.basics as m 

215 return m._Strs + m._Bytes # + (range, map) 

216 

217 @_Property_RO 

218 def streprs(self): 

219 '''Get module C{pygeodesy.streprs}, I{once}. 

220 ''' 

221 from pygeodesy import streprs # DON'T _lazy_import2 

222 return streprs 

223 

224 @_Property_RO 

225 def version(self): 

226 '''Get pygeodesy version, I{once}. 

227 ''' 

228 from pygeodesy import version 

229 return version 

230 

231_MODS = _MODS_Base() # PYCHOK overwritten by .lazily 

232 

233 

234def _caller3(up): # in .lazily, .named 

235 '''(INTERNAL) Get 3-tuple C{(caller name, file name, line number)} 

236 for the caller B{C{up}} stack frames in the Python call stack. 

237 ''' 

238 # sys._getframe(1) ... 'importlib._bootstrap' line 1032, 

239 # may throw a ValueError('call stack not deep enough') 

240 f = _sys._getframe(up + 1) 

241 c = f.f_code 

242 return (c.co_name, # caller name 

243 _os_path.basename(c.co_filename), # file name .py 

244 f.f_lineno) # line number 

245 

246 

247def _dunder_ismain(name): 

248 '''(INTERNAL) Return C{name == '__main__'}. 

249 ''' 

250 return name == '__main__' 

251 

252 

253def _enquote(strs, quote=_QUOTE2_, white=NN): # in .basics, .solveBase 

254 '''(INTERNAL) Enquote a string containing whitespace or replace 

255 whitespace by C{white} if specified. 

256 ''' 

257 if strs: 

258 t = strs.split() 

259 if len(t) > 1: 

260 strs = white.join(t if white else (quote, strs, quote)) 

261 return strs 

262 

263 

264def _fper(p, q, per=100.0, prec=1): 

265 '''Format a percentage C{B{p} * B{per} / B{q}} (C{str}). 

266 ''' 

267 return '%.*f%%' % (prec, (float(p) * per / float(q))) 

268 

269 

270def _headof(name): 

271 '''(INTERNAL) Get the head name of qualified C{name} or the C{name}. 

272 ''' 

273 i = name.find(_DOT_) 

274 return name if i < 0 else name[:i] 

275 

276 

277# def _is(a, b): # PYCHOK no cover 

278# '''(INTERNAL) C{a is b}? in C{PyPy} 

279# ''' 

280# return (a == b) if _isPyPy() else (a is b) 

281 

282 

283def _isAppleM(): 

284 '''(INTERNAL) Is this C{Apple Silicon}? (C{bool}) 

285 ''' 

286 return _ismacOS() and machine().startswith(_arm64_) 

287 

288 

289def _isiOS(): # in test/bases.py 

290 '''(INTERNAL) Is this C{iOS}? (C{bool}) 

291 ''' 

292 return _MODS.osversion2[0] is _iOS_ 

293 

294 

295def _ismacOS(): # in test/bases.py 

296 '''(INTERNAL) Is this C{macOS}? (C{bool}) 

297 ''' 

298 return _sys.platform[:6] == 'darwin' and \ 

299 _MODS.osversion2[0] is _macOS_ # and os.name == 'posix' 

300 

301 

302def _isNix(): # in test/bases.py 

303 '''(INTERNAL) Is this a C{Linux} distro? (C{str} or L{NN}) 

304 ''' 

305 return _MODS.nix2[0] 

306 

307 

308def _isPyChecker(): 

309 '''(INTERNAL) Is C{PyChecker} running? (C{bool}). 

310 ''' 

311 # .../pychecker/checker.py --limit 0 --stdlib pygeodesy/<mod>/<name>.py 

312 return _sys.argv[0].endswith('/pychecker/checker.py') 

313 

314 

315def _isPyPy(): # in test/bases.py 

316 '''(INTERNAL) Is this C{PyPy}? (C{bool}) 

317 ''' 

318 # platform.python_implementation() == 'PyPy' 

319 return _MODS.Pythonarchine[0].startswith(_PyPy__) 

320 

321 

322def _isWindows(): # in test/bases.py 

323 '''(INTERNAL) Is this C{Windows}? (C{bool}) 

324 ''' 

325 return _sys.platform[:3] == 'win' and \ 

326 _MODS.osversion2[0] is _Windows_ 

327 

328 

329def _load_lib(name): 

330 '''(INTERNAL) Load a C{dylib}, B{C{name}} must startwith('lib'). 

331 ''' 

332 # macOS 11+ (aka 10.16) no longer provides direct loading of 

333 # system libraries. As a result, C{ctypes.util.find_library} 

334 # will not find any library, unless previously installed by a 

335 # low-level dlopen(name) call (with the library base C{name}). 

336 CDLL, dlopen, find_lib = _MODS.ctypes3 

337 

338 ns = find_lib(name), name 

339 if dlopen is not _passarg: # _ismacOS() 

340 ns += (_DOT_(name, 'dylib'), 

341 _DOT_(name, 'framework'), _os_path.join( 

342 _DOT_(name, 'framework'), name)) 

343 for n in ns: 

344 try: 

345 if n and dlopen(n): # pre-load handle 

346 lib = CDLL(n) # == ctypes.cdll.LoadLibrary(n) 

347 if lib._name: # has a qualified name 

348 return lib 

349 except (AttributeError, OSError): 

350 pass 

351 

352 return None # raise OSError 

353 

354 

355def machine(): 

356 '''Return standard C{platform.machine}, but distinguishing Intel I{native} 

357 from Intel I{emulation} on Apple Silicon (on macOS only). 

358 

359 @return: Machine C{'arm64'} for Apple Silicon I{native}, C{'x86_64'} 

360 for Intel I{native}, C{"arm64_x86_64"} for Intel I{emulation}, 

361 etc. (C{str} with C{comma}s replaced by C{underscore}s). 

362 ''' 

363 return _MODS.bits_machine2[1] 

364 

365 

366def _Math_K_2(): 

367 '''(INTERNAL) Return the I{Karney} Math setting. 

368 ''' 

369 return _MODS.karney._wrapped.Math_K_2 

370 

371 

372def _name_version(pkg): 

373 '''(INTERNAL) Return C{pskg.__name__ + ' ' + .__version__}. 

374 ''' 

375 return _SPACE_(pkg.__name__, pkg.__version__) 

376 

377 

378def _name_binary(path): 

379 '''(INTERNAL) Return C{(basename + ' ' + version)} of an executable. 

380 ''' 

381 if path: 

382 try: 

383 _, r = _MODS.solveBase._popen2((path, '--version')) 

384 return _SPACE_(_os_path.basename(path), r.split()[-1]) 

385 except (IndexError, IOError, OSError): 

386 pass 

387 return NN 

388 

389 

390def _osversion2(sep=NN): # in .lazily, test/bases.versions 

391 '''(INTERNAL) Get the O/S name and release as C{2-list} or C{str}. 

392 ''' 

393 l2 = _MODS.osversion2 

394 return sep.join(l2) if sep else l2 # 2-list() 

395 

396 

397def _passarg(arg): 

398 '''(INTERNAL) Helper, no-op. 

399 ''' 

400 return arg 

401 

402 

403def _passargs(*args): 

404 '''(INTERNAL) Helper, no-op. 

405 ''' 

406 return args 

407 

408 

409def _plural(noun, n, nn=NN): 

410 '''(INTERNAL) Return C{noun}['s'] or C{NN}. 

411 ''' 

412 return NN(noun, _s_) if n > 1 else (noun if n else nn) 

413 

414 

415def print_(*args, **nl_nt_prec_prefix__end_file_flush_sep__kwds): # PYCHOK no cover 

416 '''Python 3+ C{print}-like formatting and printing. 

417 

418 @arg args: Values to be converted to C{str} and joined by B{C{sep}}, 

419 all positional. 

420 

421 @see: Function L{printf} for further details. 

422 ''' 

423 return printf(NN, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds) 

424 

425 

426def printf(fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds): 

427 '''C{Printf-style} and Python 3+ C{print}-like formatting and printing. 

428 

429 @arg fmt: U{Printf-style<https://Docs.Python.org/3/library/stdtypes.html# 

430 printf-style-string-formatting>} format specification (C{str}). 

431 @arg args: Arguments to be formatted (any C{type}, all positional). 

432 @kwarg nl_nt_prec_prefix__end_file_flush_sep__kwds: Optional keyword arguments 

433 C{B{nl}=0} for the number of leading blank lines (C{int}), C{B{nt}=0} 

434 the number of trailing blank lines (C{int}), C{B{prefix}=NN} to be 

435 inserted before the formatted text (C{str}) and Python 3+ C{print} 

436 keyword arguments C{B{end}}, C{B{sep}}, C{B{file}} and C{B{flush}}. 

437 Any remaining C{B{kwds}} are C{printf-style} name-value pairs to be 

438 formatted, I{iff no B{C{args}} are present} using C{B{prec}=6} for 

439 the number of decimal digits (C{int}). 

440 

441 @return: Number of bytes written. 

442 ''' 

443 b, e, f, fl, p, s, kwds = _print7(**nl_nt_prec_prefix__end_file_flush_sep__kwds) 

444 try: 

445 if args: 

446 t = (fmt % args) if fmt else s.join(map(str, args)) 

447 elif kwds: 

448 t = (fmt % kwds) if fmt else s.join( 

449 _MODS.streprs.pairs(kwds, prec=p)) 

450 else: 

451 t = fmt 

452 except Exception as x: 

453 _E, s = _MODS.errors._xError2(x) 

454 unstr = _MODS.streprs.unstr 

455 t = unstr(printf, fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds) 

456 raise _E(s, txt=t, cause=x) 

457 try: 

458 n = f.write(NN(b, t, e)) 

459 except UnicodeEncodeError: # XXX only Windows 

460 t = t.replace('\u2032', _QUOTE1_).replace('\u2033', _QUOTE2_) 

461 n = f.write(NN(b, t, e)) 

462 if fl: # PYCHOK no cover 

463 f.flush() 

464 return n 

465 

466 

467def _print7(nl=0, nt=0, prec=6, prefix=NN, sep=_SPACE_, file=_sys.stdout, 

468 end=_NL_, flush=False, **kwds): 

469 '''(INTERNAL) Unravel the C{printf} and remaining keyword arguments. 

470 ''' 

471 if nl > 0: 

472 prefix = NN(_NL_ * nl, prefix) 

473 if nt > 0: 

474 end = NN(end, _NL_ * nt) 

475 return prefix, end, file, flush, prec, sep, kwds 

476 

477 

478def _Pythonarchine(sep=NN): # in .lazily, test/bases.py versions 

479 '''(INTERNAL) Get PyPy and Python versions, bits and machine as C{3- or 4-list} or C{str}. 

480 ''' 

481 l3 = _MODS.Pythonarchine 

482 return sep.join(l3) if sep else l3 # 3- or 4-list 

483 

484 

485def _secs2str(secs): # in .geoids, ../test/bases.py 

486 '''Convert a time in C{secs} to C{str}. 

487 ''' 

488 if secs < _MODS.constants._100_0: 

489 unit = len(_SIsecs) - 1 

490 while 0 < secs < 1 and unit > 0: 

491 secs *= 1e3 # _1000_0 

492 unit -= 1 

493 t = '%.3f %s' % (secs, _SIsecs[unit]) 

494 else: 

495 m, s = divmod(secs, 60) 

496 if m < 60: 

497 t = '%d:%06.3f' % (int(m), s) 

498 else: 

499 h, m = divmod(int(m), 60) 

500 t = '%d:%02d:%06.3f' % (h, m, s) 

501 return t 

502 

503 

504def _sizeof(obj, deep=True): 

505 '''(INTERNAL) Recursively size an C{obj}ect. 

506 

507 @kwarg deep: If C{True}, include the size of all 

508 C{.__dict__.values()} (C{bool}). 

509 

510 @return: The C{obj} size in bytes (C{int}), ignoring 

511 class attributes and counting instances only 

512 once or C{None}. 

513 

514 @note: With C{PyPy}, the returned size is always C{None}. 

515 ''' 

516 try: 

517 _zB = _sys.getsizeof 

518 _zD = _zB(None) # some default 

519 except TypeError: # PyPy3.10 

520 return None 

521 

522 _isiterablen = _MODS.basics.isiterablen 

523 

524 def _zR(s, iterable): 

525 z, _s = 0, s.add 

526 for o in iterable: 

527 i = id(o) 

528 if i not in s: 

529 _s(i) 

530 z += _zB(o, _zD) 

531 if isinstance(o, dict): 

532 z += _zR(s, o.keys()) 

533 z += _zR(s, o.values()) 

534 elif _isiterablen(o) and not \ 

535 isinstance(o, _MODS._Str_Bytes): 

536 z += _zR(s, o) 

537 elif deep: 

538 try: # size instance' attr values only 

539 z += _zR(s, o.__dict__.values()) 

540 except AttributeError: # None, int, etc. 

541 pass 

542 return z 

543 

544 return _zR(set(), (obj,)) 

545 

546 

547def _sysctl_uint(name): 

548 '''(INTERNAL) Get an unsigned int sysctl item by name, use on macOS ONLY! 

549 ''' 

550 libc = _MODS.libc 

551 if libc: # <https://StackOverflow.com/questions/759892/python-ctypes-and-sysctl> 

552 byref, char_p, size_t, uint, sizeof = _MODS.ctypes5 

553 n = name if str is bytes else bytes(name, _utf_8_) # PYCHOK isPython2 = str is bytes 

554 u = uint(0) 

555 z = size_t(sizeof(u)) 

556 r = libc.sysctlbyname(char_p(n), byref(u), byref(z), None, size_t(0)) 

557 else: # could find or load 'libc' 

558 r = -2 

559 return int(r if r else u.value) # -1 ENOENT error, -2 no libc 

560 

561 

562def _tailof(name): 

563 '''(INTERNAL) Get the base name of qualified C{name} or the C{name}. 

564 ''' 

565 i = name.rfind(_DOT_) + 1 

566 return name[i:] if i > 0 else name 

567 

568 

569def _under(name): # PYCHOK in .datums, .auxilats, .ups, .utm, .utmupsBase, ... 

570 '''(INTERNAL) Prefix C{name} with an I{underscore}. 

571 ''' 

572 return name if name.startswith(_UNDER_) else NN(_UNDER_, name) 

573 

574 

575def _usage(file_py, *args, **opts_help): # in .etm, .geodesici 

576 '''(INTERNAL) Build "usage: python -m ..." cmd line for module B{C{file_py}}. 

577 ''' 

578 if opts_help: 

579 

580 def _help(alts=(), help=NN, **unused): 

581 if alts and help: 

582 h = NN(help, _SPACE_).lstrip(_DASH_) 

583 for a in alts: 

584 if a.startswith(h): 

585 return NN(_DASH_, a), 

586 

587 def _opts(opts=NN, alts=(), **unused): 

588 # opts='T--v-C-R meter-c|i|n|o' 

589 d, fmt = NN, _MODS.streprs.Fmt.SQUARE 

590 for o in (opts + _BAR_(*alts)).split(_DASH_): 

591 if o: 

592 yield fmt(NN(d, _DASH_, o.replace(_BAR_, ' | -'))) 

593 d = NN 

594 else: 

595 d = _DASH_ 

596 

597 args = _help(**opts_help) or (tuple(_opts(**opts_help)) + args) 

598 

599 u = _COLON_(_dunder_nameof(_usage)[1:], NN) 

600 return _SPACE_(u, *_usage_argv(file_py, *args)) 

601 

602 

603def _usage_argv(argv0, *args): 

604 '''(INTERNAL) Return 3-tuple C{(python, '-m', module, *args)}. 

605 ''' 

606 m = _os_path.dirname(argv0).replace(_os.getcwd(), _ELLIPSIS_) \ 

607 .replace(_os.sep, _DOT_).strip() 

608 b, x = _os_path.splitext(_os_path.basename(argv0)) 

609 if x == '.py' and not _dunder_ismain(b): 

610 m = _DOT_(m or _pygeodesy_, b) 

611 p = NN(_python_, _sys.version_info[0]) 

612 return (p, '-m', _enquote(m)) + args 

613 

614 

615def _version2(version, n=2): 

616 '''(INTERNAL) Split C{B{version} str} into a C{1-, 2- or 3-tuple} of C{int}s. 

617 ''' 

618 t = _version_ints(version.split(_DOT_, 2)) 

619 if len(t) < n: 

620 t += (0,) * n 

621 return t[:n] 

622 

623 

624def _version_info(package): # in .Base.karney, .basics 

625 '''(INTERNAL) Get the C{package.__version_info__} as a 2- or 

626 3-tuple C{(major, minor, revision)} if C{int}s. 

627 ''' 

628 try: 

629 return _version_ints(package.__version_info__) 

630 except AttributeError: 

631 return _version2(package.__version__.strip(), n=3) 

632 

633 

634def _version_ints(vs): 

635 # helper for _version2 and _version_info above 

636 

637 def _ints(vs): 

638 for v in vs: 

639 try: 

640 yield int(v.strip()) 

641 except (TypeError, ValueError): 

642 pass 

643 

644 return tuple(_ints(vs)) 

645 

646 

647def _versions(sep=_SPACE_): 

648 '''(INTERNAL) Get pygeodesy, PyPy and Python versions, bits, machine and OS as C{7- or 8-list} or C{str}. 

649 ''' 

650 l7 = [_pygeodesy_, _MODS.version] + _Pythonarchine() + _osversion2() 

651 return sep.join(l7) if sep else l7 # 5- or 6-list 

652 

653 

654__all__ = tuple(map(_dunder_nameof, (machine, print_, printf))) 

655__version__ = '24.09.04' 

656 

657if _dunder_ismain(__name__): # PYCHOK no cover 

658 

659 from pygeodesy import _isfrozen, isLazy 

660 

661 print_(*(_versions(sep=NN) + ['_isfrozen', _isfrozen, 

662 'isLazy', isLazy])) 

663 

664# **) MIT License 

665# 

666# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved. 

667# 

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

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

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

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

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

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

674# 

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

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

677# 

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

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

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

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

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

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

684# OTHER DEALINGS IN THE SOFTWARE.