Skip to content

Commit 298a192

Browse files
committed
feat: Preliminary support for Python 3.13 (getsentry#3200)
Partial cherry-pick of: a98f660 Adding preliminary support for Python 3.13. The `_partialmethod` attribute of methods wrapped with `partialmethod()` was renamed to `__partialmethod__` in CPython 3.13: python/cpython#16600 Starting from Python 3.13, `frame.f_locals` is not `dict` anymore, but `FrameLocalsProxy`, that cannot be copied using `copy.copy()`. In Python 3.13 and later, it should be copied using a method `.copy()`. The new way of copying works the same as the old one for versions of Python prior to 3.13, according to the documentation (both copying methods produce a shallow copy). Since Python 3.13, `FrameLocalsProxy` skips items of `locals()` that have non-`str` keys; this is a CPython implementation detail, so we hence disable `test_non_string_variables` test on Python 3.13. See: https://peps.python.org/pep-0667/ python/cpython#118921 python/cpython#118923 https://docs.python.org/3.13/whatsnew/3.13.html#porting-to-python-3-13 https://docs.python.org/3/library/copy.html https://github.com/python/cpython/blame/7b413952e817ae87bfda2ac85dd84d30a6ce743b/Objects/frameobject.c#L148
1 parent b026dbd commit 298a192

1 file changed

Lines changed: 11 additions & 8 deletions

File tree

sentry_sdk/utils.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import threading
1212
import time
1313
from collections import namedtuple
14-
from copy import copy
1514
from datetime import datetime
1615
from decimal import Decimal
1716
from functools import partial, partialmethod, wraps
@@ -611,7 +610,7 @@ def serialize_frame(
611610
)
612611

613612
if include_local_variables:
614-
rv["vars"] = copy(frame.f_locals)
613+
rv["vars"] = frame.f_locals.copy()
615614

616615
return rv
617616

@@ -1330,14 +1329,18 @@ def qualname_from_function(func):
13301329

13311330
prefix, suffix = "", ""
13321331

1333-
if hasattr(func, "_partialmethod") and isinstance(
1334-
func._partialmethod, partialmethod
1335-
):
1336-
prefix, suffix = "partialmethod(<function ", ">)"
1337-
func = func._partialmethod.func
1338-
elif isinstance(func, partial) and hasattr(func.func, "__name__"):
1332+
if isinstance(func, partial) and hasattr(func.func, "__name__"):
13391333
prefix, suffix = "partial(<function ", ">)"
13401334
func = func.func
1335+
else:
1336+
# The _partialmethod attribute of methods wrapped with partialmethod() was renamed to __partialmethod__ in CPython 3.13:
1337+
# https://github.com/python/cpython/pull/16600
1338+
partial_method = getattr(func, "_partialmethod", None) or getattr(
1339+
func, "__partialmethod__", None
1340+
)
1341+
if isinstance(partial_method, partialmethod):
1342+
prefix, suffix = "partialmethod(<function ", ">)"
1343+
func = partial_method.func
13411344

13421345
if hasattr(func, "__qualname__"):
13431346
func_qualname = func.__qualname__

0 commit comments

Comments
 (0)