Skip to content

Commit b661fc7

Browse files
committed
chore: add deprecation suppression, pull in deprecated dependency
This change adds a runtime dep of the 'deprecated' package- this https://pypi.org/project/Deprecated/ . However, being that snakeoil can be used by critical code, the internal snakeoil usage of it explicitly can fallback to py3.13 deprecated implementation, and if it's <py3.13 it just suppresses the deprecations in full. By "can fallback", this includes ImportError- broken import, and missing. It's not optimal, but this is sane enough frankly. Signed-off-by: Brian Harring <ferringb@gmail.com>
1 parent 03c2abd commit b661fc7

File tree

3 files changed

+50
-29
lines changed

3 files changed

+50
-29
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dynamic = ["version"]
2828

2929
dependencies = [
3030
"lazy-object-proxy",
31+
"deprecated~=1.2.18",
3132
]
3233

3334
[project.optional-dependencies]

src/snakeoil/deprecation.py

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,59 @@
1+
"""
2+
Deprecation functionally which gracefully degrades if degraded is missing, or <py3.13
3+
4+
All snakeoil code must use this module for deprecation functionality.
5+
6+
Snakeoil *must* work, thus the external dependency on 'degraded' module is desirable but
7+
shouldn't be strictly required- if that module doesn't exist (for whatever reason, despite
8+
deps), warn, and degrade back to >=py3.13 warning.degraded, and if <py3.13, do nothing.
9+
10+
Libraries using snakeoil should decide if they want this or not; pkgcore for example
11+
must use this library for similar 'must work' reasons, but non system critical software
12+
*should* use the degraded module directly for their own code, or use warning.degraded.
13+
14+
That's up to the author. This exists to allow having the degraded dep but not strictly
15+
require it, while providing shims that look like degraded.degraded if it's not available.
16+
"""
17+
118
__all__ = ("deprecated",)
219

20+
import functools
21+
import sys
322
import warnings
423
from contextlib import contextmanager
524

6-
_import_failed = False
725
try:
8-
from warnings import deprecated # pyright: ignore[reportAssignmentType]
9-
except ImportError:
10-
_import_failed = True
26+
from deprecated import deprecated as _deprecated_module_func
1127

12-
def deprecated(_message):
13-
"""
14-
This is a noop; deprecation warnings are disabled for pre python
15-
3.13.
16-
"""
28+
@functools.wraps(_deprecated_module_func)
29+
def deprecated(message, *args, **kwargs): # pyright: ignore[reportRedeclaration]
30+
"""Shim around deprecated.deprecated enforcing that message is always the first argument"""
31+
return _deprecated_module_func(*args, **kwargs)
32+
except ImportError:
33+
warnings.warn(
34+
"deprecated module could not be imported. Deprecation messages may not be shown"
35+
)
36+
if sys.version_info >= (3, 12, 0):
37+
# shim it, but drop the deprecated.deprecated metadata.
38+
def deprecated(message, *args, **kwargs):
39+
return warnings.deprecated(message)
40+
else:
41+
# stupid shitty python 3.11/3.12...
42+
def deprecated(_message, *args, **kwargs):
43+
"""
44+
This is disabled in full due to the deprecated module failing to import, and
45+
inability to fallback since the python version is less than 3.13
46+
"""
1747

18-
def f(thing):
19-
return thing
48+
def f(thing):
49+
return thing
2050

21-
return f
51+
return f
2252

2353

2454
@contextmanager
2555
def suppress_deprecation_warning():
26-
"""
27-
Used for suppressing all deprecation warnings beneath this
28-
29-
Use this for known deprecated code that is already addressed, but
30-
just waiting to die. Deprecated code calling deprecated code, specifically.
31-
"""
32-
if _import_failed:
33-
# noop.
56+
# see https://docs.python.org/3/library/warnings.html#temporarily-suppressing-warnings
57+
with warnings.catch_warnings():
58+
warnings.simplefilter(action="ignore", category=DeprecationWarning)
3459
yield
35-
else:
36-
# see https://docs.python.org/3/library/warnings.html#temporarily-suppressing-warnings
37-
with warnings.catch_warnings():
38-
warnings.simplefilter(action="ignore", category=DeprecationWarning)
39-
yield

tests/klass/test_init.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class c(dict):
150150
class Test_chained_getter:
151151
@staticmethod
152152
def kls(*args, **kwargs):
153-
with deprecated_call():
153+
with pytest.deprecated_call():
154154
kwargs.setdefault("disable_inst_caching", True)
155155
return klass.chained_getter(*args, **kwargs)
156156

@@ -161,7 +161,7 @@ def test_hash(self):
161161
def test_caching(self):
162162
# since it caches, it'll only trigger the warning the *first* time, thus
163163
# invoke this ourselves directly
164-
with deprecated_call():
164+
with pytest.deprecated_call():
165165
assert klass.chained_getter(
166166
"asdf", disable_inst_caching=False
167167
) is klass.chained_getter("asdf", disable_inst_caching=False)
@@ -518,12 +518,12 @@ class cls2:
518518

519519
class TestImmutableInstance:
520520
def test_metaclass(self):
521-
with deprecated_call():
521+
with pytest.deprecated_call():
522522
self.common_test(lambda x: x, metaclass=klass.immutable_instance)
523523

524524
def test_injection(self):
525525
def f(scope):
526-
with deprecated_call():
526+
with pytest.deprecated_call():
527527
klass.inject_immutable_instance(scope)
528528

529529
self.common_test(f)

0 commit comments

Comments
 (0)