Skip to content

Commit 8a51cf0

Browse files
committed
Vendor the Deprecated library
1 parent 16721e5 commit 8a51cf0

10 files changed

Lines changed: 299 additions & 12 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ repos:
3232
language: python
3333
additional_dependencies:
3434
- https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl
35-
exclude: '(.*pixi\.lock)|(\.git_archival\.txt)|(.*\.patch$)'
35+
exclude: '(.*pixi\.lock)|(\.git_archival\.txt)|(.*\.patch$)|(^cuda_core/cuda/core/_vendored/)'
3636
args: ["--fix"]
3737

3838
- id: no-markdown-in-docs-source
@@ -111,11 +111,11 @@ repos:
111111
alias: mypy-cuda-core
112112
name: mypy-cuda-core
113113
files: ^cuda_core/cuda/.*\.(py|pyi)$
114+
exclude: ^cuda_core/cuda/core/_vendored/
114115
pass_filenames: false
115116
args: [--config-file=cuda_core/pyproject.toml, cuda_core/cuda/core]
116117
additional_dependencies:
117118
- numpy
118-
- types-Deprecated
119119

120120
- repo: https://github.com/rhysd/actionlint
121121
rev: "914e7df21a07ef503a81201c76d2b11c789d3fca" # frozen: v1.7.12

cuda_core/AGENTS.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,12 @@ Reviews should point out where existing public APIs are broken.
157157
- Changes should be notated in the code and also in the release notes in the
158158
"Deprecated APIs" section.
159159

160-
**Annotating a new API** — Use the `deprecated.sphinx.versionadded` decorator:
160+
**Annotating a new API** — Use the `versionadded` decorator from the vendored
161+
`cuda.core._vendored.deprecated.sphinx` module:
161162

162163
```python
163164

164-
from deprecated.sphinx import versionadded
165+
from cuda.core._vendored.deprecated.sphinx import versionadded
165166

166167
@versionadded("1.2.0")
167168
def new_feature(...):
@@ -180,11 +181,12 @@ def new_feature(...):
180181
"""
181182
```
182183

183-
**Annotating a changed API** — Use the `deprecated.sphinx.versionchanged` decorator:
184+
**Annotating a changed API** — Use the `versionchanged` decorator from the
185+
vendored `cuda.core._vendored.deprecated.sphinx` module:
184186

185187
```python
186188

187-
from deprecated.sphinx import versionchanged
189+
from cuda.core._vendored.deprecated.sphinx import versionchanged
188190

189191
@versionchanged("1.2.0", reason="The old version was broken because...")
190192
def new_feature(...):
@@ -205,13 +207,13 @@ def new_feature(...):
205207
```
206208

207209
**Deprecating an existing API** — use the `@deprecated` decorator from the
208-
`Deprecated` PyPI package (`deprecated.sphinx` import name) and add a
210+
vendored `cuda.core._vendored.deprecated.sphinx` module and add a
209211
`.. deprecated::` directive in the docstring. The decorator emits a
210212
`DeprecationWarning` at call time; the docstring directive surfaces it in the
211213
generated docs.
212214

213215
```python
214-
from deprecated.sphinx import deprecated
216+
from cuda.core._vendored.deprecated.sphinx import deprecated
215217

216218
@deprecated(version="1.2.0", reason="Use `new_feature` instead.")
217219
def old_feature(...):

cuda_core/cuda/core/_vendored/__init__.py

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Vendored from the Deprecated package (https://pypi.org/project/Deprecated/),
2+
# version 1.3.1, (c) Laurent LAPORTE, MIT License.
3+
# Modified to remove the dependency on the `wrapt` package.
4+
5+
from cuda.core._vendored.deprecated.classic import deprecated
6+
from cuda.core._vendored.deprecated.params import deprecated_params
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Vendored from the Deprecated package (https://pypi.org/project/Deprecated/),
2+
# version 1.3.1, (c) Laurent LAPORTE, MIT License.
3+
# Modified to remove the dependency on the `wrapt` package.
4+
5+
import functools
6+
import inspect
7+
import warnings
8+
9+
# stacklevel=2 points past the wrapper to the actual call site
10+
_routine_stacklevel = 2
11+
_class_stacklevel = 2
12+
13+
string_types = (bytes, str)
14+
15+
16+
class ClassicAdapter:
17+
"""
18+
Classic adapter -- *for advanced usage only*
19+
20+
This adapter is used to get the deprecation message according to the wrapped
21+
object type: class, function, standard method, static method, or class method.
22+
23+
This is the base class of the :class:`~deprecated.sphinx.SphinxAdapter` class
24+
which is used to update the wrapped object docstring.
25+
"""
26+
27+
def __init__(self, reason="", version="", action=None, category=DeprecationWarning, extra_stacklevel=0):
28+
self.reason = reason or ""
29+
self.version = version or ""
30+
self.action = action
31+
self.category = category
32+
self.extra_stacklevel = extra_stacklevel
33+
34+
def get_deprecated_msg(self, wrapped, instance):
35+
if instance is None:
36+
if inspect.isclass(wrapped):
37+
fmt = "Call to deprecated class {name}."
38+
else:
39+
fmt = "Call to deprecated function (or staticmethod) {name}."
40+
else:
41+
if inspect.isclass(instance):
42+
fmt = "Call to deprecated class method {name}."
43+
else:
44+
fmt = "Call to deprecated method {name}."
45+
if self.reason:
46+
fmt += " ({reason})"
47+
if self.version:
48+
fmt += " -- Deprecated since version {version}."
49+
return fmt.format(name=wrapped.__name__, reason=self.reason or "", version=self.version or "")
50+
51+
def __call__(self, wrapped):
52+
if inspect.isclass(wrapped):
53+
old_new1 = wrapped.__new__
54+
55+
def wrapped_cls(cls, *args, **kwargs):
56+
msg = self.get_deprecated_msg(wrapped, None)
57+
stacklevel = _class_stacklevel + self.extra_stacklevel
58+
if self.action:
59+
with warnings.catch_warnings():
60+
warnings.simplefilter(self.action, self.category)
61+
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
62+
else:
63+
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
64+
if old_new1 is object.__new__:
65+
return old_new1(cls)
66+
return old_new1(cls, *args, **kwargs)
67+
68+
wrapped.__new__ = staticmethod(wrapped_cls)
69+
return wrapped
70+
71+
elif inspect.isroutine(wrapped):
72+
adapter = self
73+
74+
@functools.wraps(wrapped)
75+
def wrapper(*args, **kwargs):
76+
msg = adapter.get_deprecated_msg(wrapped, None)
77+
stacklevel = _routine_stacklevel + adapter.extra_stacklevel
78+
if adapter.action:
79+
with warnings.catch_warnings():
80+
warnings.simplefilter(adapter.action, adapter.category)
81+
warnings.warn(msg, category=adapter.category, stacklevel=stacklevel)
82+
else:
83+
warnings.warn(msg, category=adapter.category, stacklevel=stacklevel)
84+
return wrapped(*args, **kwargs)
85+
86+
return wrapper
87+
88+
else:
89+
raise TypeError(repr(type(wrapped)))
90+
91+
92+
def deprecated(*args, **kwargs):
93+
"""
94+
Decorator which can be used to mark functions as deprecated.
95+
96+
It will result in a warning being emitted when the function is used.
97+
"""
98+
if args and isinstance(args[0], string_types):
99+
kwargs["reason"] = args[0]
100+
args = args[1:]
101+
102+
if args and not callable(args[0]):
103+
raise TypeError(repr(type(args[0])))
104+
105+
if args:
106+
adapter_cls = kwargs.pop("adapter_cls", ClassicAdapter)
107+
adapter = adapter_cls(**kwargs)
108+
wrapped = args[0]
109+
return adapter(wrapped)
110+
111+
return functools.partial(deprecated, **kwargs)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Vendored from the Deprecated package (https://pypi.org/project/Deprecated/),
2+
# version 1.3.1, (c) Laurent LAPORTE, MIT License.
3+
# Modified to remove the dependency on the `wrapt` package.
4+
5+
import collections
6+
import functools
7+
import inspect
8+
import warnings
9+
10+
11+
class DeprecatedParams:
12+
"""
13+
Decorator for functions where one or more parameters are deprecated.
14+
"""
15+
16+
def __init__(self, param, reason="", category=DeprecationWarning):
17+
self.messages = {}
18+
self.category = category
19+
self.populate_messages(param, reason=reason)
20+
21+
def populate_messages(self, param, reason=""):
22+
if isinstance(param, dict):
23+
self.messages.update(param)
24+
elif isinstance(param, str):
25+
fmt = "'{param}' parameter is deprecated"
26+
reason = reason or fmt.format(param=param)
27+
self.messages[param] = reason
28+
else:
29+
raise TypeError(param)
30+
31+
def check_params(self, signature, *args, **kwargs):
32+
binding = signature.bind(*args, **kwargs)
33+
bound = collections.OrderedDict(binding.arguments, **binding.kwargs)
34+
return [param for param in bound if param in self.messages]
35+
36+
def warn_messages(self, messages):
37+
for message in messages:
38+
warnings.warn(message, category=self.category, stacklevel=3)
39+
40+
def __call__(self, f):
41+
signature = inspect.signature(f)
42+
43+
@functools.wraps(f)
44+
def wrapper(*args, **kwargs):
45+
invalid_params = self.check_params(signature, *args, **kwargs)
46+
self.warn_messages([self.messages[param] for param in invalid_params])
47+
return f(*args, **kwargs)
48+
49+
return wrapper
50+
51+
52+
deprecated_params = DeprecatedParams
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Vendored from the Deprecated package (https://pypi.org/project/Deprecated/),
2+
# version 1.3.1, (c) Laurent LAPORTE, MIT License.
3+
# Modified to remove the dependency on the `wrapt` package.
4+
5+
import re
6+
import textwrap
7+
8+
from cuda.core._vendored.deprecated.classic import ClassicAdapter
9+
from cuda.core._vendored.deprecated.classic import deprecated as _classic_deprecated
10+
11+
12+
class SphinxAdapter(ClassicAdapter):
13+
"""
14+
Sphinx adapter -- *for advanced usage only*
15+
16+
This adapter overrides :class:`~deprecated.classic.ClassicAdapter` to add
17+
Sphinx directives ("versionadded", "versionchanged", "deprecated") to the
18+
end of the decorated function or class docstring.
19+
"""
20+
21+
def __init__(
22+
self,
23+
directive,
24+
reason="",
25+
version="",
26+
action=None,
27+
category=DeprecationWarning,
28+
extra_stacklevel=0,
29+
line_length=70,
30+
):
31+
if not version:
32+
raise ValueError("'version' argument is required in Sphinx directives")
33+
self.directive = directive
34+
self.line_length = line_length
35+
super().__init__(
36+
reason=reason, version=version, action=action, category=category, extra_stacklevel=extra_stacklevel
37+
)
38+
39+
def __call__(self, wrapped):
40+
fmt = ".. {directive}:: {version}" if self.version else ".. {directive}::"
41+
div_lines = [fmt.format(directive=self.directive, version=self.version)]
42+
width = self.line_length - 3 if self.line_length > 3 else 2**16
43+
reason = textwrap.dedent(self.reason).strip()
44+
for paragraph in reason.splitlines():
45+
if paragraph:
46+
div_lines.extend(
47+
textwrap.fill(
48+
paragraph,
49+
width=width,
50+
initial_indent=" ",
51+
subsequent_indent=" ",
52+
).splitlines()
53+
)
54+
else:
55+
div_lines.append("")
56+
57+
docstring = wrapped.__doc__ or ""
58+
lines = docstring.splitlines(True) or [""]
59+
docstring = textwrap.dedent("".join(lines[1:])) if len(lines) > 1 else ""
60+
docstring = lines[0] + docstring
61+
if docstring:
62+
docstring = re.sub(r"\n+$", "", docstring, flags=re.DOTALL) + "\n\n"
63+
else:
64+
docstring = "\n"
65+
66+
docstring += "".join(f"{line}\n" for line in div_lines)
67+
68+
wrapped.__doc__ = docstring
69+
if self.directive in {"versionadded", "versionchanged"}:
70+
return wrapped
71+
return super().__call__(wrapped)
72+
73+
def get_deprecated_msg(self, wrapped, instance):
74+
msg = super().get_deprecated_msg(wrapped, instance)
75+
msg = re.sub(r"(?: : [a-zA-Z]+ )? : [a-zA-Z]+ : (`[^`]*`)", r"\1", msg, flags=re.X)
76+
return msg
77+
78+
79+
def versionadded(reason="", version="", line_length=70):
80+
"""
81+
Decorator that inserts a "versionadded" Sphinx directive into the docstring.
82+
"""
83+
return SphinxAdapter(
84+
"versionadded",
85+
reason=reason,
86+
version=version,
87+
line_length=line_length,
88+
)
89+
90+
91+
def versionchanged(reason="", version="", line_length=70):
92+
"""
93+
Decorator that inserts a "versionchanged" Sphinx directive into the docstring.
94+
"""
95+
return SphinxAdapter(
96+
"versionchanged",
97+
reason=reason,
98+
version=version,
99+
line_length=line_length,
100+
)
101+
102+
103+
def deprecated(reason="", version="", line_length=70, **kwargs):
104+
"""
105+
Decorator that inserts a "deprecated" Sphinx directive into the docstring
106+
and emits a :exc:`DeprecationWarning` when the decorated object is called.
107+
"""
108+
directive = kwargs.pop("directive", "deprecated")
109+
adapter_cls = kwargs.pop("adapter_cls", SphinxAdapter)
110+
kwargs["reason"] = reason
111+
kwargs["version"] = version
112+
kwargs["line_length"] = line_length
113+
return _classic_deprecated(directive=directive, adapter_cls=adapter_cls, **kwargs)

cuda_core/cuda/core/system/_device.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ from typing import Iterable
66

77
import cuda.core
88
from cuda.bindings import nvml
9+
from cuda.core._vendored.deprecated.sphinx import (deprecated, versionadded,
10+
versionchanged)
911
from cuda.core.system.typing import (AddressingMode, AffinityScope, ClockId,
1012
ClocksEventReasons, ClockType,
1113
CoolerControl, CoolerTarget, DeviceArch,
@@ -14,7 +16,6 @@ from cuda.core.system.typing import (AddressingMode, AffinityScope, ClockId,
1416
GpuTopologyLevel, InforomObject,
1517
TemperatureThresholds, ThermalController,
1618
ThermalTarget)
17-
from deprecated.sphinx import deprecated, versionadded, versionchanged
1819

1920

2021
class ClockOffsets:

cuda_core/cuda/core/system/_device.pyx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ from multiprocessing import cpu_count
99
from typing import Iterable, TYPE_CHECKING
1010
import warnings
1111

12-
from deprecated.sphinx import deprecated, versionadded, versionchanged
13-
1412
from cuda.bindings import nvml
1513

1614
from ._nvml_context cimport initialize
@@ -35,6 +33,7 @@ from cuda.core.system.typing import (
3533
ThermalController,
3634
ThermalTarget,
3735
)
36+
from cuda.core._vendored.deprecated.sphinx import deprecated, versionadded, versionchanged
3837

3938
if TYPE_CHECKING:
4039
import cuda.core # no-cython-lint

0 commit comments

Comments
 (0)