pygeodesy 24.3.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 (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
pygeodesy/props.py ADDED
@@ -0,0 +1,628 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Mutable, immutable and caching/memoizing properties and
5
+ deprecation decorators.
6
+
7
+ To enable C{DeprecationWarning}s from C{PyGeodesy}, set env var
8
+ C{PYGEODESY_WARNINGS} to a non-empty string I{AND} run C{python}
9
+ with command line option C{-X dev} or with one of the C{-W}
10
+ choices, see callable L{DeprecationWarnings} below.
11
+ '''
12
+
13
+ from pygeodesy.basics import isclass as _isclass # _MODS
14
+ from pygeodesy.errors import _AssertionError, _AttributeError, \
15
+ _xkwds, _xkwds_get
16
+ from pygeodesy.interns import MISSING, NN, _an_, _COMMASPACE_, \
17
+ _DEPRECATED_, _DOT_, _EQUALSPACED_, \
18
+ _immutable_, _invalid_, _module_, _N_A_, \
19
+ _not_, _SPACE_, _UNDER_, _DNL_ # PYCHOK used!
20
+ # from pygeodesy.named import callname # _MODS, avoid circular
21
+ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, \
22
+ _FOR_DOCS, _WARNINGS_X_DEV
23
+ # from pygeodesy.streprs import Fmt # _MODS
24
+
25
+ from functools import wraps as _wraps
26
+
27
+ __all__ = _ALL_LAZY.props
28
+ __version__ = '24.03.06'
29
+
30
+ _class_ = 'class'
31
+ _dont_use_ = _DEPRECATED_ + ", don't use."
32
+ _function_ = 'function'
33
+ _get_and_set_ = 'get and set'
34
+ _has_been_ = 'has been' # PYCHOK used!
35
+ _method_ = 'method'
36
+ _not_an_inst_ = _not_(_an_, 'instance')
37
+
38
+
39
+ def _allPropertiesOf(Clas_or_inst, *Bases):
40
+ '''(INTERNAL) Yield all C{R/property/_RO}s at C{Clas_or_inst}
41
+ as specified in the C{Bases} arguments.
42
+ '''
43
+ if _isclass(Clas_or_inst):
44
+ S = Clas_or_inst, # just this Clas
45
+ else: # class and super-classes of inst
46
+ try:
47
+ S = Clas_or_inst.__class__.__mro__[:-1] # not object
48
+ except AttributeError:
49
+ raise
50
+ S = () # not an inst
51
+ B = Bases or _PropertyBase
52
+ for C in S:
53
+ for n, p in C.__dict__.items():
54
+ if isinstance(p, B) and p.name == n:
55
+ yield p
56
+
57
+
58
+ def _allPropertiesOf_n(n, Clas_or_inst, *Bases):
59
+ '''(INTERNAL) Assert the number of C{R/property/_RO}s at C{Clas_or_inst}.
60
+ '''
61
+ t = tuple(p.name for p in _allPropertiesOf(Clas_or_inst, *Bases))
62
+ if len(t) != n:
63
+ raise _AssertionError(_COMMASPACE_.join(t), Clas_or_inst,
64
+ txt=_COMMASPACE_(len(t), _not_(n)))
65
+ return t
66
+
67
+
68
+ def _hasProperty(inst, name, *Classes): # in .named._NamedBase._update
69
+ '''(INTERNAL) Check whether C{inst} has a C{P/property/_RO} by this C{name}.
70
+ '''
71
+ p = getattr(inst.__class__, name, None) # walks __class__.__mro__
72
+ return bool(p and isinstance(p, Classes or _PropertyBase)
73
+ and p.name == name)
74
+
75
+
76
+ # def _isclass(obj):
77
+ # '''(INTERNAL) Get and overwrite C{_isclass}.
78
+ # '''
79
+ # _MODS.getmodule(__name__)._isclass = f = _MODS.basics.isclass
80
+ # return f(obj)
81
+
82
+
83
+ def _update_all(inst, *attrs, **Base_needed):
84
+ '''(INTERNAL) Zap all I{cached} L{property_RO}s, L{Property}s,
85
+ L{Property_RO}s and the named C{attrs} of an instance.
86
+
87
+ @return: The number of updates (C{int}), if any.
88
+ '''
89
+ if _isclass(inst):
90
+ raise _AssertionError(inst, txt=_not_an_inst_)
91
+ try:
92
+ d = inst.__dict__
93
+ except AttributeError:
94
+ return 0
95
+ u = len(d)
96
+ if u > _xkwds_get(Base_needed, needed=0):
97
+ B = _xkwds_get(Base_needed, Base=_PropertyBase)
98
+ for p in _allPropertiesOf(inst, B):
99
+ p._update(inst) # d.pop(p.name, None)
100
+
101
+ if attrs:
102
+ _update_attrs(inst, *attrs) # remove attributes from inst.__dict__
103
+ u -= len(d)
104
+ return u # updates
105
+
106
+
107
+ # def _update_all_from(inst, other, **Base):
108
+ # '''(INTERNAL) Update all I{cached} L{Property}s and
109
+ # L{Property_RO}s of instance C{inst} from C{other}.
110
+ #
111
+ # @return: The number of updates (C{int}), if any.
112
+ # '''
113
+ # if _isclass(inst):
114
+ # raise _AssertionError(inst, txt=_not_an_inst_)
115
+ # try:
116
+ # d = inst.__dict__
117
+ # f = other.__dict__
118
+ # except AttributeError:
119
+ # return 0
120
+ # u = len(f)
121
+ # if u:
122
+ # u = len(d)
123
+ # B = _xkwds_get(Base, Base=_PropertyBase)
124
+ # for p in _allPropertiesOf(inst, B):
125
+ # p._update_from(inst, other)
126
+ # u -= len(d)
127
+ # return u # number of updates
128
+
129
+
130
+ def _update_attrs(inst, *attrs):
131
+ '''(INTERNAL) Zap all named C{attrs} of an instance.
132
+
133
+ @return: The number of updates (C{int}), if any.
134
+ '''
135
+ try:
136
+ d = inst.__dict__
137
+ except AttributeError:
138
+ return 0
139
+ u = len(d)
140
+ if u: # zap attrs from inst.__dict__
141
+ _p = d.pop
142
+ for a in attrs:
143
+ _ = _p(a, MISSING)
144
+ # if _ is MISSING and not hasattr(inst, a):
145
+ # n = _MODS.named.classname(inst, prefixed=True)
146
+ # a = _DOT_(n, _SPACE_(a, _invalid_))
147
+ # raise _AssertionError(a, txt=repr(inst))
148
+ # _ = _p(a, None) # redo: hasattr side effect
149
+ u -= len(d)
150
+ # assert u >= 0
151
+ return u # number of named C{attrs} zapped
152
+
153
+
154
+ class _PropertyBase(property):
155
+ '''(INTERNAL) Base class for C{P/property/_RO}.
156
+ '''
157
+ def __init__(self, method, fget, fset, doc=NN):
158
+
159
+ if not callable(method):
160
+ self.getter(method) # PYCHOK no cover
161
+
162
+ self.method = method
163
+ self.name = method.__name__
164
+ d = doc or method.__doc__
165
+ if _FOR_DOCS and d:
166
+ self.__doc__ = d # PYCHOK no cover
167
+
168
+ property.__init__(self, fget, fset, self._fdel, d or _N_A_)
169
+
170
+ def _Error(self, kind, nameter, farg):
171
+ '''(INTERNAL) Return an C{AttributeError} instance.
172
+ '''
173
+ if farg:
174
+ n = _DOT_(self.name, nameter.__name__)
175
+ n = _SPACE_(n, farg.__name__)
176
+ else:
177
+ n = nameter
178
+ e = _SPACE_(kind, _MODS.named.classname(self))
179
+ return _AttributeError(e, txt=n)
180
+
181
+ def _fdel(self, inst):
182
+ '''Zap the I{cached/memoized} C{property} value.
183
+ '''
184
+ self._update(inst, None) # PYCHOK no cover
185
+
186
+ def _fget(self, inst):
187
+ '''Get and I{cache/memoize} the C{property} value.
188
+ '''
189
+ try: # to get the value cached in instance' __dict__
190
+ return inst.__dict__[self.name]
191
+ except KeyError:
192
+ # cache the value in the instance' __dict__
193
+ inst.__dict__[self.name] = val = self.method(inst)
194
+ return val
195
+
196
+ def _fset_error(self, inst, val):
197
+ '''Throws an C{AttributeError}, always.
198
+ '''
199
+ n = _MODS.named.classname(inst)
200
+ n = _DOT_(n, self.name)
201
+ n = _EQUALSPACED_(n, repr(val))
202
+ raise self._Error(_immutable_, n, None)
203
+
204
+ def _update(self, inst, *unused):
205
+ '''(INTERNAL) Zap the I{cached/memoized} C{inst.__dict__[name]} item.
206
+ '''
207
+ inst.__dict__.pop(self.name, None) # name, NOT _name
208
+
209
+ def _update_from(self, inst, other):
210
+ '''(INTERNAL) Copy a I{cached/memoized} C{inst.__dict__[name]} item
211
+ from C{other.__dict__[name]} if present, otherwise zap it.
212
+ '''
213
+ n = self.name # name, NOT _name
214
+ v = other.__dict__.get(n, MISSING)
215
+ if v is MISSING:
216
+ inst.__dict__.pop(n, None)
217
+ else:
218
+ inst.__dict__[n] = v
219
+
220
+ def deleter(self, fdel):
221
+ '''Throws an C{AttributeError}, always.
222
+ '''
223
+ raise self._Error(_invalid_, self.deleter, fdel)
224
+
225
+ def getter(self, fget):
226
+ '''Throws an C{AttributeError}, always.
227
+ '''
228
+ raise self._Error(_invalid_, self.getter, fget)
229
+
230
+ def setter(self, fset):
231
+ '''Throws an C{AttributeError}, always.
232
+ '''
233
+ raise self._Error(_immutable_, self.setter, fset)
234
+
235
+
236
+ class Property_RO(_PropertyBase):
237
+ # No __doc__ on purpose
238
+ def __init__(self, method, doc=NN): # PYCHOK expected
239
+ '''New I{immutable}, I{caching}, I{memoizing} C{property} I{Factory}
240
+ to be used as C{decorator}.
241
+
242
+ @arg method: The callable being decorated as this C{property}'s C{getter},
243
+ to be invoked only once.
244
+ @kwarg doc: Optional property documentation (C{str}).
245
+
246
+ @note: Like standard Python C{property} without a C{setter}, but with
247
+ a more descriptive error message when set.
248
+
249
+ @see: Python 3's U{functools.cached_property<https://docs.Python.org/3/
250
+ library/functools.html#functools.cached_property>} and U{-.cache
251
+ <https://Docs.Python.org/3/library/functools.html#functools.cache>}
252
+ to I{cache} or I{memoize} the property value.
253
+
254
+ @see: Luciano Ramalho, "Fluent Python", page 636, O'Reilly, Example
255
+ 19-24, 2016 p. 636 or Example 22-28, 2022 p. 869+ and U{class
256
+ Property<https://docs.Python.org/3/howto/descriptor.html>}.
257
+ '''
258
+ _fget = method if _FOR_DOCS else self._fget # XXX force method.__doc__ to epydoc
259
+ _PropertyBase.__init__(self, method, _fget, self._fset_error)
260
+
261
+ def __get__(self, inst, *unused): # PYCHOK 2 vs 3 args
262
+ if inst is None:
263
+ return self
264
+ try: # to get the cached value immediately
265
+ return inst.__dict__[self.name]
266
+ except (AttributeError, KeyError):
267
+ return self._fget(inst)
268
+
269
+
270
+ class Property(Property_RO):
271
+ # No __doc__ on purpose
272
+ __init__ = Property_RO.__init__
273
+ '''New I{mutable}, I{caching}, I{memoizing} C{property} I{Factory}
274
+ to be used as C{decorator}.
275
+
276
+ @see: L{Property_RO} for more details.
277
+
278
+ @note: Unless and until the C{setter} is defined, this L{Property} behaves
279
+ like an I{immutable}, I{caching}, I{memoizing} L{Property_RO}.
280
+ '''
281
+
282
+ def setter(self, method):
283
+ '''Make this C{Property} I{mutable}.
284
+
285
+ @arg method: The callable being decorated as this C{Property}'s C{setter}.
286
+
287
+ @note: Setting a new property value always clears the previously I{cached}
288
+ or I{memoized} value I{after} invoking the B{C{method}}.
289
+ '''
290
+ if not callable(method):
291
+ _PropertyBase.setter(self, method) # PYCHOK no cover
292
+
293
+ if _FOR_DOCS: # XXX force method.__doc__ into epydoc
294
+ _PropertyBase.__init__(self, self.method, self.method, method)
295
+ else:
296
+
297
+ def _fset(inst, val):
298
+ '''Set and I{cache}, I{memoize} the C{property} value.
299
+ '''
300
+ method(inst, val)
301
+ self._update(inst) # un-cache this item
302
+
303
+ # class Property <https://docs.Python.org/3/howto/descriptor.html>
304
+ _PropertyBase.__init__(self, self.method, self._fget, _fset)
305
+ return self
306
+
307
+
308
+ class property_RO(_PropertyBase):
309
+ # No __doc__ on purpose
310
+ _uname = NN
311
+
312
+ def __init__(self, method, doc=NN): # PYCHOK expected
313
+ '''New I{immutable}, standard C{property} to be used as C{decorator}.
314
+
315
+ @arg method: The callable being decorated as C{property}'s C{getter}.
316
+ @kwarg doc: Optional property documentation (C{str}).
317
+
318
+ @note: Like standard Python C{property} without a setter, but with
319
+ a more descriptive error message when set.
320
+
321
+ @see: L{Property_RO}.
322
+ '''
323
+ _PropertyBase.__init__(self, method, method, self._fset_error, doc=doc)
324
+ self._uname = NN(_UNDER_, self.name) # actual attr UNDER<name>
325
+
326
+ def _update(self, inst, *Clas): # PYCHOK signature
327
+ '''(INTERNAL) Zap the I{cached} C{B{inst}.__dict__[_name]} item.
328
+ '''
329
+ uname = self._uname
330
+ if uname in inst.__dict__:
331
+ if Clas: # overrides inst.__class__
332
+ d = Clas[0].__dict__.get(uname, MISSING)
333
+ else:
334
+ d = getattr(inst.__class__, uname, MISSING)
335
+ # if d is MISSING: # XXX superfluous
336
+ # for c in inst.__class__.__mro__[:-1]:
337
+ # if uname in c.__dict__:
338
+ # d = c.__dict__[uname]
339
+ # break
340
+ if d is None: # remove inst value
341
+ inst.__dict__.pop(uname)
342
+
343
+
344
+ class _NamedProperty(property):
345
+ '''Class C{property} with retrievable name.
346
+ '''
347
+ @Property_RO
348
+ def name(self):
349
+ '''Get the name of this C{property} (C{str}).
350
+ '''
351
+ return self.fget.__name__
352
+
353
+
354
+ def property_doc_(doc):
355
+ '''Decorator for a standard C{property} with basic documentation.
356
+
357
+ @arg doc: The property documentation (C{str}).
358
+
359
+ @example:
360
+
361
+ >>> @property_doc_("documentation text.")
362
+ >>> def name(self):
363
+ >>> ...
364
+ >>>
365
+ >>> @name.setter
366
+ >>> def name(self, value):
367
+ >>> ...
368
+ '''
369
+ # See Luciano Ramalho, "Fluent Python", O'Reilly, Example 7-23,
370
+ # 2016 p. 212+, 2022 p. 331+, Example 9-22 and <https://
371
+ # Python-3-Patterns-Idioms-Test.ReadTheDocs.io/en/latest/PythonDecorators.html>
372
+
373
+ def _documented_property(method):
374
+ '''(INTERNAL) Return the documented C{property}.
375
+ '''
376
+ t = _get_and_set_ if doc.startswith(_SPACE_) else NN
377
+ return _NamedProperty(method, None, None, NN('Property to ', t, doc))
378
+
379
+ return _documented_property
380
+
381
+
382
+ def _deprecated(call, kind, qual_d):
383
+ '''(INTERNAL) Decorator for DEPRECATED functions, methods, etc.
384
+
385
+ @see: Brett Slatkin, "Effective Python", page 105, 2nd ed,
386
+ Addison-Wesley, 2019.
387
+ '''
388
+ doc = _docof(call)
389
+
390
+ @_wraps(call) # PYCHOK self?
391
+ def _deprecated_call(*args, **kwds):
392
+ if qual_d: # function
393
+ q = qual_d
394
+ elif args: # method
395
+ q = _qualified(args[0], call.__name__)
396
+ else: # PYCHOK no cover
397
+ q = call.__name__
398
+ _throwarning(kind, q, doc)
399
+ return call(*args, **kwds)
400
+
401
+ return _deprecated_call
402
+
403
+
404
+ def deprecated_class(cls_or_class):
405
+ '''Use inside __new__ or __init__ of a DEPRECATED class.
406
+
407
+ @arg cls_or_class: The class (C{cls} or C{Class}).
408
+
409
+ @note: NOT a decorator!
410
+ '''
411
+ if _WARNINGS_X_DEV:
412
+ q = _DOT_(cls_or_class.__module__, cls_or_class.__name__)
413
+ _throwarning(_class_, q, cls_or_class.__doc__)
414
+
415
+
416
+ def deprecated_function(call):
417
+ '''Decorator for a DEPRECATED function.
418
+
419
+ @arg call: The deprecated function (C{callable}).
420
+
421
+ @return: The B{C{call}} DEPRECATED.
422
+ '''
423
+ return _deprecated(call, _function_, _DOT_(
424
+ call.__module__, call.__name__)) if \
425
+ _WARNINGS_X_DEV else call
426
+
427
+
428
+ def deprecated_method(call):
429
+ '''Decorator for a DEPRECATED method.
430
+
431
+ @arg call: The deprecated method (C{callable}).
432
+
433
+ @return: The B{C{call}} DEPRECATED.
434
+ '''
435
+ return _deprecated(call, _method_, NN) if _WARNINGS_X_DEV else call
436
+
437
+
438
+ def _deprecated_module(name): # PYCHOK no cover
439
+ '''(INTERNAL) Callable within a DEPRECATED module.
440
+ '''
441
+ if _WARNINGS_X_DEV:
442
+ _throwarning(_module_, name, _dont_use_)
443
+
444
+
445
+ if _WARNINGS_X_DEV:
446
+ class deprecated_property(_PropertyBase):
447
+ '''Decorator for a DEPRECATED C{property} or C{Property}.
448
+ '''
449
+ def __init__(self, method):
450
+ '''Decorator for a DEPRECATED C{property} or C{Property} getter.
451
+ '''
452
+ doc = _docof(method)
453
+
454
+ def _fget(inst): # PYCHOK no cover
455
+ '''Get the C{property} or C{Property} value.
456
+ '''
457
+ q = _qualified(inst, self.name)
458
+ _throwarning(property.__name__, q, doc)
459
+ return self.method(inst) # == method
460
+
461
+ _PropertyBase.__init__(self, method, _fget, None, doc=doc)
462
+
463
+ def setter(self, method):
464
+ '''Decorator for a DEPRECATED C{property} or C{Property} setter.
465
+
466
+ @arg method: The callable being decorated as this C{Property}'s C{setter}.
467
+
468
+ @note: Setting a new property value always clears the previously I{cached}
469
+ or I{memoized} value I{after} invoking the B{C{method}}.
470
+ '''
471
+ if not callable(method):
472
+ _PropertyBase.setter(self, method) # PYCHOK no cover
473
+
474
+ if _FOR_DOCS: # XXX force method.__doc__ into epydoc
475
+ _PropertyBase.__init__(self, self.method, self.method, method)
476
+ else:
477
+
478
+ def _fset(inst, val):
479
+ '''Set the C{property} or C{Property} value.
480
+ '''
481
+ q = _qualified(inst, self.name)
482
+ _throwarning(property.__name__, q, _docof(method))
483
+ method(inst, val)
484
+ # self._update(inst) # un-cache this item
485
+
486
+ # class Property <https://docs.Python.org/3/howto/descriptor.html>
487
+ _PropertyBase.__init__(self, self.method, self._fget, _fset)
488
+ return self
489
+
490
+ else: # PYCHOK no cover
491
+ class deprecated_property(property): # PYCHOK expected
492
+ '''Decorator for a DEPRECATED C{property} or C{Property}.
493
+ '''
494
+ pass
495
+
496
+ deprecated_Property = deprecated_property
497
+
498
+
499
+ def deprecated_Property_RO(method):
500
+ '''Decorator for a DEPRECATED L{Property_RO}.
501
+
502
+ @arg method: The C{Property_RO.fget} method (C{callable}).
503
+
504
+ @return: The B{C{method}} DEPRECATED.
505
+ '''
506
+ return _deprecated_RO(method, Property_RO)
507
+
508
+
509
+ def deprecated_property_RO(method):
510
+ '''Decorator for a DEPRECATED L{property_RO}.
511
+
512
+ @arg method: The C{property_RO.fget} method (C{callable}).
513
+
514
+ @return: The B{C{method}} DEPRECATED.
515
+ '''
516
+ return _deprecated_RO(method, property_RO)
517
+
518
+
519
+ def _deprecated_RO(method, _RO):
520
+ '''(INTERNAL) Create a DEPRECATED C{property_RO} or C{Property_RO}.
521
+ '''
522
+ doc = _docof(method)
523
+
524
+ if _WARNINGS_X_DEV:
525
+
526
+ class _Deprecated_RO(_PropertyBase):
527
+ __doc__ = doc
528
+
529
+ def __init__(self, method):
530
+ _PropertyBase.__init__(self, method, self._fget, self._fset_error, doc=doc)
531
+
532
+ def _fget(self, inst): # PYCHOK no cover
533
+ q = _qualified(inst, self.name)
534
+ _throwarning(_RO.__name__, q, doc)
535
+ return self.method(inst)
536
+
537
+ return _Deprecated_RO(method)
538
+ else: # PYCHOK no cover
539
+ return _RO(method, doc=doc)
540
+
541
+
542
+ def _docof(obj):
543
+ '''(INTERNAL) Get uniform DEPRECATED __doc__ string.
544
+ '''
545
+ try:
546
+ d = obj.__doc__.strip()
547
+ i = d.find(_DEPRECATED_)
548
+ except AttributeError:
549
+ i = -1
550
+ return _DOT_(_DEPRECATED_, NN) if i < 0 else d[i:]
551
+
552
+
553
+ def _qualified(inst, name):
554
+ '''(INTERNAL) Fully qualify a name.
555
+ '''
556
+ # _DOT_(inst.classname, name), not _DOT_(inst.named4, name)
557
+ c = inst.__class__
558
+ q = _DOT_(c.__module__, c.__name__, name)
559
+ return q
560
+
561
+
562
+ class DeprecationWarnings(object):
563
+ '''(INTERNAL) Handle C{DeprecationWaring}s.
564
+ '''
565
+ _Warnings = 0
566
+
567
+ def __call__(self): # for backward compatibility
568
+ '''Have any C{DeprecationWarning}s been reported or raised?
569
+
570
+ @return: The number of C{DeprecationWarning}s (C{int}) so
571
+ far or C{None} if not enabled.
572
+
573
+ @note: To get C{DeprecationWarning}s if any, run C{python}
574
+ with env var C{PYGEODESY_WARNINGS} set to a non-empty
575
+ string I{AND} use C{python[3]} command line option
576
+ C{-X dev}, C{-W always} or C{-W error}, etc.
577
+ '''
578
+ return self.Warnings
579
+
580
+ def throw(self, kind, name, doc, **stacklevel): # stacklevel=3
581
+ '''Report or raise a C{DeprecationWarning}.
582
+ '''
583
+ line = doc.split(_DNL_, 1)[0].strip()
584
+ name = _MODS.streprs.Fmt.CURLY(L=name)
585
+ text = _SPACE_(kind, name, _has_been_, *line.split())
586
+ kwds = _xkwds(stacklevel, stacklevel=3)
587
+ # XXX invoke warn or raise DeprecationWarning(text)
588
+ self._warn(text, category=DeprecationWarning, **kwds)
589
+ self._Warnings += 1
590
+
591
+ @Property_RO
592
+ def _warn(self):
593
+ '''Get Python's C{warnings.warn}.
594
+ '''
595
+ from warnings import warn
596
+ return warn
597
+
598
+ @property_RO
599
+ def Warnings(self):
600
+ '''Get the number of C{DeprecationWarning}s (C{int}) so
601
+ far or C{None} if not enabled.
602
+ '''
603
+ return self._Warnings if _WARNINGS_X_DEV else None
604
+
605
+ DeprecationWarnings = DeprecationWarnings() # PYCHOK singleton
606
+ _throwarning = DeprecationWarnings.throw
607
+
608
+ # **) MIT License
609
+ #
610
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
611
+ #
612
+ # Permission is hereby granted, free of charge, to any person obtaining a
613
+ # copy of this software and associated documentation files (the "Software"),
614
+ # to deal in the Software without restriction, including without limitation
615
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
616
+ # and/or sell copies of the Software, and to permit persons to whom the
617
+ # Software is furnished to do so, subject to the following conditions:
618
+ #
619
+ # The above copyright notice and this permission notice shall be included
620
+ # in all copies or substantial portions of the Software.
621
+ #
622
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
623
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
624
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
625
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
626
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
627
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
628
+ # OTHER DEALINGS IN THE SOFTWARE.