deprecated-params 0.2.0__tar.gz
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.
- deprecated_params-0.2.0/LICENSE +21 -0
- deprecated_params-0.2.0/PKG-INFO +87 -0
- deprecated_params-0.2.0/README.md +73 -0
- deprecated_params-0.2.0/deprecated_params/__init__.py +434 -0
- deprecated_params-0.2.0/deprecated_params.egg-info/PKG-INFO +87 -0
- deprecated_params-0.2.0/deprecated_params.egg-info/SOURCES.txt +11 -0
- deprecated_params-0.2.0/deprecated_params.egg-info/dependency_links.txt +1 -0
- deprecated_params-0.2.0/deprecated_params.egg-info/requires.txt +3 -0
- deprecated_params-0.2.0/deprecated_params.egg-info/top_level.txt +1 -0
- deprecated_params-0.2.0/pyproject.toml +28 -0
- deprecated_params-0.2.0/setup.cfg +4 -0
- deprecated_params-0.2.0/setup.py +4 -0
- deprecated_params-0.2.0/tests/test_wrapper.py +142 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vizonex
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deprecated-params
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Wrapper for functions, class objects and methods for deprecating keyword parameters
|
|
5
|
+
Author-email: Vizonex <VizonexBusiness@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/Vizonex/deprecated-params
|
|
8
|
+
Project-URL: repository, https://github.com/Vizonex/deprecated-params.git
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typing-extensions; python_version < "3.10"
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Deprecated Params
|
|
16
|
+
[](https://badge.fury.io/py/deprecated-params)
|
|
17
|
+

|
|
18
|
+
[](https://opensource.org/licenses/MIT)
|
|
19
|
+
[](https://opensource.org/licenses/Appache-2-0)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Inspired after python's warning.deprecated wrapper, deprecated_params is made to serve the single purpose of deprecating parameter names to warn users
|
|
23
|
+
about incoming changes as well as retaining typehinting.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## How to Deprecate Parameters
|
|
28
|
+
Parameters should be keyword arguments, not positional, Reason
|
|
29
|
+
for this implementation is that in theory you should've already
|
|
30
|
+
planned an alternative approach to an argument you wish
|
|
31
|
+
to deprecate. Most of the times these arguments will most
|
|
32
|
+
likely be one of 3 cases.
|
|
33
|
+
- misspellings
|
|
34
|
+
- better functionality that replaces old arguments with better ones.
|
|
35
|
+
- removed parameters but you want to warn developers
|
|
36
|
+
to move without being aggressive about it.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from deprecated_params import deprecated_params
|
|
41
|
+
|
|
42
|
+
@deprecated_params(['x'])
|
|
43
|
+
def func(y, *, x:int = 0):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# DeprecationWarning: Parameter "x" is deprecated
|
|
47
|
+
func(None, x=20)
|
|
48
|
+
|
|
49
|
+
# NOTE: **kw is accepted but also you could put down more than one
|
|
50
|
+
# parameter if needed...
|
|
51
|
+
@deprecated_params(['foo'], {"foo":"foo was removed in ... don't use it"}, display_kw=False)
|
|
52
|
+
class MyClass:
|
|
53
|
+
def __init__(self, spam:object, **kw):
|
|
54
|
+
self.spam = spam
|
|
55
|
+
self.foo = kw.get("foo", None)
|
|
56
|
+
|
|
57
|
+
# DeprecationWarning: foo was removed in ... don't use it
|
|
58
|
+
mc = MyClass("spam", foo="X")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Why I wrote Deprecated Params
|
|
62
|
+
I got tired of throwing random warnings in my code and wanted something cleaner that didn't
|
|
63
|
+
interfere with a function's actual code and didn't blind anybody trying to go through it.
|
|
64
|
+
Contributors and Reviewers should be able to utilize a library that saves them from these problems
|
|
65
|
+
while improving the readability of a function. After figuring out that the functionality I was
|
|
66
|
+
looking for didn't exist I took the opportunity to implement it.
|
|
67
|
+
|
|
68
|
+
## Deprecated Params used in real-world Examples
|
|
69
|
+
Deprecated-params is now used with two of my own libraries by default.
|
|
70
|
+
|
|
71
|
+
- [aiothreading (up until 0.1.6)](https://github.com/Vizonex/aiothreading)
|
|
72
|
+
- Originally aiothreading had it's own wrapper but I split it off to this library along with a rewrite after finding out that
|
|
73
|
+
parameter names were not showing up ides such as vs-code. The rewrite felt a bit bigger and knowing that users would want to utilize
|
|
74
|
+
this concept in other places was how this library ultimately got started.
|
|
75
|
+
- Lots of interior changes were made and with many arguments being suddenly dropped to increase the performance, the best solution was to warn
|
|
76
|
+
developers to stop using certain parameters as they will be deleted in the future.
|
|
77
|
+
- It is planned to be dropped as many of the things we wanted to remove have been slowly removed from the library which means this library will
|
|
78
|
+
be removed from it but that doesn't mean I won't keep maintaining it, it is invented for short-use cases and can be added and removed freely
|
|
79
|
+
without needing additional dependencies.
|
|
80
|
+
|
|
81
|
+
- [aiocallback (mainly used in version 1.6)](https://github.com/Vizonex/aiocallback)
|
|
82
|
+
- Same situation as aiothreading but I decided to buy users more time due to how fast some releases were going and it also allowed
|
|
83
|
+
- Currently I removed deprecated-params from aiocallback since it wasn't needed anymore but this is what deprecated-param's purpose
|
|
84
|
+
was for, being there only when its need. I desired nothing more or less.
|
|
85
|
+
|
|
86
|
+
If you would like to add examples of your own libraries that have used this library feel free to throw me an issue or send me a pull request.
|
|
87
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Deprecated Params
|
|
2
|
+
[](https://badge.fury.io/py/deprecated-params)
|
|
3
|
+

|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://opensource.org/licenses/Appache-2-0)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Inspired after python's warning.deprecated wrapper, deprecated_params is made to serve the single purpose of deprecating parameter names to warn users
|
|
9
|
+
about incoming changes as well as retaining typehinting.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## How to Deprecate Parameters
|
|
14
|
+
Parameters should be keyword arguments, not positional, Reason
|
|
15
|
+
for this implementation is that in theory you should've already
|
|
16
|
+
planned an alternative approach to an argument you wish
|
|
17
|
+
to deprecate. Most of the times these arguments will most
|
|
18
|
+
likely be one of 3 cases.
|
|
19
|
+
- misspellings
|
|
20
|
+
- better functionality that replaces old arguments with better ones.
|
|
21
|
+
- removed parameters but you want to warn developers
|
|
22
|
+
to move without being aggressive about it.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from deprecated_params import deprecated_params
|
|
27
|
+
|
|
28
|
+
@deprecated_params(['x'])
|
|
29
|
+
def func(y, *, x:int = 0):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
# DeprecationWarning: Parameter "x" is deprecated
|
|
33
|
+
func(None, x=20)
|
|
34
|
+
|
|
35
|
+
# NOTE: **kw is accepted but also you could put down more than one
|
|
36
|
+
# parameter if needed...
|
|
37
|
+
@deprecated_params(['foo'], {"foo":"foo was removed in ... don't use it"}, display_kw=False)
|
|
38
|
+
class MyClass:
|
|
39
|
+
def __init__(self, spam:object, **kw):
|
|
40
|
+
self.spam = spam
|
|
41
|
+
self.foo = kw.get("foo", None)
|
|
42
|
+
|
|
43
|
+
# DeprecationWarning: foo was removed in ... don't use it
|
|
44
|
+
mc = MyClass("spam", foo="X")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Why I wrote Deprecated Params
|
|
48
|
+
I got tired of throwing random warnings in my code and wanted something cleaner that didn't
|
|
49
|
+
interfere with a function's actual code and didn't blind anybody trying to go through it.
|
|
50
|
+
Contributors and Reviewers should be able to utilize a library that saves them from these problems
|
|
51
|
+
while improving the readability of a function. After figuring out that the functionality I was
|
|
52
|
+
looking for didn't exist I took the opportunity to implement it.
|
|
53
|
+
|
|
54
|
+
## Deprecated Params used in real-world Examples
|
|
55
|
+
Deprecated-params is now used with two of my own libraries by default.
|
|
56
|
+
|
|
57
|
+
- [aiothreading (up until 0.1.6)](https://github.com/Vizonex/aiothreading)
|
|
58
|
+
- Originally aiothreading had it's own wrapper but I split it off to this library along with a rewrite after finding out that
|
|
59
|
+
parameter names were not showing up ides such as vs-code. The rewrite felt a bit bigger and knowing that users would want to utilize
|
|
60
|
+
this concept in other places was how this library ultimately got started.
|
|
61
|
+
- Lots of interior changes were made and with many arguments being suddenly dropped to increase the performance, the best solution was to warn
|
|
62
|
+
developers to stop using certain parameters as they will be deleted in the future.
|
|
63
|
+
- It is planned to be dropped as many of the things we wanted to remove have been slowly removed from the library which means this library will
|
|
64
|
+
be removed from it but that doesn't mean I won't keep maintaining it, it is invented for short-use cases and can be added and removed freely
|
|
65
|
+
without needing additional dependencies.
|
|
66
|
+
|
|
67
|
+
- [aiocallback (mainly used in version 1.6)](https://github.com/Vizonex/aiocallback)
|
|
68
|
+
- Same situation as aiothreading but I decided to buy users more time due to how fast some releases were going and it also allowed
|
|
69
|
+
- Currently I removed deprecated-params from aiocallback since it wasn't needed anymore but this is what deprecated-param's purpose
|
|
70
|
+
was for, being there only when its need. I desired nothing more or less.
|
|
71
|
+
|
|
72
|
+
If you would like to add examples of your own libraries that have used this library feel free to throw me an issue or send me a pull request.
|
|
73
|
+
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deprecated Params
|
|
3
|
+
-----------------
|
|
4
|
+
|
|
5
|
+
A Library dedicated for warning users about deprecated parameter
|
|
6
|
+
names and changes
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import inspect
|
|
12
|
+
import sys
|
|
13
|
+
import warnings
|
|
14
|
+
from functools import wraps
|
|
15
|
+
from types import MethodType
|
|
16
|
+
from typing import (
|
|
17
|
+
Any,
|
|
18
|
+
Callable,
|
|
19
|
+
Iterable,
|
|
20
|
+
Mapping,
|
|
21
|
+
Sequence,
|
|
22
|
+
TypeVar,
|
|
23
|
+
overload,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if sys.version_info < (3, 10):
|
|
28
|
+
from typing_extensions import ParamSpec
|
|
29
|
+
else:
|
|
30
|
+
from typing import ParamSpec
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__version__ = "0.2.0"
|
|
34
|
+
__license__ = "Apache 2.0 / MIT"
|
|
35
|
+
__author__ = "Vizonex"
|
|
36
|
+
|
|
37
|
+
_T = TypeVar("_T", covariant=True)
|
|
38
|
+
_P = ParamSpec("_P")
|
|
39
|
+
|
|
40
|
+
KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY
|
|
41
|
+
VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = (
|
|
45
|
+
"MissingKeywordsError",
|
|
46
|
+
"InvalidParametersError",
|
|
47
|
+
"deprecated_params",
|
|
48
|
+
"__author__",
|
|
49
|
+
"__license__",
|
|
50
|
+
"__version__",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Word of Warning:
|
|
54
|
+
# All functions marked with underscores should be treated as do not use
|
|
55
|
+
# directly. If you want these parts of code, they are under an MIT License and you
|
|
56
|
+
# are allowed to copy and paste them freely as long as it's not apart of python's
|
|
57
|
+
# warnings.deprecated function which then you will need to license under an APACHE 2.0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class _KeywordsBaseException(Exception):
|
|
61
|
+
def __init__(self, keywords: set[str], *args: Any) -> None:
|
|
62
|
+
self._keywords = frozenset(keywords)
|
|
63
|
+
super().__init__(*args)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def keywords(self) -> frozenset[str]:
|
|
67
|
+
"""tells what keywords were bad"""
|
|
68
|
+
return self._keywords
|
|
69
|
+
|
|
70
|
+
@keywords.setter
|
|
71
|
+
def keywords(self, _: frozenset[str]) -> None:
|
|
72
|
+
"""Throws ValueError because keywords is an
|
|
73
|
+
immutable property that shouldn't be edited."""
|
|
74
|
+
raise ValueError("keywords property is immutable")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class MissingKeywordsError(_KeywordsBaseException):
|
|
78
|
+
"""Raised when Missing a keyword for an argument"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, keywords: set[str], *args: Any) -> None:
|
|
81
|
+
"""Initializes missing keywords"""
|
|
82
|
+
super().__init__(
|
|
83
|
+
keywords,
|
|
84
|
+
f"Missing Keyword arguments for: {list(keywords)!r}",
|
|
85
|
+
*args,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class InvalidParametersError(_KeywordsBaseException):
|
|
90
|
+
"""Raised when Parameters were positional arguments without defaults or keyword arguments"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, keywords: set[str], *args: Any) -> None:
|
|
93
|
+
"""initializes invalid keywords, deprecated parameters should not be positional arguments
|
|
94
|
+
as that would defeat the purpose of deprecating a function's parameters."""
|
|
95
|
+
super().__init__(
|
|
96
|
+
keywords,
|
|
97
|
+
f"Arguments :{list(keywords)!r} should not be positional",
|
|
98
|
+
*args,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def join_version_if_sequence(ver: str | Sequence[int]) -> str:
|
|
103
|
+
return ".".join(map(str, ver)) if not isinstance(ver, str) else ver
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def convert_removed_in_sequences(
|
|
107
|
+
removed_in: Mapping[str, str | Sequence[int]],
|
|
108
|
+
) -> dict[str, str]:
|
|
109
|
+
return {k: join_version_if_sequence(v) for k, v in removed_in.items()}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class deprecated_params:
|
|
113
|
+
"""
|
|
114
|
+
A Wrapper inspired by python's wrapper deprecated from 3.13
|
|
115
|
+
and is used to deprecate parameters.
|
|
116
|
+
|
|
117
|
+
Since version 0.1.8 this wrapper also passes along an attribute
|
|
118
|
+
called `__deprecated_params__` with a dictionary of all the
|
|
119
|
+
preloaded deprecation warnings to each given parameter. Ides
|
|
120
|
+
such as VSCode, Pycharm and more could theoretically utilize
|
|
121
|
+
`__deprecated_params__` elsewhere help to assist users and developers
|
|
122
|
+
while writing and editing code.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
# __slots__ was an optimizations since subclassing deprecated_params should really be discouraged
|
|
126
|
+
# if this is not your case scenario and you must subclass this object throw me an issue.
|
|
127
|
+
|
|
128
|
+
__slots__ = (
|
|
129
|
+
"params",
|
|
130
|
+
"message",
|
|
131
|
+
"message_is_dict",
|
|
132
|
+
"display_kw",
|
|
133
|
+
"category",
|
|
134
|
+
"stacklevel",
|
|
135
|
+
"default_message",
|
|
136
|
+
"removed_in",
|
|
137
|
+
"_warning_messages",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def __init__(
|
|
141
|
+
self,
|
|
142
|
+
params: Sequence[str] | Iterable[str] | str,
|
|
143
|
+
message: str | Mapping[str, str] = "is deprecated",
|
|
144
|
+
/,
|
|
145
|
+
*,
|
|
146
|
+
# default_message should be utilized when a keyword isn't
|
|
147
|
+
# given in message if messaged is defined as a dictionary.
|
|
148
|
+
default_message: str | None = None,
|
|
149
|
+
category: type[Warning] | None = DeprecationWarning,
|
|
150
|
+
stacklevel: int = 1,
|
|
151
|
+
display_kw: bool = True,
|
|
152
|
+
# removed_in is inspired by the deprecation library
|
|
153
|
+
removed_in: str
|
|
154
|
+
| Sequence[int]
|
|
155
|
+
| Mapping[str, str | Sequence[int]]
|
|
156
|
+
| None = None,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Initializes deprecated parameters to pass along to different functions
|
|
160
|
+
|
|
161
|
+
:param params: A Sequence of keyword parameters of single keyword parameter to deprecate and warn the removal of.
|
|
162
|
+
:param message: A single message for to assign to each parameter to be deprecated otherwise
|
|
163
|
+
you can deprecate multiple under different reasons::
|
|
164
|
+
|
|
165
|
+
@deprecated_params(
|
|
166
|
+
['mispel', 'x'],
|
|
167
|
+
message={
|
|
168
|
+
'mispel': 'mispel was deprecated due to misspelling the word',
|
|
169
|
+
'x':'you get the idea...'
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
def mispelled_func(misspelling = None, *, mispel:str, x:int): ...
|
|
173
|
+
|
|
174
|
+
:param category: Used to warrant a custom warning category if required or needed to specify what
|
|
175
|
+
Deprecation warning should appear.
|
|
176
|
+
:param stacklevel: What level should this wanring appear at? Default: 1
|
|
177
|
+
:param default_message: When a parameter doesn't have a warning message try using this message instead
|
|
178
|
+
:param display_kw: Displays which parameter is deprecated in the warning message under `Parameter "%s" ...`
|
|
179
|
+
followed by the rest of the message
|
|
180
|
+
:param removed_in: Displays which version of your library's program will remove this keyword parameter in::
|
|
181
|
+
|
|
182
|
+
@deprecated_params(
|
|
183
|
+
['mispel', 'x'],
|
|
184
|
+
removed_in={
|
|
185
|
+
'mispel':'0.1.4',
|
|
186
|
+
'x':(0, 1, 3)
|
|
187
|
+
} # sequences of numbers are also allowed if preferred.
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def mispelled_func(misspelling = None, *, mispel:str, x:int): ...
|
|
191
|
+
|
|
192
|
+
you can also say that all parameters will be removed in one version::
|
|
193
|
+
|
|
194
|
+
@deprecated_params(
|
|
195
|
+
['mispel', 'x'],
|
|
196
|
+
removed_in='0.1.5' # or (0, 1, 5)
|
|
197
|
+
)
|
|
198
|
+
def mispelled_func(misspelling = None, *, mispel:str, x:int): ...
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
if not params:
|
|
203
|
+
raise ValueError(f"params should not be empty got {params!r}")
|
|
204
|
+
if not isinstance(message, (str, dict, Mapping)):
|
|
205
|
+
raise TypeError(
|
|
206
|
+
f"Expected an object of type str or dict or Mappable type for 'message', not {type(message).__name__!r}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
self.params = (
|
|
210
|
+
set(params) if not isinstance(params, str) else set([params])
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
self.message = message or "is deprecated"
|
|
214
|
+
self.message_is_dict = isinstance(message, (Mapping, dict))
|
|
215
|
+
self.display_kw = display_kw
|
|
216
|
+
self.category = category
|
|
217
|
+
self.stacklevel = stacklevel
|
|
218
|
+
self.default_message = default_message or "do not use"
|
|
219
|
+
|
|
220
|
+
if removed_in:
|
|
221
|
+
if isinstance(removed_in, (dict, Mapping)):
|
|
222
|
+
# Some people might be more comfortable giving versions in tuples or lists.
|
|
223
|
+
self.removed_in = convert_removed_in_sequences(removed_in)
|
|
224
|
+
else:
|
|
225
|
+
# single removed version meaning that all parameters will be removed in this version
|
|
226
|
+
ver = join_version_if_sequence(removed_in)
|
|
227
|
+
self.removed_in = {k: ver for k in params}
|
|
228
|
+
else:
|
|
229
|
+
self.removed_in = {}
|
|
230
|
+
|
|
231
|
+
# Preloaded previews of all warning messages new in deprecated-params 0.1.8 for extra speed
|
|
232
|
+
# upon loading the message
|
|
233
|
+
self._warning_messages: dict[str, str] = {
|
|
234
|
+
p: self.__write_warning(p) for p in self.params
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
def __check_with_missing(
|
|
238
|
+
self,
|
|
239
|
+
fn: Callable[..., Any],
|
|
240
|
+
missing: set[str] | None = None,
|
|
241
|
+
invalid_params: set[str] | None = None,
|
|
242
|
+
skip_missing: bool | None = None,
|
|
243
|
+
allow_miss: bool = False,
|
|
244
|
+
) -> tuple[set[str], set[str], bool]:
|
|
245
|
+
sig = inspect.signature(fn)
|
|
246
|
+
|
|
247
|
+
missing = missing if missing is not None else set(self.params)
|
|
248
|
+
invalid_params = set() if invalid_params is None else invalid_params
|
|
249
|
+
|
|
250
|
+
skip_missing = (
|
|
251
|
+
any([p.kind == VAR_KEYWORD for p in sig.parameters.values()])
|
|
252
|
+
if skip_missing is None
|
|
253
|
+
else skip_missing
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
for m in self.params:
|
|
257
|
+
if not allow_miss:
|
|
258
|
+
p = sig.parameters[m]
|
|
259
|
+
else:
|
|
260
|
+
p = sig.parameters.get(m) # type: ignore
|
|
261
|
+
if p is None:
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
# Check if were keyword only or aren't carrying a default param
|
|
265
|
+
if p.kind != KEYWORD_ONLY:
|
|
266
|
+
# Anything this isn't a keyword should be considered as deprecated
|
|
267
|
+
# as were still technically using it.
|
|
268
|
+
invalid_params.add(p.name)
|
|
269
|
+
|
|
270
|
+
if not skip_missing:
|
|
271
|
+
missing.remove(p.name)
|
|
272
|
+
|
|
273
|
+
return missing, invalid_params, skip_missing
|
|
274
|
+
|
|
275
|
+
def __check_for_missing_kwds(
|
|
276
|
+
self,
|
|
277
|
+
fn: Callable[..., Any],
|
|
278
|
+
missing: set[str] | None = None,
|
|
279
|
+
invalid_params: set[str] | None = None,
|
|
280
|
+
skip_missing: bool | None = None,
|
|
281
|
+
allow_miss: bool = False,
|
|
282
|
+
) -> None:
|
|
283
|
+
# copy sequence to check for missing parameter names
|
|
284
|
+
missing, invalid_params, skip_missing = self.__check_with_missing(
|
|
285
|
+
fn, missing, invalid_params, skip_missing, allow_miss
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
if invalid_params:
|
|
289
|
+
raise InvalidParametersError(invalid_params)
|
|
290
|
+
|
|
291
|
+
if missing and not skip_missing:
|
|
292
|
+
raise MissingKeywordsError(missing)
|
|
293
|
+
|
|
294
|
+
def __write_warning(self, kw_name: str) -> str:
|
|
295
|
+
msg = ""
|
|
296
|
+
if self.display_kw:
|
|
297
|
+
msg += 'Parameter "%s" ' % kw_name
|
|
298
|
+
|
|
299
|
+
if self.message_is_dict:
|
|
300
|
+
msg += self.message.get(kw_name, self.default_message) # type: ignore
|
|
301
|
+
else:
|
|
302
|
+
msg += self.message # type: ignore
|
|
303
|
+
|
|
304
|
+
if self.removed_in:
|
|
305
|
+
if kw_removed_in := self.removed_in.get(kw_name):
|
|
306
|
+
msg += " [Removed In: "
|
|
307
|
+
msg += kw_removed_in
|
|
308
|
+
msg += "]"
|
|
309
|
+
|
|
310
|
+
return msg
|
|
311
|
+
|
|
312
|
+
def __warn(self, kw_name: str, source: Any) -> None:
|
|
313
|
+
warnings.warn(
|
|
314
|
+
self._warning_messages[kw_name],
|
|
315
|
+
self.category,
|
|
316
|
+
stacklevel=self.stacklevel + 1,
|
|
317
|
+
source=source,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
@overload
|
|
321
|
+
def __call__(self, arg: type[_T]) -> type[_T]: ...
|
|
322
|
+
|
|
323
|
+
@overload
|
|
324
|
+
def __call__(self, arg: Callable[_P, _T]) -> Callable[_P, _T]: ...
|
|
325
|
+
|
|
326
|
+
# Mirrors python's deprecated wrapper with a few changes
|
|
327
|
+
# Since 0.1.8 a new attribute is added called __deprecated_params__
|
|
328
|
+
# based off and inspired by python's __deprecated__ dunder value.
|
|
329
|
+
|
|
330
|
+
def __call__(
|
|
331
|
+
self, arg: type[_T] | Callable[_P, _T]
|
|
332
|
+
) -> type[_T] | Callable[_P, _T]:
|
|
333
|
+
def check_kw_arguments(kw: dict[str, Any]) -> None:
|
|
334
|
+
captured = self.params.intersection(kw.keys())
|
|
335
|
+
for k in captured:
|
|
336
|
+
self.__warn(k, arg)
|
|
337
|
+
|
|
338
|
+
if isinstance(arg, type):
|
|
339
|
+
# NOTE: Combining init and new together is done to
|
|
340
|
+
# solve deprecation of both new_args and init_args
|
|
341
|
+
|
|
342
|
+
missing, invalid_params, skip_missing = self.__check_with_missing(
|
|
343
|
+
arg, allow_miss=True
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
original_new: Callable[..., type[_T]] = arg.__new__
|
|
347
|
+
self.__check_for_missing_kwds(
|
|
348
|
+
original_new,
|
|
349
|
+
missing,
|
|
350
|
+
invalid_params,
|
|
351
|
+
skip_missing,
|
|
352
|
+
allow_miss=True,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
@wraps(original_new)
|
|
356
|
+
def __new__(
|
|
357
|
+
cls: type[_T], *args: _P.args, **kwargs: _P.kwargs
|
|
358
|
+
) -> type[_T]:
|
|
359
|
+
check_kw_arguments(kwargs)
|
|
360
|
+
if original_new is not object.__new__:
|
|
361
|
+
return original_new(cls, *args, **kwargs)
|
|
362
|
+
# Python Comment: Mirrors a similar check in object.__new__.
|
|
363
|
+
elif cls.__init__ is object.__init__ and (args or kwargs):
|
|
364
|
+
raise TypeError(f"{cls.__name__}() takes no arguments")
|
|
365
|
+
else:
|
|
366
|
+
return original_new(cls)
|
|
367
|
+
|
|
368
|
+
arg.__new__ = staticmethod(__new__) # type: ignore
|
|
369
|
+
arg.__new__.__deprecated_params__ = self._warning_messages.copy() # type: ignore
|
|
370
|
+
|
|
371
|
+
original_init_subclass = arg.__init_subclass__
|
|
372
|
+
# Python Comment: We need slightly different behavior if __init_subclass__
|
|
373
|
+
# is a bound method (likely if it was implemented in Python)
|
|
374
|
+
if isinstance(original_init_subclass, MethodType):
|
|
375
|
+
self.__check_for_missing_kwds(
|
|
376
|
+
original_init_subclass,
|
|
377
|
+
missing,
|
|
378
|
+
invalid_params,
|
|
379
|
+
skip_missing,
|
|
380
|
+
allow_miss=True,
|
|
381
|
+
)
|
|
382
|
+
original_init_subclass = original_init_subclass.__func__
|
|
383
|
+
|
|
384
|
+
@wraps(original_init_subclass)
|
|
385
|
+
def __init_subclass__(
|
|
386
|
+
*args: _P.args, **kwargs: _P.kwargs
|
|
387
|
+
) -> Any:
|
|
388
|
+
check_kw_arguments(kwargs)
|
|
389
|
+
return original_init_subclass(*args, **kwargs)
|
|
390
|
+
|
|
391
|
+
arg.__init_subclass__ = classmethod(__init_subclass__) # type: ignore
|
|
392
|
+
arg.__init_subclass__.__deprecated_params__ = ( # type: ignore[attr-defined]
|
|
393
|
+
self._warning_messages.copy()
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Python Comment: Or otherwise, which likely means it's a builtin such as
|
|
397
|
+
# object's implementation of __init_subclass__.
|
|
398
|
+
else:
|
|
399
|
+
|
|
400
|
+
@wraps(original_init_subclass)
|
|
401
|
+
def __init_subclass__(
|
|
402
|
+
*args: _P.args, **kwargs: _P.kwargs
|
|
403
|
+
) -> None:
|
|
404
|
+
check_kw_arguments(kwargs)
|
|
405
|
+
return original_init_subclass(*args, **kwargs)
|
|
406
|
+
|
|
407
|
+
arg.__init_subclass__ = __init_subclass__ # type: ignore
|
|
408
|
+
arg.__init_subclass__.__deprecated_params__ = ( # type: ignore[attr-defined]
|
|
409
|
+
self._warning_messages.copy()
|
|
410
|
+
)
|
|
411
|
+
return arg
|
|
412
|
+
|
|
413
|
+
elif callable(arg):
|
|
414
|
+
# Check for missing function arguments
|
|
415
|
+
self.__check_for_missing_kwds(arg)
|
|
416
|
+
|
|
417
|
+
@wraps(arg)
|
|
418
|
+
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T:
|
|
419
|
+
check_kw_arguments(kwargs)
|
|
420
|
+
return arg(*args, **kwargs)
|
|
421
|
+
|
|
422
|
+
if sys.version_info >= (3, 12):
|
|
423
|
+
if inspect.iscoroutinefunction(arg):
|
|
424
|
+
wrapper = inspect.markcoroutinefunction(wrapper)
|
|
425
|
+
|
|
426
|
+
# Wrapper now contains a shadow copy of deprecated parameters
|
|
427
|
+
wrapper.__deprecated_params__ = self._warning_messages.copy() # type: ignore
|
|
428
|
+
return wrapper
|
|
429
|
+
|
|
430
|
+
else:
|
|
431
|
+
raise TypeError(
|
|
432
|
+
"@deprecated_params decorator with non-None category must be applied to "
|
|
433
|
+
f"a class or callable, not {arg!r}"
|
|
434
|
+
)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deprecated-params
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Wrapper for functions, class objects and methods for deprecating keyword parameters
|
|
5
|
+
Author-email: Vizonex <VizonexBusiness@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/Vizonex/deprecated-params
|
|
8
|
+
Project-URL: repository, https://github.com/Vizonex/deprecated-params.git
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typing-extensions; python_version < "3.10"
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Deprecated Params
|
|
16
|
+
[](https://badge.fury.io/py/deprecated-params)
|
|
17
|
+

|
|
18
|
+
[](https://opensource.org/licenses/MIT)
|
|
19
|
+
[](https://opensource.org/licenses/Appache-2-0)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Inspired after python's warning.deprecated wrapper, deprecated_params is made to serve the single purpose of deprecating parameter names to warn users
|
|
23
|
+
about incoming changes as well as retaining typehinting.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## How to Deprecate Parameters
|
|
28
|
+
Parameters should be keyword arguments, not positional, Reason
|
|
29
|
+
for this implementation is that in theory you should've already
|
|
30
|
+
planned an alternative approach to an argument you wish
|
|
31
|
+
to deprecate. Most of the times these arguments will most
|
|
32
|
+
likely be one of 3 cases.
|
|
33
|
+
- misspellings
|
|
34
|
+
- better functionality that replaces old arguments with better ones.
|
|
35
|
+
- removed parameters but you want to warn developers
|
|
36
|
+
to move without being aggressive about it.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from deprecated_params import deprecated_params
|
|
41
|
+
|
|
42
|
+
@deprecated_params(['x'])
|
|
43
|
+
def func(y, *, x:int = 0):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# DeprecationWarning: Parameter "x" is deprecated
|
|
47
|
+
func(None, x=20)
|
|
48
|
+
|
|
49
|
+
# NOTE: **kw is accepted but also you could put down more than one
|
|
50
|
+
# parameter if needed...
|
|
51
|
+
@deprecated_params(['foo'], {"foo":"foo was removed in ... don't use it"}, display_kw=False)
|
|
52
|
+
class MyClass:
|
|
53
|
+
def __init__(self, spam:object, **kw):
|
|
54
|
+
self.spam = spam
|
|
55
|
+
self.foo = kw.get("foo", None)
|
|
56
|
+
|
|
57
|
+
# DeprecationWarning: foo was removed in ... don't use it
|
|
58
|
+
mc = MyClass("spam", foo="X")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Why I wrote Deprecated Params
|
|
62
|
+
I got tired of throwing random warnings in my code and wanted something cleaner that didn't
|
|
63
|
+
interfere with a function's actual code and didn't blind anybody trying to go through it.
|
|
64
|
+
Contributors and Reviewers should be able to utilize a library that saves them from these problems
|
|
65
|
+
while improving the readability of a function. After figuring out that the functionality I was
|
|
66
|
+
looking for didn't exist I took the opportunity to implement it.
|
|
67
|
+
|
|
68
|
+
## Deprecated Params used in real-world Examples
|
|
69
|
+
Deprecated-params is now used with two of my own libraries by default.
|
|
70
|
+
|
|
71
|
+
- [aiothreading (up until 0.1.6)](https://github.com/Vizonex/aiothreading)
|
|
72
|
+
- Originally aiothreading had it's own wrapper but I split it off to this library along with a rewrite after finding out that
|
|
73
|
+
parameter names were not showing up ides such as vs-code. The rewrite felt a bit bigger and knowing that users would want to utilize
|
|
74
|
+
this concept in other places was how this library ultimately got started.
|
|
75
|
+
- Lots of interior changes were made and with many arguments being suddenly dropped to increase the performance, the best solution was to warn
|
|
76
|
+
developers to stop using certain parameters as they will be deleted in the future.
|
|
77
|
+
- It is planned to be dropped as many of the things we wanted to remove have been slowly removed from the library which means this library will
|
|
78
|
+
be removed from it but that doesn't mean I won't keep maintaining it, it is invented for short-use cases and can be added and removed freely
|
|
79
|
+
without needing additional dependencies.
|
|
80
|
+
|
|
81
|
+
- [aiocallback (mainly used in version 1.6)](https://github.com/Vizonex/aiocallback)
|
|
82
|
+
- Same situation as aiothreading but I decided to buy users more time due to how fast some releases were going and it also allowed
|
|
83
|
+
- Currently I removed deprecated-params from aiocallback since it wasn't needed anymore but this is what deprecated-param's purpose
|
|
84
|
+
was for, being there only when its need. I desired nothing more or less.
|
|
85
|
+
|
|
86
|
+
If you would like to add examples of your own libraries that have used this library feel free to throw me an issue or send me a pull request.
|
|
87
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
deprecated_params/__init__.py
|
|
6
|
+
deprecated_params.egg-info/PKG-INFO
|
|
7
|
+
deprecated_params.egg-info/SOURCES.txt
|
|
8
|
+
deprecated_params.egg-info/dependency_links.txt
|
|
9
|
+
deprecated_params.egg-info/requires.txt
|
|
10
|
+
deprecated_params.egg-info/top_level.txt
|
|
11
|
+
tests/test_wrapper.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deprecated_params
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools >= 47"
|
|
4
|
+
]
|
|
5
|
+
[project]
|
|
6
|
+
name = "deprecated-params"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A Wrapper for functions, class objects and methods for deprecating keyword parameters"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Vizonex", email="VizonexBusiness@gmail.com"},
|
|
12
|
+
]
|
|
13
|
+
license = "MIT"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
dependencies = [
|
|
16
|
+
'typing-extensions; python_version<"3.10"'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
homepage = "https://github.com/Vizonex/deprecated-params"
|
|
22
|
+
repository = "https://github.com/Vizonex/deprecated-params.git"
|
|
23
|
+
|
|
24
|
+
[tool.ruff]
|
|
25
|
+
line-length = 79
|
|
26
|
+
indent-width = 4
|
|
27
|
+
target-version = "py39"
|
|
28
|
+
fix = true
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from deprecated_params import deprecated_params
|
|
4
|
+
import sys
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# should carry w x y
|
|
9
|
+
def test_deprecated_param() -> None:
|
|
10
|
+
@deprecated_params(["x"], "is deprecated")
|
|
11
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
with pytest.warns(DeprecationWarning, match='Parameter "x" is deprecated'):
|
|
15
|
+
my_func(0, x=0)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_single_deprecated_param() -> None:
|
|
19
|
+
@deprecated_params("x", "is deprecated")
|
|
20
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
with pytest.warns(DeprecationWarning, match='Parameter "x" is deprecated'):
|
|
24
|
+
my_func(0, x=0)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_no_warn_if_deprecated_parameter_not_passed() -> None:
|
|
28
|
+
@deprecated_params("x", "is deprecated")
|
|
29
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
# Do not raise or print a warning when X is not passed...
|
|
33
|
+
with warnings.catch_warnings():
|
|
34
|
+
warnings.simplefilter("error")
|
|
35
|
+
my_func(1, y=0)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_deprecated_param_removed_in() -> None:
|
|
39
|
+
@deprecated_params(["x"], "is deprecated", removed_in={"x": (0, 1, 5)})
|
|
40
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
with pytest.warns(
|
|
44
|
+
DeprecationWarning,
|
|
45
|
+
match=r"Parameter \"x\" is deprecated \[Removed In\: 0.1.5\]",
|
|
46
|
+
):
|
|
47
|
+
my_func(0, x=0)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_deprecated_params_dunder_attribute() -> None:
|
|
51
|
+
@deprecated_params(["x"], "is deprecated", removed_in={"x": (0, 1, 5)})
|
|
52
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
assert getattr(my_func, "__deprecated_params__") == {
|
|
56
|
+
"x": 'Parameter "x" is deprecated [Removed In: 0.1.5]'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Since 0.2.0 we no longer repeat warnings once. We do it
|
|
61
|
+
# so that developers are more willing to remove specific keyword parameters
|
|
62
|
+
def test_deprecated_param_repeat_twice() -> None:
|
|
63
|
+
@deprecated_params(["x"], "is deprecated", removed_in={"x": (0, 1, 5)})
|
|
64
|
+
def my_func(w: int, *, x: int = 0, y: int = 0) -> None:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
with pytest.warns(
|
|
68
|
+
DeprecationWarning,
|
|
69
|
+
match=r"Parameter \"x\" is deprecated \[Removed In\: 0.1.5\]",
|
|
70
|
+
):
|
|
71
|
+
my_func(0, x=0)
|
|
72
|
+
|
|
73
|
+
with pytest.warns(
|
|
74
|
+
DeprecationWarning,
|
|
75
|
+
match=r"Parameter \"x\" is deprecated \[Removed In\: 0.1.5\]",
|
|
76
|
+
):
|
|
77
|
+
my_func(0, x=0)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_class_wrapper_and_kw_display_disabled() -> None:
|
|
81
|
+
@deprecated_params(["foo"], "foo is deprecated", display_kw=False)
|
|
82
|
+
class MyClass:
|
|
83
|
+
def __init__(self, spam: str, *, foo: Optional[str] = None):
|
|
84
|
+
self.spam = spam
|
|
85
|
+
self.foo = foo
|
|
86
|
+
|
|
87
|
+
mc = MyClass("spam")
|
|
88
|
+
assert mc.spam == "spam"
|
|
89
|
+
assert mc.foo is None
|
|
90
|
+
|
|
91
|
+
with pytest.warns(DeprecationWarning, match="foo is deprecated"):
|
|
92
|
+
MyClass("spam", foo="foo")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_class_wrapper_and_kw_display_disabled_one_param() -> None:
|
|
96
|
+
@deprecated_params("foo", "foo is deprecated", display_kw=False)
|
|
97
|
+
class MyClass:
|
|
98
|
+
def __init__(self, spam: str, *, foo: Optional[str] = None):
|
|
99
|
+
self.spam = spam
|
|
100
|
+
self.foo = foo
|
|
101
|
+
|
|
102
|
+
mc = MyClass("spam")
|
|
103
|
+
assert mc.spam == "spam"
|
|
104
|
+
assert mc.foo is None
|
|
105
|
+
|
|
106
|
+
with pytest.warns(DeprecationWarning, match="foo is deprecated"):
|
|
107
|
+
MyClass("spam", foo="foo")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# There was nothing sillier than this...
|
|
111
|
+
class TornadoWarning(DeprecationWarning):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="kw_only not on 3.9")
|
|
116
|
+
def test_dataclasses_with_wrapper_message_dicts_custom_warning() -> None:
|
|
117
|
+
from dataclasses import dataclass, field
|
|
118
|
+
|
|
119
|
+
@deprecated_params(
|
|
120
|
+
["foo", "spam"],
|
|
121
|
+
{"foo": "got foo", "spam": "got spam"},
|
|
122
|
+
display_kw=False,
|
|
123
|
+
category=TornadoWarning,
|
|
124
|
+
)
|
|
125
|
+
@dataclass
|
|
126
|
+
class Class:
|
|
127
|
+
foo: Optional[str] = field(kw_only=True, default=None)
|
|
128
|
+
spam: Optional[str] = field(kw_only=True, default=None)
|
|
129
|
+
|
|
130
|
+
with pytest.warns(TornadoWarning, match="got foo"):
|
|
131
|
+
Class(foo="foo")
|
|
132
|
+
|
|
133
|
+
with pytest.warns(TornadoWarning, match="got spam"):
|
|
134
|
+
Class(spam="foo")
|
|
135
|
+
|
|
136
|
+
# Do Not raise if class doesn't pass a deprecated parameter
|
|
137
|
+
with warnings.catch_warnings():
|
|
138
|
+
warnings.simplefilter(action="error")
|
|
139
|
+
Class()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# TODO: Metaclasses...
|