Skip to content

Commit 25baa75

Browse files
ericapisaniclaude
andcommitted
ref: Remove residual Python 2 compatibility code
Python 2 support was dropped in SDK 2.0.0, but several Python 2 shims, dead branches, and stale comments remained. Clean them up: - Delete `with_metaclass()` from `_compat.py` and rewrite `class Hub(with_metaclass(HubMeta))` to use the native `metaclass=` keyword (also drops a `type: ignore`). - Remove the `im_class`/`im_func` try/except in `qualname_from_function` (unreachable on Python 3) and collapse the `__qualname__` vs `__name__` elif in both `utils.qualname_from_function` and the Django `signals_handlers._get_receiver_name` (Py3 always has `__qualname__`). - Drop the `try: ModuleNotFoundError except Exception: ImportError` shim in `client.py` — `ModuleNotFoundError` has existed since Py3.6 — and use `ModuleNotFoundError` directly at the call site. - Replace `super(_Client, self).__init__(...)` with `super().__init__(...)`. - Remove the `bdist_wheel universal=1` option from `setup.py`; universal wheels are for Py2/Py3-compatible packages and do not apply here. - Strip misleading Py2 references from comments in the Django middleware integration and in `tests/conftest.py`. No behavior change. Minimum supported Python stays at 3.6 — Python 3.6-vs-later compat code (e.g. `aiocontextvars`, `nullcontext`, `re.Pattern` fallback) is intentionally left alone. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e476bf5 commit 25baa75

8 files changed

Lines changed: 5 additions & 50 deletions

File tree

sentry_sdk/_compat.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
11
import sys
22

3-
from typing import TYPE_CHECKING
4-
5-
if TYPE_CHECKING:
6-
from typing import Any
7-
from typing import TypeVar
8-
9-
T = TypeVar("T")
10-
113

124
PY37 = sys.version_info[0] == 3 and sys.version_info[1] >= 7
135
PY38 = sys.version_info[0] == 3 and sys.version_info[1] >= 8
146
PY310 = sys.version_info[0] == 3 and sys.version_info[1] >= 10
157
PY311 = sys.version_info[0] == 3 and sys.version_info[1] >= 11
168

179

18-
def with_metaclass(meta: "Any", *bases: "Any") -> "Any":
19-
class MetaClass(type):
20-
def __new__(metacls: "Any", name: "Any", this_bases: "Any", d: "Any") -> "Any":
21-
return meta(name, bases, d)
22-
23-
return type.__new__(MetaClass, "temporary_class", (), {})
24-
25-
2610
def check_uwsgi_thread_support() -> bool:
2711
# We check two things here:
2812
#

sentry_sdk/client.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,6 @@ def _get_options(*args: "Optional[str]", **kwargs: "Any") -> "Dict[str, Any]":
167167
return rv
168168

169169

170-
try:
171-
# Python 3.6+
172-
module_not_found_error = ModuleNotFoundError
173-
except Exception:
174-
# Older Python versions
175-
module_not_found_error = ImportError # type: ignore
176-
177-
178170
class BaseClient:
179171
"""
180172
.. versionadded:: 2.0.0
@@ -289,7 +281,7 @@ class _Client(BaseClient):
289281
"""
290282

291283
def __init__(self, *args: "Any", **kwargs: "Any") -> None:
292-
super(_Client, self).__init__(options=get_options(*args, **kwargs))
284+
super().__init__(options=get_options(*args, **kwargs))
293285
self._init_impl()
294286

295287
def __getstate__(self) -> "Any":
@@ -318,7 +310,7 @@ def _setup_instrumentation(
318310
function_obj = getattr(module_obj, function_name)
319311
setattr(module_obj, function_name, trace(function_obj))
320312
logger.debug("Enabled tracing for %s", function_qualname)
321-
except module_not_found_error:
313+
except ModuleNotFoundError:
322314
try:
323315
# Try to import a class
324316
# ex: "mymodule.submodule.MyClassName.member_function"

sentry_sdk/hub.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
get_global_scope,
99
get_isolation_scope,
1010
)
11-
from sentry_sdk._compat import with_metaclass
1211
from sentry_sdk.client import Client
1312
from sentry_sdk.consts import INSTRUMENTER
1413
from sentry_sdk.scope import _ScopeManager
@@ -109,7 +108,7 @@ def main(cls) -> "Hub":
109108
return GLOBAL_HUB
110109

111110

112-
class Hub(with_metaclass(HubMeta)): # type: ignore
111+
class Hub(metaclass=HubMeta):
113112
"""
114113
.. deprecated:: 2.0.0
115114
The Hub is deprecated. Its functionality will be merged into :py:class:`sentry_sdk.scope.Scope`.

sentry_sdk/integrations/django/middleware.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ def sentry_wrapped_method(*args: "Any", **kwargs: "Any") -> "Any":
103103
return old_method(*args, **kwargs)
104104

105105
try:
106-
# fails for __call__ of function on Python 2 (see py2.7-django-1.11)
107106
sentry_wrapped_method = wraps(old_method)(sentry_wrapped_method)
108107

109108
# Necessary for Django 3.1

sentry_sdk/integrations/django/signals_handlers.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ def _get_receiver_name(receiver: "Callable[..., Any]") -> str:
1818

1919
if hasattr(receiver, "__qualname__"):
2020
name = receiver.__qualname__
21-
elif hasattr(receiver, "__name__"): # Python 2.7 has no __qualname__
22-
name = receiver.__name__
2321
elif hasattr(
2422
receiver, "func"
2523
): # certain functions (like partials) dont have a name

sentry_sdk/utils.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,16 +1472,6 @@ def qualname_from_function(func: "Callable[..., Any]") -> "Optional[str]":
14721472
"""Return the qualified name of func. Works with regular function, lambda, partial and partialmethod."""
14731473
func_qualname: "Optional[str]" = None
14741474

1475-
# Python 2
1476-
try:
1477-
return "%s.%s.%s" % (
1478-
func.im_class.__module__, # type: ignore
1479-
func.im_class.__name__, # type: ignore
1480-
func.__name__,
1481-
)
1482-
except Exception:
1483-
pass
1484-
14851475
prefix, suffix = "", ""
14861476

14871477
if isinstance(func, partial) and hasattr(func.func, "__name__"):
@@ -1499,10 +1489,7 @@ def qualname_from_function(func: "Callable[..., Any]") -> "Optional[str]":
14991489

15001490
if hasattr(func, "__qualname__"):
15011491
func_qualname = func.__qualname__
1502-
elif hasattr(func, "__name__"): # Python 2.7 has no __qualname__
1503-
func_qualname = func.__name__
15041492

1505-
# Python 3: methods, functions, classes
15061493
if func_qualname is not None:
15071494
if hasattr(func, "__module__") and isinstance(func.__module__, str):
15081495
func_qualname = func.__module__ + "." + func_qualname

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,4 @@ def get_file_text(file_name):
113113
"Programming Language :: Python :: 3.14",
114114
"Topic :: Software Development :: Libraries :: Python Modules",
115115
],
116-
options={"bdist_wheel": {"universal": "1"}},
117116
)

tests/conftest.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,6 @@ def __eq__(self, test_string):
527527
if not isinstance(test_string, self.valid_types):
528528
return False
529529

530-
# this is safe even in py2 because as of 2.6, `bytes` exists in py2
531-
# as an alias for `str`
532530
if isinstance(test_string, bytes):
533531
test_string = test_string.decode()
534532

@@ -548,9 +546,8 @@ def _safe_is_equal(x, y):
548546
Compares two values, preferring to use the first's __eq__ method if it
549547
exists and is implemented.
550548
551-
Accounts for py2/py3 differences (like ints in py2 not having a __eq__
552-
method), as well as the incomparability of certain types exposed by using
553-
raw __eq__ () rather than ==.
549+
Accounts for the incomparability of certain types exposed by using raw
550+
__eq__ () rather than ==.
554551
"""
555552

556553
# Prefer using __eq__ directly to ensure that examples like

0 commit comments

Comments
 (0)