Skip to content

Commit c632be5

Browse files
authored
Merge pull request #3230 from A5rocks/change-deprecated-attributes
Use PEP 562 instead of _ModuleWithDeprecations
2 parents a19343d + 6f94687 commit c632be5

File tree

5 files changed

+29
-38
lines changed

5 files changed

+29
-38
lines changed

newsfragments/2135.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow `trio` to be a `types.ModuleType` and still have deprecated attributes.

src/trio/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@
112112

113113
from . import _deprecate as _deprecate
114114

115-
_deprecate.enable_attribute_deprecations(__name__)
116-
117-
__deprecated_attributes__: dict[str, _deprecate.DeprecatedAttribute] = {}
115+
_deprecate.deprecate_attributes(__name__, {})
118116

119117
# Having the public path in .__module__ attributes is important for:
120118
# - exception names in printed tracebacks

src/trio/_deprecate.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import sys
44
import warnings
55
from functools import wraps
6-
from types import ModuleType
76
from typing import TYPE_CHECKING, ClassVar, TypeVar
87

98
import attrs
@@ -150,28 +149,20 @@ class DeprecatedAttribute:
150149
instead: object = _not_set
151150

152151

153-
class _ModuleWithDeprecations(ModuleType):
154-
__deprecated_attributes__: dict[str, DeprecatedAttribute]
155-
156-
def __getattr__(self, name: str) -> object:
157-
if name in self.__deprecated_attributes__:
158-
info = self.__deprecated_attributes__[name]
152+
def deprecate_attributes(
153+
module_name: str, deprecated_attributes: dict[str, DeprecatedAttribute]
154+
) -> None:
155+
def __getattr__(name: str) -> object:
156+
if name in deprecated_attributes:
157+
info = deprecated_attributes[name]
159158
instead = info.instead
160159
if instead is DeprecatedAttribute._not_set:
161160
instead = info.value
162-
thing = f"{self.__name__}.{name}"
161+
thing = f"{module_name}.{name}"
163162
warn_deprecated(thing, info.version, issue=info.issue, instead=instead)
164163
return info.value
165164

166165
msg = "module '{}' has no attribute '{}'"
167-
raise AttributeError(msg.format(self.__name__, name))
168-
166+
raise AttributeError(msg.format(module_name, name))
169167

170-
def enable_attribute_deprecations(module_name: str) -> None:
171-
module = sys.modules[module_name]
172-
module.__class__ = _ModuleWithDeprecations
173-
assert isinstance(module, _ModuleWithDeprecations)
174-
# Make sure that this is always defined so that
175-
# _ModuleWithDeprecations.__getattr__ can access it without jumping
176-
# through hoops or risking infinite recursion.
177-
module.__deprecated_attributes__ = {}
168+
sys.modules[module_name].__getattr__ = __getattr__ # type: ignore[method-assign]
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
regular = "hi"
22

3+
import sys
4+
35
from .. import _deprecate
46

5-
_deprecate.enable_attribute_deprecations(__name__)
6-
7-
# Make sure that we don't trigger infinite recursion when accessing module
8-
# attributes in between calling enable_attribute_deprecations and defining
9-
# __deprecated_attributes__:
10-
import sys
7+
_deprecate.deprecate_attributes(
8+
__name__,
9+
{
10+
"dep1": _deprecate.DeprecatedAttribute("value1", "1.1", issue=1),
11+
"dep2": _deprecate.DeprecatedAttribute(
12+
"value2",
13+
"1.2",
14+
issue=1,
15+
instead="instead-string",
16+
),
17+
},
18+
)
1119

1220
this_mod = sys.modules[__name__]
1321
assert this_mod.regular == "hi"
14-
assert not hasattr(this_mod, "dep1")
15-
16-
__deprecated_attributes__ = {
17-
"dep1": _deprecate.DeprecatedAttribute("value1", "1.1", issue=1),
18-
"dep2": _deprecate.DeprecatedAttribute(
19-
"value2",
20-
"1.2",
21-
issue=1,
22-
instead="instead-string",
23-
),
24-
}
22+
assert "dep1" not in globals()

src/trio/_tests/test_deprecate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import inspect
44
import warnings
5+
from types import ModuleType
56

67
import pytest
78

@@ -244,6 +245,8 @@ def test_module_with_deprecations(recwarn_always: pytest.WarningsRecorder) -> No
244245
assert module_with_deprecations.regular == "hi"
245246
assert len(recwarn_always) == 0
246247

248+
assert type(module_with_deprecations) is ModuleType
249+
247250
filename, lineno = _here()
248251
assert module_with_deprecations.dep1 == "value1" # type: ignore[attr-defined]
249252
got = recwarn_always.pop(DeprecationWarning)

0 commit comments

Comments
 (0)