Coverage for pygeodesy/internals.py: 88%

298 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-22 18:16 -0400

1 

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

3 

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

5''' 

6# from pygeodesy.basics import isiterablen, ubstr # _MODS 

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

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

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

10 _QUOTE2_, _s_, _SPACE_, _sys, _UNDER_, _utf_8_ 

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

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

13 

14# import os # _MODS 

15# import os.path # _MODS 

16# import sys as _sys # from .interns 

17 

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

19_arm64_ = 'arm64' 

20_iOS_ = 'iOS' 

21_macOS_ = 'macOS' 

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

23_Windows_ = 'Windows' 

24 

25 

26def _DUNDER_nameof(inst, *dflt): 

27 '''(INTERNAL) Get the DUNDER C{.__name__} attr. 

28 ''' 

29 try: 

30 return inst.__name__ 

31 except AttributeError: 

32 pass 

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

34 

35 

36def _DUNDER_nameof_(*names__): # in .errors._IsnotError 

37 '''(INTERNAL) Yield the _DUNDER_nameof or name. 

38 ''' 

39 return map(_DUNDER_nameof, names__, names__) 

40 

41 

42def _Property_RO(method): 

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

44 ''' 

45 name = _DUNDER_nameof(method) 

46 

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

48 delattr(inst, attr) # force error 

49 

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

51 try: # to get the cached value immediately 

52 v = inst.__dict__[name] 

53 except (AttributeError, KeyError): 

54 # cache the value in the instance' __dict__ 

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

56 return v 

57 

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

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

60 

61 return property(_get, _set, _del) 

62 

63 

64class _MODS_Base(object): 

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

66 ''' 

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

68 self.__dict__.pop(attr, None) 

69 

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

71 e = _MODS.errors 

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

73 raise e._AttributeError(_immutable_, txt=t) 

74 

75 @_Property_RO 

76 def basics(self): 

77 '''Get module C{pygeodesy.basics}, I{once}. 

78 ''' 

79 from pygeodesy import basics as b # DON'T _lazy_import2 

80 return b 

81 

82 @_Property_RO 

83 def bits_machine2(self): 

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

85 ''' 

86 import platform as p 

87 

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

89 m = m.replace(_COMMA_, _UNDER_) 

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

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

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

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

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

95 if _sysctl_uint('sysctl.proc_translated'): 

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

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

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

99 

100 @_Property_RO 

101 def ctypes3(self): 

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

103 ''' 

104 if _ismacOS(): 

105 from ctypes import CDLL, DEFAULT_MODE, _dlopen 

106 

107 def dlopen(name): 

108 return _dlopen(name, DEFAULT_MODE) 

109 

110 else: # PYCHOK no cover 

111 from ctypes import CDLL 

112 dlopen = _passarg 

113 

114 from ctypes.util import find_library 

115 return CDLL, dlopen, find_library 

116 

117 @_Property_RO 

118 def ctypes5(self): 

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

120 ''' 

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

122 return byref, c_char_p, c_size_t, c_uint, sizeof 

123 

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

125 return _DOT_(self.name, name) 

126 

127 @_Property_RO 

128 def errors(self): 

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

130 ''' 

131 from pygeodesy import errors as e # DON'T _lazy_import2 

132 return e 

133 

134 @_Property_RO 

135 def inspect(self): # in .basics 

136 '''Get module C{inspect}, I{once}. 

137 ''' 

138 import inspect as i 

139 return i 

140 

141 def ios_ver(self): 

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

143 ''' 

144 try: # Pythonista only 

145 from platform import iOS_ver 

146 return iOS_ver() 

147 except (AttributeError, ImportError): 

148 return NN, (NN, NN, NN), NN 

149 

150 @_Property_RO 

151 def libc(self): 

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

153 ''' 

154 return _load_lib('libc') 

155 

156 @_Property_RO 

157 def name(self): 

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

159 ''' 

160 return _DUNDER_nameof(self.__class__) 

161 

162 @_Property_RO 

163 def nix2(self): # PYCHOK no cover 

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

165 ''' 

166 import platform as p 

167 

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

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

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

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

172 _a = _MODS.streprs.anstr 

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

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

175 except (AttributeError, ImportError): 

176 pass # v = str(_0_0) 

177 n = n.capitalize() 

178 return n, v 

179 

180 def nix_ver(self): # PYCHOK no cover 

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

182 ''' 

183 _, v = _MODS.nix2 

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

185 return v, t, machine() 

186 

187 @_Property_RO 

188 def os(self): 

189 '''Get module C{os}, I{once}. 

190 ''' 

191 import os as o 

192 import os.path 

193 return o 

194 

195 @_Property_RO 

196 def osversion2(self): 

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

198 ''' 

199 import platform as p 

200 

201 _Nix, _ = _MODS.nix2 

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

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

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

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

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

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

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

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

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

211 (_macOS_, p.mac_ver), 

212 (_Windows_, p.win32_ver), 

213 (_Nix, _MODS.nix_ver), 

214 ('Java', p.java_ver), 

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

216 v = v()[0] 

217 if v and n: 

218 break 

219 else: 

220 n = v = NN # XXX AssertioError? 

221 return [n, v] 

222 

223 @_Property_RO 

224 def _Popen_kwds2(self): 

225 '''(INTERNAL) Get C{subprocess.Popen} and C{-kwds}. 

226 ''' 

227 import subprocess as _sub 

228 kwds = dict(creationflags=0, # executable=sys.executable, shell=True, 

229 stdin=_sub.PIPE, stdout=_sub.PIPE, stderr=_sub.STDOUT) 

230 if _sys.version_info[:2] > (3, 6): 

231 kwds.update(text=True) 

232 return _sub.Popen, kwds 

233 

234 @_Property_RO 

235 def Pythonarchine(self): 

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

237 ''' 

238 v = _sys.version 

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

240 pypy = _PyPy__(v) 

241 if pypy: # PYCHOK no cover 

242 l3.insert(0, pypy) 

243 return l3 

244 

245 @_Property_RO 

246 def streprs(self): 

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

248 ''' 

249 from pygeodesy import streprs as s # DON'T _lazy_import2 

250 return s 

251 

252 @_Property_RO 

253 def sys_version_info2(self): 

254 '''Get C{sys.version_inf0[:2], I{once}. 

255 ''' 

256 return _sys.version_info[:2] 

257 

258 @_Property_RO 

259 def version(self): 

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

261 ''' 

262 from pygeodesy import version as v 

263 return v 

264 

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

266 

267 

268def _caller3(up, base=True): # in .lazily, .named 

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

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

271 

272 @kwarg base: Use C{B{base}=False} for the fully-qualified file 

273 name, otherwise the base (module) name (C{bool}). 

274 ''' 

275 f = None 

276 _f = _MODS.os.path.basename if base else _passarg 

277 try: 

278 f = _sys._getframe(up + 1) # == inspect.stack()[up + 1][0] 

279 t = _MODS.inspect.getframeinfo(f) 

280 t = t.function, _f(t.filename), t.lineno 

281# or ... 

282 # f = _sys._getframe(up + 1) 

283 # c = f.f_code 

284 # t = (c.co_name, # caller name 

285 # _f(c.co_filename), # file name .py 

286 # f.f_lineno) # line number 

287# or ... 

288 # t = _MODS.inspect.stack()[up + 1] # (frame, filename, lineno, function, ...) 

289 # t = t[3], _f(t[1]), t[2] 

290 except (AttributeError, IndexError, ValueError): 

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

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

293 t = NN, NN, 0 

294 finally: 

295 del f # break ref cycle 

296 return t 

297 

298 

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

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

301 whitespace by C{white} if specified. 

302 ''' 

303 if strs: 

304 t = strs.split() 

305 if len(t) > 1: 

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

307 return strs 

308 

309 

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

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

312 ''' 

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

314 

315 

316_getenv = _MODS.os.getenv # PYCHOK in .lazily, ... 

317 

318 

319def _getPYGEODESY(which, dflt=NN): 

320 '''(INTERNAL) Return an C{PYGEODESY_...} ENV value or C{dflt}. 

321 ''' 

322 return _getenv(_PYGEODESY(which), dflt) 

323 

324 

325def _headof(name): 

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

327 ''' 

328 i = name.find(_DOT_) 

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

330 

331 

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

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

334# ''' 

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

336 

337 

338def _isAppleSi(): 

339 '''(INTERNAL) Is this C{macOS on Apple Silicon}? (C{bool}) 

340 ''' 

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

342 

343 

344def _is_DUNDER_main(name): 

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

346 ''' 

347 return name == '__main__' 

348 

349 

350def _isiOS(): # in test/bases 

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

352 ''' 

353 return _MODS.osversion2[0] is _iOS_ 

354 

355 

356def _ismacOS(): # in test/bases 

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

358 ''' 

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

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

361 

362 

363def _isNix(): # in test/bases 

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

365 ''' 

366 return _MODS.nix2[0] 

367 

368 

369def _isPyChecker(): 

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

371 ''' 

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

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

374 

375 

376def _isPyPy(): # in test/bases 

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

378 ''' 

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

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

381 

382 

383def _isWindows(): # in test/bases 

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

385 ''' 

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

387 _MODS.osversion2[0] is _Windows_ 

388 

389 

390def _load_lib(name): 

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

392 ''' 

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

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

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

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

397 CDLL, dlopen, find_lib = _MODS.ctypes3 

398 

399 ns = find_lib(name), name 

400 if dlopen is not _passarg: # _ismacOS() 

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

402 _DOT_(name, 'framework'), _MODS.os.path.join( 

403 _DOT_(name, 'framework'), name)) 

404 for n in ns: 

405 try: 

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

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

408 if lib._name: # has a qualified name 

409 return lib 

410 except (AttributeError, OSError): 

411 pass 

412 

413 return None # raise OSError 

414 

415 

416def machine(): 

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

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

419 

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

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

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

423 ''' 

424 return _MODS.bits_machine2[1] 

425 

426 

427def _name_version(pkg): 

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

429 ''' 

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

431 

432 

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

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

435 ''' 

436 l2 = _MODS.osversion2 

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

438 

439 

440def _passarg(arg): 

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

442 ''' 

443 return arg 

444 

445 

446def _passargs(*args): 

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

448 ''' 

449 return args 

450 

451 

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

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

454 ''' 

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

456 

457 

458def _popen2(cmd, stdin=None): # in .mgrs, .solveBase, .testMgrs 

459 '''(INTERNAL) Invoke C{B{cmd} tuple} and return 2-tuple C{(std, status)} 

460 with all C{stdout/-err} output, I{stripped} and C{int} exit status. 

461 ''' 

462 _Popen, kwds = _MODS._Popen_kwds2 

463 p = _Popen(cmd, **kwds) # PYCHOK kwArgs 

464 r = p.communicate(stdin)[0] # stdout + NL + stderr 

465 return _MODS.basics.ub2str(r).strip(), p.returncode 

466 

467 

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

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

470 

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

472 all positional. 

473 

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

475 ''' 

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

477 

478 

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

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

481 

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

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

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

485 @kwarg nl_nt_prec_prefix__end_file_flush_sep__kwds: Optional keyword arguments 

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

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

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

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

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

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

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

493 

494 @return: Number of bytes written. 

495 ''' 

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

497 try: 

498 if args: 

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

500 elif kwds: 

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

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

503 else: 

504 t = fmt 

505 except Exception as x: 

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

507 unstr = _MODS.streprs.unstr 

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

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

510 try: 

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

512 except UnicodeEncodeError: # XXX only Windows 

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

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

515 if fl: # PYCHOK no cover 

516 f.flush() 

517 return n 

518 

519 

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

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

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

523 ''' 

524 if nl > 0: 

525 prefix = NN(_NL_ * nl, prefix) 

526 if nt > 0: 

527 end = NN(end, _NL_ * nt) 

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

529 

530 

531def _PYGEODESY(which, i=0): 

532 '''(INTERNAL) Return an ENV C{str} C{PYGEODESY_...}. 

533 ''' 

534 try: 

535 w = which.__name__.lstrip(_UNDER_)[i:] 

536 except AttributeError: 

537 w = which 

538 return _UNDER_(_pygeodesy_, w).upper() 

539 

540 

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

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

543 ''' 

544 l3 = _MODS.Pythonarchine 

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

546 

547 

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

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

550 ''' 

551 if secs < 100.0: # _100_0 

552 unit = len(_SIsecs) - 1 

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

554 secs *= 1e3 # _1000_0 

555 unit -= 1 

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

557 else: 

558 m, s = divmod(secs, 60) 

559 if m < 60: 

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

561 else: 

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

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

564 return t 

565 

566 

567def _sizeof(obj, deep=True): 

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

569 

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

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

572 

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

574 class attributes and counting instances only 

575 once or C{None}. 

576 

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

578 ''' 

579 try: 

580 _zB = _sys.getsizeof 

581 _zD = _zB(None) # some default 

582 except TypeError: # PyPy3.10 

583 return None 

584 

585 b = _MODS.basics 

586 _isiterablen = b.isiterablen 

587 _Str_Bytes = b._Strs + b._Bytes # + (range, map) 

588 

589 def _zR(s, iterable): 

590 z, _s = 0, s.add 

591 for o in iterable: 

592 i = id(o) 

593 if i not in s: 

594 _s(i) 

595 z += _zB(o, _zD) 

596 if isinstance(o, dict): 

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

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

599 elif _isiterablen(o) and not \ 

600 isinstance(o, _Str_Bytes): 

601 z += _zR(s, o) 

602 elif deep: 

603 try: # size instance' attr values only 

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

605 except AttributeError: # None, int, etc. 

606 pass 

607 return z 

608 

609 return _zR(set(), (obj,)) 

610 

611 

612def _sysctl_uint(name): 

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

614 ''' 

615 libc = _MODS.libc 

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

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

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

619 u = uint(0) 

620 z = size_t(sizeof(u)) 

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

622 else: # couldn't find or load 'libc' 

623 r = -2 

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

625 

626 

627def _tailof(name): 

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

629 ''' 

630 i = name.rfind(_DOT_) + 1 

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

632 

633 

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

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

636 ''' 

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

638 

639 

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

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

642 ''' 

643 if opts_help: 

644 

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

646 if alts and help: 

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

648 for a in alts: 

649 if a.startswith(h): 

650 return NN(_DASH_, a), 

651 

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

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

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

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

656 if o: 

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

658 d = NN 

659 else: 

660 d = _DASH_ 

661 

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

663 

664 u = _COLON_(_DUNDER_nameof(_usage)[1:], NN) 

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

666 

667 

668def _usage_argv(argv0, *args): 

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

670 ''' 

671 o = _MODS.os 

672 m = o.path.dirname(argv0) 

673 m = m.replace(o.getcwd(), _ELLIPSIS_) \ 

674 .replace(o.sep, _DOT_).strip() 

675 b = o.path.basename(argv0) 

676 b, x = o.path.splitext(b) 

677 if x == '.py' and not _is_DUNDER_main(b): 

678 m = _DOT_(m or _pygeodesy_, b) 

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

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

681 

682 

683def _version2(version, n=2): 

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

685 ''' 

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

687 if len(t) < n: 

688 t += (0,) * n 

689 return t[:n] 

690 

691 

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

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

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

695 ''' 

696 try: 

697 return _version_ints(package.__version_info__) 

698 except AttributeError: 

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

700 

701 

702def _version_ints(vs): 

703 # helper for _version2 and _version_info above 

704 

705 def _ints(vs): 

706 for v in vs: 

707 try: 

708 yield int(v.strip()) 

709 except (TypeError, ValueError): 

710 pass 

711 

712 return tuple(_ints(vs)) 

713 

714 

715def _versions(sep=_SPACE_): 

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

717 ''' 

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

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

720 

721 

722__all__ = tuple(map(_DUNDER_nameof, (machine, print_, printf))) 

723__version__ = '24.10.20' 

724 

725if _is_DUNDER_main(__name__): # PYCHOK no cover 

726 

727 def _main(): 

728 from pygeodesy import _isfrozen, isLazy 

729 

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

731 'isLazy', isLazy])) 

732 

733 _main() 

734 

735# **) MIT License 

736# 

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

738# 

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

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

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

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

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

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

745# 

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

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

748# 

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

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

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

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

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

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

755# OTHER DEALINGS IN THE SOFTWARE.