pygeodesy 24.5.8__py2.py3-none-any.whl → 24.5.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/METADATA +2 -2
  2. PyGeodesy-24.5.24.dist-info/RECORD +116 -0
  3. pygeodesy/__init__.py +16 -12
  4. pygeodesy/__main__.py +9 -10
  5. pygeodesy/albers.py +42 -42
  6. pygeodesy/auxilats/__init__.py +1 -1
  7. pygeodesy/auxilats/__main__.py +7 -10
  8. pygeodesy/auxilats/auxAngle.py +32 -31
  9. pygeodesy/auxilats/auxLat.py +81 -51
  10. pygeodesy/azimuthal.py +123 -124
  11. pygeodesy/basics.py +165 -176
  12. pygeodesy/booleans.py +14 -15
  13. pygeodesy/cartesianBase.py +25 -23
  14. pygeodesy/clipy.py +3 -3
  15. pygeodesy/constants.py +8 -6
  16. pygeodesy/css.py +50 -42
  17. pygeodesy/datums.py +50 -48
  18. pygeodesy/dms.py +6 -6
  19. pygeodesy/ecef.py +27 -27
  20. pygeodesy/elevations.py +2 -2
  21. pygeodesy/ellipsoidalBase.py +28 -27
  22. pygeodesy/ellipsoidalBaseDI.py +8 -7
  23. pygeodesy/ellipsoidalNvector.py +11 -12
  24. pygeodesy/ellipsoids.py +41 -35
  25. pygeodesy/elliptic.py +12 -10
  26. pygeodesy/epsg.py +4 -3
  27. pygeodesy/errors.py +35 -13
  28. pygeodesy/etm.py +62 -53
  29. pygeodesy/fmath.py +48 -41
  30. pygeodesy/formy.py +93 -65
  31. pygeodesy/frechet.py +117 -102
  32. pygeodesy/fstats.py +52 -46
  33. pygeodesy/fsums.py +169 -145
  34. pygeodesy/gars.py +10 -9
  35. pygeodesy/geodesicw.py +32 -30
  36. pygeodesy/geodesicx/__init__.py +1 -1
  37. pygeodesy/geodesicx/__main__.py +4 -4
  38. pygeodesy/geodesicx/gx.py +40 -32
  39. pygeodesy/geodesicx/gxarea.py +15 -12
  40. pygeodesy/geodesicx/gxbases.py +3 -4
  41. pygeodesy/geodesicx/gxline.py +6 -8
  42. pygeodesy/geodsolve.py +28 -26
  43. pygeodesy/geohash.py +47 -44
  44. pygeodesy/geoids.py +37 -35
  45. pygeodesy/hausdorff.py +112 -99
  46. pygeodesy/heights.py +136 -129
  47. pygeodesy/internals.py +576 -0
  48. pygeodesy/interns.py +6 -207
  49. pygeodesy/iters.py +22 -19
  50. pygeodesy/karney.py +18 -15
  51. pygeodesy/ktm.py +31 -24
  52. pygeodesy/latlonBase.py +12 -11
  53. pygeodesy/lazily.py +140 -218
  54. pygeodesy/lcc.py +24 -25
  55. pygeodesy/ltp.py +83 -71
  56. pygeodesy/ltpTuples.py +7 -5
  57. pygeodesy/mgrs.py +5 -4
  58. pygeodesy/named.py +136 -49
  59. pygeodesy/namedTuples.py +33 -25
  60. pygeodesy/nvectorBase.py +10 -9
  61. pygeodesy/osgr.py +14 -12
  62. pygeodesy/points.py +13 -13
  63. pygeodesy/props.py +7 -7
  64. pygeodesy/rhumb/__init__.py +1 -1
  65. pygeodesy/rhumb/bases.py +3 -2
  66. pygeodesy/rhumb/solve.py +2 -2
  67. pygeodesy/solveBase.py +8 -7
  68. pygeodesy/sphericalTrigonometry.py +5 -5
  69. pygeodesy/streprs.py +8 -7
  70. pygeodesy/trf.py +8 -8
  71. pygeodesy/triaxials.py +67 -63
  72. pygeodesy/units.py +48 -50
  73. pygeodesy/unitsBase.py +24 -11
  74. pygeodesy/ups.py +7 -6
  75. pygeodesy/utily.py +4 -4
  76. pygeodesy/utm.py +53 -52
  77. pygeodesy/utmupsBase.py +11 -8
  78. pygeodesy/vector2d.py +6 -7
  79. pygeodesy/vector3d.py +16 -17
  80. pygeodesy/vector3dBase.py +5 -5
  81. PyGeodesy-24.5.8.dist-info/RECORD +0 -115
  82. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/WHEEL +0 -0
  83. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/top_level.txt +0 -0
pygeodesy/internals.py ADDED
@@ -0,0 +1,576 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ u'''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
7
+ from pygeodesy.interns import NN, _COLON_, _DOT_, _ELLIPSIS_, _EQUALSPACED_, \
8
+ _immutable_, _NL_, _pygeodesy_, _PyPy__, _python_, \
9
+ _QUOTE1_, _QUOTE2_, _s_, _SPACE_, _sys, _UNDER_, _utf_8_
10
+ from pygeodesy.interns import _COMMA_, _Python_ # PYCHOK used!
11
+ # from pygeodesy.streprs import anstr, pairs, unstr # _MODS
12
+
13
+ import os as _os # in .lazily, ...
14
+ import 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
+ _Windows_ = 'Windows'
22
+
23
+
24
+ def _dunder_nameof(inst, *dflt):
25
+ '''(INTERNAL) Get the double_underscore __name__ attr.
26
+ '''
27
+ try:
28
+ return inst.__name__
29
+ except AttributeError:
30
+ pass
31
+ return dflt[0] if dflt else inst.__class__.__name__
32
+
33
+
34
+ def _Property_RO(method):
35
+ '''(INTERNAL) Can't Irecursively import L{props.property_RO}.
36
+ '''
37
+ name = _dunder_nameof(method)
38
+
39
+ def _del(inst, attr): # PYCHOK no cover
40
+ delattr(inst, attr) # force error
41
+
42
+ def _get(inst, **unused): # PYCHOK 2 vs 3 args
43
+ try: # to get the cached value immediately
44
+ v = inst.__dict__[name]
45
+ except (AttributeError, KeyError):
46
+ # cache the value in the instance' __dict__
47
+ inst.__dict__[name] = v = method(inst)
48
+ return v
49
+
50
+ def _set(inst, val): # PYCHOK no cover
51
+ setattr(inst, name, val) # force error
52
+
53
+ return property(_get, _set, _del)
54
+
55
+
56
+ class _ALL_MODS_Base(object):
57
+ '''(INTERNAL) Base-class for C{lazily._ALL_MODS}.
58
+ '''
59
+ def __delattr__(self, attr): # PYCHOK no cover
60
+ self.__dict__.pop(attr, None)
61
+
62
+ def __setattr__(self, attr, value): # PYCHOK no cover
63
+ m = _MODS.errors
64
+ t = _EQUALSPACED_(self._DOT_(attr), repr(value))
65
+ raise m._AttributeError(_immutable_, txt=t)
66
+
67
+ @_Property_RO
68
+ def bits_machine2(self):
69
+ '''Get platform 2-list C{[bits, machine]}, I{once}.
70
+ '''
71
+ import platform as p
72
+
73
+ m = p.machine() # ARM64, arm64, x86_64, iPhone13,2, etc.
74
+ m = m.replace(_COMMA_, _UNDER_)
75
+ if m.lower() == 'x86_64': # PYCHOK on Intel or Rosetta2 ...
76
+ v = p.mac_ver()[0] # ... and only on macOS ...
77
+ if v and _version2(v) > (10, 15): # ... 11+ aka 10.16
78
+ # <https://Developer.Apple.com/forums/thread/659846>
79
+ # _sysctl_uint('hw.optional.arm64') and \
80
+ if _sysctl_uint('sysctl.proc_translated'):
81
+ m = _UNDER_(_arm64_, m) # Apple Si emulating Intel x86-64
82
+ return [p.architecture()[0], # bits
83
+ m] # arm64, arm64_x86_64, x86_64, etc.
84
+
85
+ @_Property_RO
86
+ def ctypes3(self):
87
+ '''Get 3-tuple C{(ctypes.CDLL, ._dlopen, .util.findlibrary)}, I{once}.
88
+ '''
89
+ if _ismacOS():
90
+ from ctypes import CDLL, DEFAULT_MODE, _dlopen
91
+
92
+ def dlopen(name):
93
+ return _dlopen(name, DEFAULT_MODE)
94
+ else: # PYCHOK no cover
95
+ from ctypes import CDLL
96
+ dlopen = _passarg
97
+
98
+ from ctypes.util import find_library
99
+ return CDLL, dlopen, find_library
100
+
101
+ @_Property_RO
102
+ def ctypes5(self):
103
+ '''Get 5-tuple C{(ctypes.byref, .c_char_p, .c_size_t, .c_uint, .sizeof)}, I{once}.
104
+ '''
105
+ from ctypes import byref, c_char_p, c_size_t, c_uint, sizeof # get_errno
106
+ return byref, c_char_p, c_size_t, c_uint, sizeof
107
+
108
+ def _DOT_(self, name): # PYCHOK no cover
109
+ return _DOT_(self.name, name)
110
+
111
+ @_Property_RO
112
+ def errors(self):
113
+ '''Get module C{pygeodesy.errors}, I{once}.
114
+ '''
115
+ from pygeodesy import errors # DON'T _lazy_import2
116
+ return errors
117
+
118
+ def ios_ver(self):
119
+ '''Mimick C{platform.xxx_ver} for C{iOS}.
120
+ '''
121
+ try: # Pythonista only
122
+ from platform import iOS_ver
123
+ return iOS_ver()
124
+ except (AttributeError, ImportError):
125
+ return NN, (NN, NN, NN), NN
126
+
127
+ @_Property_RO
128
+ def libc(self):
129
+ '''Load C{libc.dll|dylib}, I{once}.
130
+ '''
131
+ return _load_lib('libc')
132
+
133
+ @_Property_RO
134
+ def name(self):
135
+ '''Get this name (C{str}).
136
+ '''
137
+ return _dunder_nameof(self.__class__)
138
+
139
+ @_Property_RO
140
+ def nix2(self): # PYCHOK no cover
141
+ '''Get Linux 2-list C{[distro, version]}, I{once}.
142
+ '''
143
+ import platform as p
144
+
145
+ n, v = p.uname()[0], NN
146
+ if n.lower() == 'linux':
147
+ try: # use distro only for Linux, not macOS, etc.
148
+ import distro # <https://PyPI.org/project/distro>
149
+ _a = _MODS.streprs.anstr
150
+ v = _a(distro.version()) # first
151
+ n = _a(distro.id()) # .name()?
152
+ except (AttributeError, ImportError):
153
+ pass # v = str(_0_0)
154
+ n = n.capitalize()
155
+ return n, v
156
+
157
+ def nix_ver(self): # PYCHOK no cover
158
+ '''Mimick C{platform.xxx_ver} for C{*nix}.
159
+ '''
160
+ _, v = _MODS.nix2
161
+ t = _version2(v, n=3) if v else (NN, NN, NN)
162
+ return v, t, machine()
163
+
164
+ @_Property_RO
165
+ def osversion2(self):
166
+ '''Get 2-list C{[OS, release]}, I{once}.
167
+ '''
168
+ import platform as p
169
+
170
+ _Nix, _ = _MODS.nix2
171
+ # - mac_ver() returns ('10.12.5', ..., 'x86_64') on
172
+ # macOS and ('10.3.3', ..., 'iPad4,2') on iOS
173
+ # - win32_ver is ('XP', ..., 'SP3', ...) on Windows XP SP3
174
+ # - platform() returns 'Darwin-16.6.0-x86_64-i386-64bit'
175
+ # on macOS and 'Darwin-16.6.0-iPad4,2-64bit' on iOS
176
+ # - sys.platform is 'darwin' on macOS, 'ios' on iOS,
177
+ # 'win32' on Windows and 'cygwin' on Windows/Gygwin
178
+ # - distro.id() and .name() return 'Darwin' on macOS
179
+ for n, v in ((_iOS_, _MODS.ios_ver),
180
+ (_macOS_, p.mac_ver),
181
+ (_Windows_, p.win32_ver),
182
+ (_Nix, _MODS.nix_ver),
183
+ ('Java', p.java_ver),
184
+ ('uname', p.uname)):
185
+ v = v()[0]
186
+ if v and n:
187
+ break
188
+ else:
189
+ n = v = NN # XXX AssertioError?
190
+ return [n, v]
191
+
192
+ @_Property_RO
193
+ def Pythonarchine(self):
194
+ '''Get 3- or 4-list C{[PyPy, Python, bits, machine]}, I{once}.
195
+ '''
196
+ v = _sys.version
197
+ l3 = [_Python_(v)] + self.bits_machine2
198
+ pypy = _PyPy__(v)
199
+ if pypy: # PYCHOK no cover
200
+ l3.insert(0, pypy)
201
+ return l3
202
+
203
+ @_Property_RO
204
+ def streprs(self):
205
+ '''Get module C{pygeodesy.streprs}, I{once}.
206
+ '''
207
+ from pygeodesy import streprs # DON'T _lazy_import2
208
+ return streprs
209
+
210
+ _MODS = _ALL_MODS_Base() # PYCHOK overwritten by .lazily
211
+
212
+
213
+ def _caller3(up): # in .lazily, .named
214
+ '''(INTERNAL) Get 3-tuple C{(caller name, file name, line number)}
215
+ for the caller B{C{up}} stack frames in the Python call stack.
216
+ '''
217
+ # sys._getframe(1) ... 'importlib._bootstrap' line 1032,
218
+ # may throw a ValueError('call stack not deep enough')
219
+ f = _sys._getframe(up + 1)
220
+ return (f.f_code.co_name, # caller name
221
+ _os_path.basename(f.f_code.co_filename), # file name
222
+ f.f_lineno) # line number
223
+
224
+
225
+ def _dunder_ismain(name):
226
+ '''(INTERNAL) Return C{name == '__main__'}.
227
+ '''
228
+ return name == '__main__'
229
+
230
+
231
+ def _enquote(strs, quote=_QUOTE2_, white=NN): # in .basics, .solveBase
232
+ '''(INTERNAL) Enquote a string containing whitespace or replace
233
+ whitespace by C{white} if specified.
234
+ '''
235
+ if strs:
236
+ t = strs.split()
237
+ if len(t) > 1:
238
+ strs = white.join(t if white else (quote, strs, quote))
239
+ return strs
240
+
241
+
242
+ def _headof(name):
243
+ '''(INTERNAL) Get the head name of qualified C{name} or the C{name}.
244
+ '''
245
+ i = name.find(_DOT_)
246
+ return name if i < 0 else name[:i]
247
+
248
+
249
+ # def _is(a, b): # PYCHOK no cover
250
+ # '''(INTERNAL) C{a is b}? in C{PyPy}
251
+ # '''
252
+ # return (a == b) if _isPyPy() else (a is b)
253
+
254
+
255
+ def _isAppleM():
256
+ '''(INTERNAL) Is this C{Apple Silicon}? (C{bool})
257
+ '''
258
+ return _ismacOS() and machine().startswith(_arm64_)
259
+
260
+
261
+ def _isiOS(): # in test/bases.py
262
+ '''(INTERNAL) Is this C{iOS}? (C{bool})
263
+ '''
264
+ return _MODS.osversion2[0] is _iOS_
265
+
266
+
267
+ def _ismacOS(): # in test/bases.py
268
+ '''(INTERNAL) Is this C{macOS}? (C{bool})
269
+ '''
270
+ return _sys.platform[:6] == 'darwin' and \
271
+ _MODS.osversion2[0] is _macOS_ # and os.name == 'posix'
272
+
273
+
274
+ def _isNix(): # in test/bases.py
275
+ '''(INTERNAL) Is this a C{Linux} distro? (C{str} or L{NN})
276
+ '''
277
+ return _MODS.nix2[0]
278
+
279
+
280
+ def _isPyPy(): # in test/bases.py
281
+ '''(INTERNAL) Is this C{PyPy}? (C{bool})
282
+ '''
283
+ # platform.python_implementation() == 'PyPy'
284
+ return _MODS.Pythonarchine[0].startswith(_PyPy__)
285
+
286
+
287
+ def _isWindows(): # in test/bases.py
288
+ '''(INTERNAL) Is this C{Windows}? (C{bool})
289
+ '''
290
+ return _sys.platform[:3] == 'win' and \
291
+ _MODS.osversion2[0] is _Windows_
292
+
293
+
294
+ def _load_lib(name):
295
+ '''(INTERNAL) Load a C{dylib}, B{C{name}} must startwith('lib').
296
+ '''
297
+ # macOS 11+ (aka 10.16) no longer provides direct loading of
298
+ # system libraries. As a result, C{ctypes.util.find_library}
299
+ # will not find any library, unless previously installed by a
300
+ # low-level dlopen(name) call (with the library base C{name}).
301
+ CDLL, dlopen, find_lib = _MODS.ctypes3
302
+
303
+ ns = find_lib(name), name
304
+ if dlopen is not _passarg: # _ismacOS()
305
+ ns += (_DOT_(name, 'dylib'),
306
+ _DOT_(name, 'framework'), _os_path.join(
307
+ _DOT_(name, 'framework'), name))
308
+ for n in ns:
309
+ try:
310
+ if n and dlopen(n): # pre-load handle
311
+ lib = CDLL(n) # == ctypes.cdll.LoadLibrary(n)
312
+ if lib._name: # has a qualified name
313
+ return lib
314
+ except (AttributeError, OSError):
315
+ pass
316
+
317
+ return None # raise OSError
318
+
319
+
320
+ def machine():
321
+ '''Return standard C{platform.machine}, but distinguishing Intel I{native}
322
+ from Intel I{emulation} on Apple Silicon (on macOS only).
323
+
324
+ @return: Machine C{'arm64'} for Apple Silicon I{native}, C{'x86_64'}
325
+ for Intel I{native}, C{"arm64_x86_64"} for Intel I{emulation},
326
+ etc. (C{str} with C{comma}s replaced by C{underscore}s).
327
+ '''
328
+ return _MODS.bits_machine2[1]
329
+
330
+
331
+ def _name_version(pkg):
332
+ '''(INTERNAL) Return C{pskg.__name__ + ' ' + .__version__}.
333
+ '''
334
+ return _SPACE_(pkg.__name__, pkg.__version__)
335
+
336
+
337
+ def _osversion2(sep=NN): # in .lazily, test/bases.versions
338
+ '''(INTERNAL) Get the O/S name and release as C{2-list} or C{str}.
339
+ '''
340
+ l2 = _MODS.osversion2
341
+ return sep.join(l2) if sep else l2 # 2-list()
342
+
343
+
344
+ def _passarg(arg):
345
+ '''(INTERNAL) Helper, no-op.
346
+ '''
347
+ return arg
348
+
349
+
350
+ def _passargs(*args):
351
+ '''(INTERNAL) Helper, no-op.
352
+ '''
353
+ return args
354
+
355
+
356
+ def _plural(noun, n):
357
+ '''(INTERNAL) Return C{noun}['s'] or C{NN}.
358
+ '''
359
+ return NN(noun, _s_) if n > 1 else (noun if n else NN)
360
+
361
+
362
+ def print_(*args, **nl_nt_prec_prefix__end_file_flush_sep_kwds): # PYCHOK no cover
363
+ '''Python 3+ C{print}-like formatting and printing.
364
+
365
+ @arg args: Values to be converted to C{str} and joined by B{C{sep}},
366
+ all positional.
367
+
368
+ @see: Function L{printf} for further details.
369
+ '''
370
+ return printf(NN, *args, **nl_nt_prec_prefix__end_file_flush_sep_kwds)
371
+
372
+
373
+ def printf(fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep_kwds):
374
+ '''C{Printf-style} and Python 3+ C{print}-like formatting and printing.
375
+
376
+ @arg fmt: U{Printf-style<https://Docs.Python.org/3/library/stdtypes.html#
377
+ printf-style-string-formatting>} format specification (C{str}).
378
+ @arg args: Arguments to be formatted (any C{type}, all positional).
379
+ @kwarg nl_nt_prec_prefix__end_file_flush_sep_kwds: Optional keyword arguments
380
+ C{B{nl}=0} for the number of leading blank lines (C{int}), C{B{nt}=0}
381
+ the number of trailing blank lines (C{int}), C{B{prefix}=NN} to be
382
+ inserted before the formatted text (C{str}) and Python 3+ C{print}
383
+ keyword arguments C{B{end}}, C{B{sep}}, C{B{file}} and C{B{flush}}.
384
+ Any remaining C{B{kwds}} are C{printf-style} name-value pairs to be
385
+ formatted, I{iff no B{C{args}} are present} using C{B{prec}=6} for
386
+ the number of decimal digits (C{int}).
387
+
388
+ @return: Number of bytes written.
389
+ '''
390
+ b, e, f, fl, p, s, kwds = _print7(**nl_nt_prec_prefix__end_file_flush_sep_kwds)
391
+ try:
392
+ if args:
393
+ t = (fmt % args) if fmt else s.join(map(str, args))
394
+ elif kwds:
395
+ t = (fmt % kwds) if fmt else s.join(
396
+ _MODS.streprs.pairs(kwds, prec=p))
397
+ else:
398
+ t = fmt
399
+ except Exception as x:
400
+ _E, s = _MODS.errors._xError2(x)
401
+ unstr = _MODS.streprs.unstr
402
+ t = unstr(printf, fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep_kwds)
403
+ raise _E(s, txt=t, cause=x)
404
+ try:
405
+ n = f.write(NN(b, t, e))
406
+ except UnicodeEncodeError: # XXX only Windows
407
+ t = t.replace('\u2032', _QUOTE1_).replace('\u2033', _QUOTE2_)
408
+ n = f.write(NN(b, t, e))
409
+ if fl: # PYCHOK no cover
410
+ f.flush()
411
+ return n
412
+
413
+
414
+ def _print7(nl=0, nt=0, prec=6, prefix=NN, sep=_SPACE_, file=_sys.stdout,
415
+ end=_NL_, flush=False, **kwds):
416
+ '''(INTERNAL) Unravel the C{printf} and remaining keyword arguments.
417
+ '''
418
+ if nl > 0:
419
+ prefix = NN(_NL_ * nl, prefix)
420
+ if nt > 0:
421
+ end = NN(end, _NL_ * nt)
422
+ return prefix, end, file, flush, prec, sep, kwds
423
+
424
+
425
+ def _Pythonarchine(sep=NN): # in .lazily, test/bases.py versions
426
+ '''(INTERNAL) Get PyPy and Python versions, bit and machine as C{3- or 4-list} or C{str}.
427
+ '''
428
+ l3 = _MODS.Pythonarchine
429
+ return sep.join(l3) if sep else l3 # 3- or 4-list
430
+
431
+
432
+ def _sizeof(obj):
433
+ '''(INTERNAL) Recursively size an C{obj}ect.
434
+
435
+ @return: The C{obj} size in bytes (C{int}),
436
+ ignoring class attributes and
437
+ counting duplicates only once or
438
+ C{None}.
439
+
440
+ @note: With C{PyPy}, the size is always C{None}.
441
+ '''
442
+ try:
443
+ _zB = _sys.getsizeof
444
+ _zD = _zB(None) # some default
445
+ except TypeError: # PyPy3.10
446
+ return None
447
+
448
+ _isiterablen = _MODS.basics.isiterablen
449
+
450
+ def _zR(s, iterable):
451
+ z, _s = 0, s.add
452
+ for o in iterable:
453
+ i = id(o)
454
+ if i not in s:
455
+ _s(i)
456
+ z += _zB(o, _zD)
457
+ if isinstance(o, dict):
458
+ z += _zR(s, o.keys())
459
+ z += _zR(s, o.values())
460
+ elif _isiterablen(o): # not map, ...
461
+ z += _zR(s, o)
462
+ else:
463
+ try: # size instance' attr values only
464
+ z += _zR(s, o.__dict__.values())
465
+ except AttributeError: # None, int, etc.
466
+ pass
467
+ return z
468
+
469
+ return _zR(set(), (obj,))
470
+
471
+
472
+ def _sysctl_uint(name):
473
+ '''(INTERNAL) Get an unsigned int sysctl item by name, use on macOS ONLY!
474
+ '''
475
+ libc = _MODS.libc
476
+ if libc: # <https://StackOverflow.com/questions/759892/python-ctypes-and-sysctl>
477
+ byref, char_p, size_t, uint, sizeof = _MODS.ctypes5
478
+ n = name if str is bytes else bytes(name, _utf_8_) # PYCHOK isPython2 = str is bytes
479
+ u = uint(0)
480
+ z = size_t(sizeof(u))
481
+ r = libc.sysctlbyname(char_p(n), byref(u), byref(z), None, size_t(0))
482
+ else: # could find or load 'libc'
483
+ r = -2
484
+ return int(r if r else u.value) # -1 ENOENT error, -2 no libc
485
+
486
+
487
+ def _tailof(name):
488
+ '''(INTERNAL) Get the base name of qualified C{name} or the C{name}.
489
+ '''
490
+ i = name.rfind(_DOT_) + 1
491
+ return name[i:] if i > 0 else name
492
+
493
+
494
+ def _under(name): # PYCHOK in .datums, .auxilats, .ups, .utm, .utmupsBase, ...
495
+ '''(INTERNAL) Prefix C{name} with an I{underscore}.
496
+ '''
497
+ return name if name.startswith(_UNDER_) else NN(_UNDER_, name)
498
+
499
+
500
+ def _usage(file_py, *args): # in .etm
501
+ '''(INTERNAL) Build "usage: python -m ..." cmd line for module B{C{file_py}}.
502
+ '''
503
+ m = _os_path.dirname(file_py).replace(_os.getcwd(), _ELLIPSIS_) \
504
+ .replace(_os.sep, _DOT_).strip()
505
+ b, x = _os_path.splitext(_os_path.basename(file_py))
506
+ if x == '.py' and not _dunder_ismain(b):
507
+ m = _DOT_(m or _pygeodesy_, b)
508
+ p = NN(_python_, _sys.version_info[0])
509
+ u = _COLON_(_dunder_nameof(_usage)[1:], NN)
510
+ return _SPACE_(u, p, '-m', _enquote(m), *args)
511
+
512
+
513
+ def _version2(version, n=2):
514
+ '''(INTERNAL) Split C{B{version} str} into a C{1-, 2- or 3-tuple} of C{int}s.
515
+ '''
516
+ t = _version_ints(version.split(_DOT_, 2))
517
+ if len(t) < n:
518
+ t += (0,) * n
519
+ return t[:n]
520
+
521
+
522
+ def _version_info(package): # in .Base.karney, .basics
523
+ '''(INTERNAL) Get the C{package.__version_info__} as a 2- or
524
+ 3-tuple C{(major, minor, revision)} if C{int}s.
525
+ '''
526
+ try:
527
+ return _version_ints(package.__version_info__)
528
+ except AttributeError:
529
+ return _version2(package.__version__.strip(), n=3)
530
+
531
+
532
+ def _version_ints(vs):
533
+ # helper for _version2 and _version_info above
534
+
535
+ def _ints(vs):
536
+ for v in vs:
537
+ try:
538
+ yield int(v.strip())
539
+ except (TypeError, ValueError):
540
+ pass
541
+
542
+ return tuple(_ints(vs))
543
+
544
+
545
+ __all__ = tuple(map(_dunder_nameof, (machine, print_, printf)))
546
+ __version__ = '24.05.21'
547
+
548
+ if _dunder_ismain(__name__): # PYCHOK no cover
549
+
550
+ from pygeodesy import _isfrozen, isLazy, version as vs
551
+
552
+ print_(_pygeodesy_, vs, *(_Pythonarchine() + _osversion2()
553
+ + ['isfrozen', _isfrozen,
554
+ 'isLazy', isLazy]))
555
+
556
+ # **) MIT License
557
+ #
558
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
559
+ #
560
+ # Permission is hereby granted, free of charge, to any person obtaining a
561
+ # copy of this software and associated documentation files (the "Software"),
562
+ # to deal in the Software without restriction, including without limitation
563
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
564
+ # and/or sell copies of the Software, and to permit persons to whom the
565
+ # Software is furnished to do so, subject to the following conditions:
566
+ #
567
+ # The above copyright notice and this permission notice shall be included
568
+ # in all copies or substantial portions of the Software.
569
+ #
570
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
571
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
572
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
573
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
574
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
575
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
576
+ # OTHER DEALINGS IN THE SOFTWARE.