Skip to content

Commit

Permalink
feat: Preliminary support for Python 3.13 (#3200)
Browse files Browse the repository at this point in the history
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

---------

Co-authored-by: Roman Inflianskas <[email protected]>
  • Loading branch information
sentrivana and rominf authored Jul 16, 2024
1 parent cf8e37f commit a98f660
Show file tree
Hide file tree
Showing 15 changed files with 43 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-ai.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -96,6 +97,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-integrations-aws-lambda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-cloud-computing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -92,6 +93,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/test-integrations-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]
python-version: ["3.6","3.7","3.8","3.9","3.10","3.11","3.12","3.13"]
# python3.6 reached EOL and is no longer being supported on
# new versions of hosted runners on Github Actions
# ubuntu-20.04 is the last version that supported python3.6
Expand All @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-data-processing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Start Redis
uses: supercharge/[email protected]
- name: Setup Test Env
Expand Down Expand Up @@ -102,6 +103,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Start Redis
uses: supercharge/[email protected]
- name: Setup Test Env
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-databases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- uses: getsentry/action-clickhouse-in-ci@v1
- name: Setup Test Env
run: |
Expand Down Expand Up @@ -137,6 +138,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- uses: getsentry/action-clickhouse-in-ci@v1
- name: Setup Test Env
run: |
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-graphql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -92,6 +93,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-miscellaneous.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -96,6 +97,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-networking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -92,6 +93,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-web-frameworks-1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -128,6 +129,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-integrations-web-frameworks-2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down Expand Up @@ -112,6 +113,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Setup Test Env
run: |
pip install coverage tox
Expand Down
1 change: 1 addition & 0 deletions scripts/split-tox-gh-actions/templates/test_group.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- uses: actions/setup-python@v5
with:
python-version: {% raw %}${{ matrix.python-version }}{% endraw %}
allow-prereleases: true
{% if needs_clickhouse %}
- uses: getsentry/action-clickhouse-in-ci@v1
{% endif %}
Expand Down
19 changes: 11 additions & 8 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import threading
import time
from collections import namedtuple
from copy import copy
from datetime import datetime
from decimal import Decimal
from functools import partial, partialmethod, wraps
Expand Down Expand Up @@ -611,7 +610,7 @@ def serialize_frame(
)

if include_local_variables:
rv["vars"] = copy(frame.f_locals)
rv["vars"] = frame.f_locals.copy()

return rv

Expand Down Expand Up @@ -1330,14 +1329,18 @@ def qualname_from_function(func):

prefix, suffix = "", ""

if hasattr(func, "_partialmethod") and isinstance(
func._partialmethod, partialmethod
):
prefix, suffix = "partialmethod(<function ", ">)"
func = func._partialmethod.func
elif isinstance(func, partial) and hasattr(func.func, "__name__"):
if isinstance(func, partial) and hasattr(func.func, "__name__"):
prefix, suffix = "partial(<function ", ">)"
func = func.func
else:
# The _partialmethod attribute of methods wrapped with partialmethod() was renamed to __partialmethod__ in CPython 3.13:
# https://github.com/python/cpython/pull/16600
partial_method = getattr(func, "_partialmethod", None) or getattr(
func, "__partialmethod__", None
)
if isinstance(partial_method, partialmethod):
prefix, suffix = "partialmethod(<function ", ">)"
func = partial_method.func

if hasattr(func, "__qualname__"):
func_qualname = func.__qualname__
Expand Down
7 changes: 7 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
from sentry_sdk._types import Event


maximum_python_312 = pytest.mark.skipif(
sys.version_info > (3, 12),
reason="Since Python 3.13, `FrameLocalsProxy` skips items of `locals()` that have non-`str` keys; this is a CPython implementation detail: https://github.com/python/cpython/blame/7b413952e817ae87bfda2ac85dd84d30a6ce743b/Objects/frameobject.c#L148",
)


class EnvelopeCapturedError(Exception):
pass

Expand Down Expand Up @@ -889,6 +895,7 @@ class FooError(Exception):
assert exception["mechanism"]["meta"]["errno"]["number"] == 69


@maximum_python_312
def test_non_string_variables(sentry_init, capture_events):
"""There is some extremely terrible code in the wild that
inserts non-strings as variable names into `locals()`."""
Expand Down
5 changes: 3 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ requires =
virtualenv<20.26.3
envlist =
# === Common ===
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-common
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-common

# === Gevent ===
{py3.6,py3.8,py3.10,py3.11,py3.12}-gevent
Expand Down Expand Up @@ -271,11 +271,12 @@ deps =

# === Common ===
py3.8-common: hypothesis
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-common: pytest-asyncio
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-common: pytest-asyncio
# See https://github.com/pytest-dev/pytest/issues/9621
# and https://github.com/pytest-dev/pytest-forked/issues/67
# for justification of the upper bound on pytest
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-common: pytest<7.0.0
py3.13-common: pytest

# === Gevent ===
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-gevent: gevent>=22.10.0, <22.11.0
Expand Down

0 comments on commit a98f660

Please sign in to comment.