pyxllib 0.3.197__py3-none-any.whl → 0.3.200__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 (126) hide show
  1. pyxllib/__init__.py +21 -21
  2. pyxllib/algo/__init__.py +8 -8
  3. pyxllib/algo/disjoint.py +54 -54
  4. pyxllib/algo/geo.py +541 -541
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -389
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -629
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -241
  11. pyxllib/algo/stat.py +494 -494
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. pyxllib/autogui/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -246
  16. pyxllib/autogui/all.py +9 -9
  17. pyxllib/autogui/autogui.py +852 -852
  18. pyxllib/autogui/uiautolib.py +362 -362
  19. pyxllib/autogui/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -827
  21. pyxllib/autogui/wechat_msg.py +421 -421
  22. pyxllib/autogui/wxautolib.py +84 -84
  23. pyxllib/cv/__init__.py +5 -5
  24. pyxllib/cv/expert.py +267 -267
  25. pyxllib/cv/imfile.py +159 -159
  26. pyxllib/cv/imhash.py +39 -39
  27. pyxllib/cv/pupil.py +9 -9
  28. pyxllib/cv/rgbfmt.py +1525 -1525
  29. pyxllib/cv/slidercaptcha.py +137 -137
  30. pyxllib/cv/trackbartools.py +251 -251
  31. pyxllib/cv/xlcvlib.py +1040 -1040
  32. pyxllib/cv/xlpillib.py +423 -423
  33. pyxllib/data/echarts.py +240 -240
  34. pyxllib/data/jsonlib.py +89 -89
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -1127
  37. pyxllib/data/sqlite.py +568 -568
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -505
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -277
  43. pyxllib/ext/kq5034lib.py +12 -12
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -497
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -827
  50. pyxllib/ext/utools.py +351 -351
  51. pyxllib/ext/webhook.py +124 -119
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -88
  54. pyxllib/ext/wpsapi.py +124 -124
  55. pyxllib/ext/xlwork.py +9 -9
  56. pyxllib/ext/yuquelib.py +1105 -1105
  57. pyxllib/file/__init__.py +17 -17
  58. pyxllib/file/docxlib.py +761 -761
  59. pyxllib/file/gitlib.py +309 -309
  60. pyxllib/file/libreoffice.py +165 -165
  61. pyxllib/file/movielib.py +148 -148
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -330
  65. pyxllib/file/packlib/zipfile.py +2441 -2441
  66. pyxllib/file/pdflib.py +426 -426
  67. pyxllib/file/pupil.py +185 -185
  68. pyxllib/file/specialist/__init__.py +685 -685
  69. pyxllib/file/specialist/dirlib.py +799 -799
  70. pyxllib/file/specialist/download.py +193 -193
  71. pyxllib/file/specialist/filelib.py +2829 -2829
  72. pyxllib/file/xlsxlib.py +3131 -3131
  73. pyxllib/file/xlsyncfile.py +341 -341
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -64
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -42
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -940
  80. pyxllib/prog/newbie.py +451 -451
  81. pyxllib/prog/pupil.py +1197 -1197
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -391
  84. pyxllib/prog/specialist/bc.py +203 -203
  85. pyxllib/prog/specialist/browser.py +497 -497
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +198 -198
  88. pyxllib/prog/specialist/tictoc.py +240 -240
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -108
  91. pyxllib/stdlib/__init__.py +17 -17
  92. pyxllib/stdlib/tablepyxl/__init__.py +10 -10
  93. pyxllib/stdlib/tablepyxl/style.py +303 -303
  94. pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
  95. pyxllib/text/__init__.py +8 -8
  96. pyxllib/text/ahocorasick.py +39 -39
  97. pyxllib/text/airscript.js +744 -744
  98. pyxllib/text/charclasslib.py +121 -121
  99. pyxllib/text/jiebalib.py +267 -267
  100. pyxllib/text/jinjalib.py +32 -32
  101. pyxllib/text/jsa_ai_prompt.md +271 -271
  102. pyxllib/text/jscode.py +922 -922
  103. pyxllib/text/latex/__init__.py +158 -158
  104. pyxllib/text/levenshtein.py +303 -303
  105. pyxllib/text/nestenv.py +1215 -1215
  106. pyxllib/text/newbie.py +300 -300
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1121
  109. pyxllib/text/pupil/xlalign.py +326 -326
  110. pyxllib/text/pycode.py +47 -47
  111. pyxllib/text/specialist/__init__.py +8 -8
  112. pyxllib/text/specialist/common.py +112 -112
  113. pyxllib/text/specialist/ptag.py +186 -186
  114. pyxllib/text/spellchecker.py +172 -172
  115. pyxllib/text/templates/echart_base.html +10 -10
  116. pyxllib/text/templates/highlight_code.html +16 -16
  117. pyxllib/text/templates/latex_editor.html +102 -102
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -747
  120. pyxllib/xl.py +42 -39
  121. pyxllib/xlcv.py +17 -17
  122. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/METADATA +1 -1
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/licenses/LICENSE +190 -190
  125. pyxllib-0.3.197.dist-info/RECORD +0 -126
  126. {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +0 -0
pyxllib/prog/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2021/06/03 21:14
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/06/03 21:14
@@ -1,64 +1,64 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2024/05/29
6
-
7
- from pyxllib.prog.pupil import check_install_package
8
-
9
- # cachetools,通用缓存工具,适用范围更广
10
- check_install_package('cachetools')
11
- # cached-property,类属性特用工具,相对比较简洁些
12
- check_install_package('cached_property', 'cached-property')
13
-
14
- # 对于普通函数,一般用lru_cache即可
15
- from functools import lru_cache
16
- import threading
17
-
18
- from cachetools import cached, LRUCache, TTLCache
19
-
20
- # 官方文档:https://pypi.org/project/cached-property/
21
- from cached_property import cached_property
22
- from cached_property import threaded_cached_property # 线程安全
23
- from cached_property import cached_property_with_ttl # 限时缓存,单位秒
24
- from cached_property import threaded_cached_property_with_ttl # 线程 + 限时
25
-
26
-
27
- # todo 240609周日21:19 https://github.com/awolverp/cachebox,据说这个缓存库速度更快的多
28
-
29
- # 进一步封装的更通用、自己常用的装饰器
30
-
31
- def xlcache(maxsize=128, *, ttl=None, lock=None, property=False):
32
- """ 那些工具接口太复杂难记,自己封装一个统一的工具
33
-
34
- 就是一个装饰器,最大缓存多少项,然后是否要开多线程安全,是否要设置限时重置,是否是作为类成员属性修饰
35
-
36
- :param property: 是否作为类成员属性修饰,不过一般不建议通过这里设置,
37
- 而是外部再加一层@property,不然IDE会识别不了这是一个property,影响开发
38
-
39
- """
40
- def decorator(func):
41
- if property:
42
- if ttl is not None:
43
- if lock:
44
- # 使用带有时间限制和线程安全的缓存属性
45
- return threaded_cached_property_with_ttl(ttl)(func)
46
- else:
47
- # 使用带有时间限制但非线程安全的缓存属性
48
- return cached_property_with_ttl(ttl)(func)
49
- else:
50
- if lock:
51
- # 使用线程安全的缓存属性
52
- return threaded_cached_property(func)
53
- else:
54
- # 使用普通的缓存属性
55
- return cached_property(func)
56
- else:
57
- lock2 = threading.RLock() if lock is True else lock
58
- if ttl is None:
59
- return cached(LRUCache(maxsize), lock=lock2)(func)
60
- else:
61
- cache = TTLCache(maxsize=maxsize, ttl=ttl)
62
- return cached(cache, lock=lock2)(func)
63
-
64
- return decorator
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2024/05/29
6
+
7
+ from pyxllib.prog.pupil import check_install_package
8
+
9
+ # cachetools,通用缓存工具,适用范围更广
10
+ check_install_package('cachetools')
11
+ # cached-property,类属性特用工具,相对比较简洁些
12
+ check_install_package('cached_property', 'cached-property')
13
+
14
+ # 对于普通函数,一般用lru_cache即可
15
+ from functools import lru_cache
16
+ import threading
17
+
18
+ from cachetools import cached, LRUCache, TTLCache
19
+
20
+ # 官方文档:https://pypi.org/project/cached-property/
21
+ from cached_property import cached_property
22
+ from cached_property import threaded_cached_property # 线程安全
23
+ from cached_property import cached_property_with_ttl # 限时缓存,单位秒
24
+ from cached_property import threaded_cached_property_with_ttl # 线程 + 限时
25
+
26
+
27
+ # todo 240609周日21:19 https://github.com/awolverp/cachebox,据说这个缓存库速度更快的多
28
+
29
+ # 进一步封装的更通用、自己常用的装饰器
30
+
31
+ def xlcache(maxsize=128, *, ttl=None, lock=None, property=False):
32
+ """ 那些工具接口太复杂难记,自己封装一个统一的工具
33
+
34
+ 就是一个装饰器,最大缓存多少项,然后是否要开多线程安全,是否要设置限时重置,是否是作为类成员属性修饰
35
+
36
+ :param property: 是否作为类成员属性修饰,不过一般不建议通过这里设置,
37
+ 而是外部再加一层@property,不然IDE会识别不了这是一个property,影响开发
38
+
39
+ """
40
+ def decorator(func):
41
+ if property:
42
+ if ttl is not None:
43
+ if lock:
44
+ # 使用带有时间限制和线程安全的缓存属性
45
+ return threaded_cached_property_with_ttl(ttl)(func)
46
+ else:
47
+ # 使用带有时间限制但非线程安全的缓存属性
48
+ return cached_property_with_ttl(ttl)(func)
49
+ else:
50
+ if lock:
51
+ # 使用线程安全的缓存属性
52
+ return threaded_cached_property(func)
53
+ else:
54
+ # 使用普通的缓存属性
55
+ return cached_property(func)
56
+ else:
57
+ lock2 = threading.RLock() if lock is True else lock
58
+ if ttl is None:
59
+ return cached(LRUCache(maxsize), lock=lock2)(func)
60
+ else:
61
+ cache = TTLCache(maxsize=maxsize, ttl=ttl)
62
+ return cached(cache, lock=lock2)(func)
63
+
64
+ return decorator
@@ -1,233 +1,233 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2021/06/06 00:50
6
-
7
- """
8
- 原官方库: https://pypi.org/project/Deprecated
9
-
10
- 其deprecated装饰器的action='once'功能跟我设想的不一样
11
-
12
- 其实从实际使用角度看,默认的每个物理行只警告一次是最合理的模式,使用起来无伤大雅
13
- 但我非要把这个'once'情景的功能给补完善~~
14
-
15
- 从 deprecated.classic 文件改编而来
16
- """
17
-
18
- import functools
19
- import inspect
20
- import platform
21
- import warnings
22
-
23
- import wrapt
24
-
25
- try:
26
- # If the C extension for wrapt was compiled and wrapt/_wrappers.pyd exists, then the
27
- # stack level that should be passed to warnings.warn should be 2. However, if using
28
- # a pure python wrapt, a extra stacklevel is required.
29
- import wrapt._wrappers
30
-
31
- _routine_stacklevel = 2
32
- _class_stacklevel = 2
33
- except ImportError:
34
- _routine_stacklevel = 3
35
- if platform.python_implementation() == "PyPy":
36
- _class_stacklevel = 2
37
- else:
38
- _class_stacklevel = 3
39
-
40
- string_types = (type(b''), type(u''))
41
-
42
-
43
- class ClassicAdapter(wrapt.AdapterFactory):
44
- """
45
- Classic adapter -- *for advanced usage only*
46
-
47
- This adapter is used to get the deprecation message according to the wrapped object type:
48
- class, function, standard method, static method, or class method.
49
-
50
- This is the base class of the :class:`~deprecated.sphinx.SphinxAdapter` class
51
- which is used to update the wrapped object docstring.
52
-
53
- You can also inherit this class to change the deprecation message.
54
-
55
- In the following example, we change the message into "The ... is deprecated.":
56
-
57
- .. code-block:: python
58
-
59
- import inspect
60
-
61
- from deprecated.classic import ClassicAdapter
62
- from deprecated.classic import deprecated
63
-
64
-
65
- class MyClassicAdapter(ClassicAdapter):
66
- def get_deprecated_msg(self, wrapped, instance):
67
- if instance is None:
68
- if inspect.isclass(wrapped):
69
- fmt = "The class {name} is deprecated."
70
- else:
71
- fmt = "The function {name} is deprecated."
72
- else:
73
- if inspect.isclass(instance):
74
- fmt = "The class method {name} is deprecated."
75
- else:
76
- fmt = "The method {name} is deprecated."
77
- if self.reason:
78
- fmt += " ({reason})"
79
- if self.version:
80
- fmt += " -- Deprecated since version {version}."
81
- return fmt.format(name=wrapped.__name__,
82
- reason=self.reason or "",
83
- version=self.version or "")
84
-
85
- Then, you can use your ``MyClassicAdapter`` class like this in your source code:
86
-
87
- .. code-block:: python
88
-
89
- @deprecated(reason="use another function", adapter_cls=MyClassicAdapter)
90
- def some_old_function(x, y):
91
- return x + y
92
- """
93
-
94
- def __init__(self, reason="", version="", action=None, category=DeprecationWarning):
95
- """
96
- Construct a wrapper adapter.
97
-
98
- :type reason: str
99
- :param reason:
100
- Reason message which documents the deprecation in your library (can be omitted).
101
-
102
- :type version: str
103
- :param version:
104
- Version of your project which deprecates this feature.
105
- If you follow the `Semantic Versioning <https://semver.org/>`_,
106
- the version number has the format "MAJOR.MINOR.PATCH".
107
-
108
- :type action: str
109
- :param action:
110
- A warning filter used to activate or not the deprecation warning.
111
- Can be one of "error", "ignore", "always", "default", "module", or "once".
112
- If ``None`` or empty, the the global filtering mechanism is used.
113
- See: `The Warnings Filter`_ in the Python documentation.
114
-
115
- :type category: type
116
- :param category:
117
- The warning category to use for the deprecation warning.
118
- By default, the category class is :class:`~DeprecationWarning`,
119
- you can inherit this class to define your own deprecation warning category.
120
- """
121
- self.reason = reason or ""
122
- self.version = version or ""
123
- self.action = action
124
- self.category = category
125
- super(ClassicAdapter, self).__init__()
126
-
127
- def get_deprecated_msg(self, wrapped, instance):
128
- """
129
- Get the deprecation warning message for the user.
130
-
131
- :param wrapped: Wrapped class or function.
132
-
133
- :param instance: The object to which the wrapped function was bound when it was called.
134
-
135
- :return: The warning message.
136
- """
137
- if instance is None:
138
- if inspect.isclass(wrapped):
139
- fmt = "Call to deprecated class {name}."
140
- else:
141
- fmt = "Call to deprecated function (or staticmethod) {name}."
142
- else:
143
- if inspect.isclass(instance):
144
- fmt = "Call to deprecated class method {name}."
145
- else:
146
- fmt = "Call to deprecated method {name}."
147
- if self.reason:
148
- fmt += " ({reason})"
149
- if self.version:
150
- fmt += " -- Deprecated since version {version}."
151
- return fmt.format(name=wrapped.__name__, reason=self.reason or "", version=self.version or "")
152
-
153
- def __call__(self, wrapped):
154
- """
155
- Decorate your class or function.
156
-
157
- :param wrapped: Wrapped class or function.
158
-
159
- :return: the decorated class or function.
160
-
161
- .. versionchanged:: 1.2.4
162
- Don't pass arguments to :meth:`object.__new__` (other than *cls*).
163
-
164
- .. versionchanged:: 1.2.8
165
- The warning filter is not set if the *action* parameter is ``None`` or empty.
166
- """
167
- if inspect.isclass(wrapped):
168
- old_new1 = wrapped.__new__
169
-
170
- def wrapped_cls(cls, *args, **kwargs):
171
- msg = self.get_deprecated_msg(wrapped, None)
172
- if self.action:
173
- with warnings.catch_warnings():
174
- warnings.simplefilter(self.action, self.category)
175
- warnings.warn(msg, category=self.category, stacklevel=_class_stacklevel)
176
- else:
177
- warnings.warn(msg, category=self.category, stacklevel=_class_stacklevel)
178
- if old_new1 is object.__new__:
179
- return old_new1(cls)
180
- # actually, we don't know the real signature of *old_new1*
181
- return old_new1(cls, *args, **kwargs)
182
-
183
- wrapped.__new__ = staticmethod(wrapped_cls)
184
-
185
- return wrapped
186
-
187
-
188
- def deprecated(*args, **kwargs):
189
- """
190
- """
191
- if args and isinstance(args[0], string_types):
192
- kwargs['reason'] = args[0]
193
- args = args[1:]
194
-
195
- if args and not callable(args[0]):
196
- raise TypeError(repr(type(args[0])))
197
-
198
- if args:
199
- action = kwargs.get('action')
200
- category = kwargs.get('category', DeprecationWarning)
201
- adapter_cls = kwargs.pop('adapter_cls', ClassicAdapter)
202
- adapter = adapter_cls(**kwargs)
203
-
204
- wrapped = args[0]
205
- if inspect.isclass(wrapped):
206
- wrapped = adapter(wrapped)
207
- return wrapped
208
-
209
- elif inspect.isroutine(wrapped):
210
- once = False
211
-
212
- @wrapt.decorator(adapter=adapter)
213
- def wrapper_function(wrapped_, instance_, args_, kwargs_):
214
- nonlocal once
215
- msg = adapter.get_deprecated_msg(wrapped_, instance_)
216
- if action:
217
- if action == 'once' and once:
218
- pass
219
- else:
220
- once = True
221
- with warnings.catch_warnings():
222
- warnings.simplefilter(action, category)
223
- warnings.warn(msg, category=category, stacklevel=_routine_stacklevel)
224
- else:
225
- warnings.warn(msg, category=category, stacklevel=_routine_stacklevel)
226
- return wrapped_(*args_, **kwargs_)
227
-
228
- return wrapper_function(wrapped)
229
-
230
- else:
231
- raise TypeError(repr(type(wrapped)))
232
-
233
- return functools.partial(deprecated, **kwargs)
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/06/06 00:50
6
+
7
+ """
8
+ 原官方库: https://pypi.org/project/Deprecated
9
+
10
+ 其deprecated装饰器的action='once'功能跟我设想的不一样
11
+
12
+ 其实从实际使用角度看,默认的每个物理行只警告一次是最合理的模式,使用起来无伤大雅
13
+ 但我非要把这个'once'情景的功能给补完善~~
14
+
15
+ 从 deprecated.classic 文件改编而来
16
+ """
17
+
18
+ import functools
19
+ import inspect
20
+ import platform
21
+ import warnings
22
+
23
+ import wrapt
24
+
25
+ try:
26
+ # If the C extension for wrapt was compiled and wrapt/_wrappers.pyd exists, then the
27
+ # stack level that should be passed to warnings.warn should be 2. However, if using
28
+ # a pure python wrapt, a extra stacklevel is required.
29
+ import wrapt._wrappers
30
+
31
+ _routine_stacklevel = 2
32
+ _class_stacklevel = 2
33
+ except ImportError:
34
+ _routine_stacklevel = 3
35
+ if platform.python_implementation() == "PyPy":
36
+ _class_stacklevel = 2
37
+ else:
38
+ _class_stacklevel = 3
39
+
40
+ string_types = (type(b''), type(u''))
41
+
42
+
43
+ class ClassicAdapter(wrapt.AdapterFactory):
44
+ """
45
+ Classic adapter -- *for advanced usage only*
46
+
47
+ This adapter is used to get the deprecation message according to the wrapped object type:
48
+ class, function, standard method, static method, or class method.
49
+
50
+ This is the base class of the :class:`~deprecated.sphinx.SphinxAdapter` class
51
+ which is used to update the wrapped object docstring.
52
+
53
+ You can also inherit this class to change the deprecation message.
54
+
55
+ In the following example, we change the message into "The ... is deprecated.":
56
+
57
+ .. code-block:: python
58
+
59
+ import inspect
60
+
61
+ from deprecated.classic import ClassicAdapter
62
+ from deprecated.classic import deprecated
63
+
64
+
65
+ class MyClassicAdapter(ClassicAdapter):
66
+ def get_deprecated_msg(self, wrapped, instance):
67
+ if instance is None:
68
+ if inspect.isclass(wrapped):
69
+ fmt = "The class {name} is deprecated."
70
+ else:
71
+ fmt = "The function {name} is deprecated."
72
+ else:
73
+ if inspect.isclass(instance):
74
+ fmt = "The class method {name} is deprecated."
75
+ else:
76
+ fmt = "The method {name} is deprecated."
77
+ if self.reason:
78
+ fmt += " ({reason})"
79
+ if self.version:
80
+ fmt += " -- Deprecated since version {version}."
81
+ return fmt.format(name=wrapped.__name__,
82
+ reason=self.reason or "",
83
+ version=self.version or "")
84
+
85
+ Then, you can use your ``MyClassicAdapter`` class like this in your source code:
86
+
87
+ .. code-block:: python
88
+
89
+ @deprecated(reason="use another function", adapter_cls=MyClassicAdapter)
90
+ def some_old_function(x, y):
91
+ return x + y
92
+ """
93
+
94
+ def __init__(self, reason="", version="", action=None, category=DeprecationWarning):
95
+ """
96
+ Construct a wrapper adapter.
97
+
98
+ :type reason: str
99
+ :param reason:
100
+ Reason message which documents the deprecation in your library (can be omitted).
101
+
102
+ :type version: str
103
+ :param version:
104
+ Version of your project which deprecates this feature.
105
+ If you follow the `Semantic Versioning <https://semver.org/>`_,
106
+ the version number has the format "MAJOR.MINOR.PATCH".
107
+
108
+ :type action: str
109
+ :param action:
110
+ A warning filter used to activate or not the deprecation warning.
111
+ Can be one of "error", "ignore", "always", "default", "module", or "once".
112
+ If ``None`` or empty, the the global filtering mechanism is used.
113
+ See: `The Warnings Filter`_ in the Python documentation.
114
+
115
+ :type category: type
116
+ :param category:
117
+ The warning category to use for the deprecation warning.
118
+ By default, the category class is :class:`~DeprecationWarning`,
119
+ you can inherit this class to define your own deprecation warning category.
120
+ """
121
+ self.reason = reason or ""
122
+ self.version = version or ""
123
+ self.action = action
124
+ self.category = category
125
+ super(ClassicAdapter, self).__init__()
126
+
127
+ def get_deprecated_msg(self, wrapped, instance):
128
+ """
129
+ Get the deprecation warning message for the user.
130
+
131
+ :param wrapped: Wrapped class or function.
132
+
133
+ :param instance: The object to which the wrapped function was bound when it was called.
134
+
135
+ :return: The warning message.
136
+ """
137
+ if instance is None:
138
+ if inspect.isclass(wrapped):
139
+ fmt = "Call to deprecated class {name}."
140
+ else:
141
+ fmt = "Call to deprecated function (or staticmethod) {name}."
142
+ else:
143
+ if inspect.isclass(instance):
144
+ fmt = "Call to deprecated class method {name}."
145
+ else:
146
+ fmt = "Call to deprecated method {name}."
147
+ if self.reason:
148
+ fmt += " ({reason})"
149
+ if self.version:
150
+ fmt += " -- Deprecated since version {version}."
151
+ return fmt.format(name=wrapped.__name__, reason=self.reason or "", version=self.version or "")
152
+
153
+ def __call__(self, wrapped):
154
+ """
155
+ Decorate your class or function.
156
+
157
+ :param wrapped: Wrapped class or function.
158
+
159
+ :return: the decorated class or function.
160
+
161
+ .. versionchanged:: 1.2.4
162
+ Don't pass arguments to :meth:`object.__new__` (other than *cls*).
163
+
164
+ .. versionchanged:: 1.2.8
165
+ The warning filter is not set if the *action* parameter is ``None`` or empty.
166
+ """
167
+ if inspect.isclass(wrapped):
168
+ old_new1 = wrapped.__new__
169
+
170
+ def wrapped_cls(cls, *args, **kwargs):
171
+ msg = self.get_deprecated_msg(wrapped, None)
172
+ if self.action:
173
+ with warnings.catch_warnings():
174
+ warnings.simplefilter(self.action, self.category)
175
+ warnings.warn(msg, category=self.category, stacklevel=_class_stacklevel)
176
+ else:
177
+ warnings.warn(msg, category=self.category, stacklevel=_class_stacklevel)
178
+ if old_new1 is object.__new__:
179
+ return old_new1(cls)
180
+ # actually, we don't know the real signature of *old_new1*
181
+ return old_new1(cls, *args, **kwargs)
182
+
183
+ wrapped.__new__ = staticmethod(wrapped_cls)
184
+
185
+ return wrapped
186
+
187
+
188
+ def deprecated(*args, **kwargs):
189
+ """
190
+ """
191
+ if args and isinstance(args[0], string_types):
192
+ kwargs['reason'] = args[0]
193
+ args = args[1:]
194
+
195
+ if args and not callable(args[0]):
196
+ raise TypeError(repr(type(args[0])))
197
+
198
+ if args:
199
+ action = kwargs.get('action')
200
+ category = kwargs.get('category', DeprecationWarning)
201
+ adapter_cls = kwargs.pop('adapter_cls', ClassicAdapter)
202
+ adapter = adapter_cls(**kwargs)
203
+
204
+ wrapped = args[0]
205
+ if inspect.isclass(wrapped):
206
+ wrapped = adapter(wrapped)
207
+ return wrapped
208
+
209
+ elif inspect.isroutine(wrapped):
210
+ once = False
211
+
212
+ @wrapt.decorator(adapter=adapter)
213
+ def wrapper_function(wrapped_, instance_, args_, kwargs_):
214
+ nonlocal once
215
+ msg = adapter.get_deprecated_msg(wrapped_, instance_)
216
+ if action:
217
+ if action == 'once' and once:
218
+ pass
219
+ else:
220
+ once = True
221
+ with warnings.catch_warnings():
222
+ warnings.simplefilter(action, category)
223
+ warnings.warn(msg, category=category, stacklevel=_routine_stacklevel)
224
+ else:
225
+ warnings.warn(msg, category=category, stacklevel=_routine_stacklevel)
226
+ return wrapped_(*args_, **kwargs_)
227
+
228
+ return wrapper_function(wrapped)
229
+
230
+ else:
231
+ raise TypeError(repr(type(wrapped)))
232
+
233
+ return functools.partial(deprecated, **kwargs)