From 2f8f40f1d89c5c0632eb284edd5897ece15c13fd Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Wed, 2 Oct 2024 12:45:15 +0300 Subject: [PATCH] Update to v2.15.0 (fedora#2310848) --- .gitignore | 1 + ...t-add_query_source-with-modules-outs.patch | 238 ------------------ ...add_query_source-with-modules-outsid.patch | 97 ------- 0001-tests-reorder-to-unpin-pytest.patch | 111 ++++++++ python-sentry-sdk.spec | 10 +- sources | 2 +- 6 files changed, 117 insertions(+), 342 deletions(-) delete mode 100644 0000-test-tracing-Test-add_query_source-with-modules-outs.patch delete mode 100644 0001-fix-tracing-Fix-add_query_source-with-modules-outsid.patch create mode 100644 0001-tests-reorder-to-unpin-pytest.patch diff --git a/.gitignore b/.gitignore index b2717d6..07a1e04 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ /sentry-python-2.1.1.tar.gz /sentry-python-2.7.1.tar.gz /sentry-python-2.13.0.tar.gz +/sentry-python-2.15.0.tar.gz diff --git a/0000-test-tracing-Test-add_query_source-with-modules-outs.patch b/0000-test-tracing-Test-add_query_source-with-modules-outs.patch deleted file mode 100644 index 04781f4..0000000 --- a/0000-test-tracing-Test-add_query_source-with-modules-outs.patch +++ /dev/null @@ -1,238 +0,0 @@ -From da164dad9eea765f2156cc44ef9a86a848aff4d9 Mon Sep 17 00:00:00 2001 -From: Roman Inflianskas -Date: Thu, 18 Jul 2024 18:54:14 +0300 -Subject: [PATCH 1/2] test(tracing): Test `add_query_source` with modules - outside of project root - -When packages added in `in_app_include` are installed to a location -outside of the project root directory, span from those packages are not -extended with OTel compatible source code information. Cases include -running Python from virtualenv created outside of the project root -directory or Python packages installed into the system using package -managers. This results in an inconsistency: spans from the same project -are be different, depending on the deployment method. - -The change extends `test_query_source_with_in_app_include` to test the -simulation of Django installed outside of the project root. - -The steps to manually reproduce the issue are as follows -(case: a virtual environment created outside of the project root): -```bash -docker run --replace --rm --detach \ - --name sentry-postgres \ - --env POSTGRES_USER=sentry \ - --env POSTGRES_PASSWORD=sentry \ - --publish 5432:5432 \ - postgres -distrobox create \ - --image ubuntu:24.04 \ - --name sentry-test-in_app_include-venv -distrobox enter sentry-test-in_app_include-venv -python3 -m venv /tmp/.venv-test-in_app_include -source /tmp/.venv-test-in_app_include/bin/activate -pip install \ - -r requirements-devenv.txt \ - pytest-django \ - psycopg2-binary \ - -e .[django] -export SENTRY_PYTHON_TEST_POSTGRES_USER=sentry -export SENTRY_PYTHON_TEST_POSTGRES_PASSWORD=sentry -pytest tests/integrations/django/test_db_query_data.py \ - -k test_query_source_with_in_app_include # FAIL -``` - -The steps to manually reproduce the issue are as follows -(case: Django is installed through system packages): -```bash -docker run --replace --rm --detach \ - --name sentry-postgres \ - --env POSTGRES_USER=sentry \ - --env POSTGRES_PASSWORD=sentry \ - --publish 5432:5432 \ - postgres -distrobox create \ - --image ubuntu:24.04 \ - --name sentry-test-in_app_include-os -distrobox enter sentry-test-in_app_include-os -sudo apt install \ - python3-django python3-pytest python3-pytest-cov \ - python3-pytest-django python3-jsonschema python3-urllib3 \ - python3-certifi python3-werkzeug python3-psycopg2 -export SENTRY_PYTHON_TEST_POSTGRES_USER=sentry -export SENTRY_PYTHON_TEST_POSTGRES_PASSWORD=sentry -pytest tests/integrations/django/test_db_query_data.py \ - -k test_query_source_with_in_app_include # FAIL -``` ---- - sentry_sdk/tracing_utils.py | 21 ++++---- - sentry_sdk/utils.py | 5 +- - .../integrations/django/test_db_query_data.py | 54 +++++++++++++++++-- - 3 files changed, 66 insertions(+), 14 deletions(-) - -diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py -index ba20dc84..47e14ecd 100644 ---- a/sentry_sdk/tracing_utils.py -+++ b/sentry_sdk/tracing_utils.py -@@ -170,6 +170,14 @@ def maybe_create_breadcrumbs_from_span(scope, span): - ) - - -+def _get_frame_module_abs_path(frame): -+ # type: (FrameType) -> Optional[str] -+ try: -+ return frame.f_code.co_filename -+ except Exception: -+ return None -+ -+ - def add_query_source(span): - # type: (sentry_sdk.tracing.Span) -> None - """ -@@ -200,10 +208,7 @@ def add_query_source(span): - # Find the correct frame - frame = sys._getframe() # type: Union[FrameType, None] - while frame is not None: -- try: -- abs_path = frame.f_code.co_filename -- except Exception: -- abs_path = "" -+ abs_path = _get_frame_module_abs_path(frame) - - try: - namespace = frame.f_globals.get("__name__") # type: Optional[str] -@@ -224,7 +229,8 @@ def add_query_source(span): - should_be_included = True - - if ( -- abs_path.startswith(project_root) -+ abs_path is not None -+ and abs_path.startswith(project_root) - and should_be_included - and not is_sentry_sdk_frame - ): -@@ -250,10 +256,7 @@ def add_query_source(span): - if namespace is not None: - span.set_data(SPANDATA.CODE_NAMESPACE, namespace) - -- try: -- filepath = frame.f_code.co_filename -- except Exception: -- filepath = None -+ filepath = _get_frame_module_abs_path(frame) - if filepath is not None: - if namespace is not None: - in_app_path = filename_for_module(namespace, filepath) -diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py -index 8a805d3d..fcbff45e 100644 ---- a/sentry_sdk/utils.py -+++ b/sentry_sdk/utils.py -@@ -1058,8 +1058,11 @@ def _module_in_list(name, items): - - - def _is_external_source(abs_path): -- # type: (str) -> bool -+ # type: (Optional[str]) -> bool - # check if frame is in 'site-packages' or 'dist-packages' -+ if abs_path is None: -+ return False -+ - external_source = ( - re.search(r"[\\/](?:dist|site)-packages[\\/]", abs_path) is not None - ) -diff --git a/tests/integrations/django/test_db_query_data.py b/tests/integrations/django/test_db_query_data.py -index 087fc5ad..265c6a79 100644 ---- a/tests/integrations/django/test_db_query_data.py -+++ b/tests/integrations/django/test_db_query_data.py -@@ -1,7 +1,9 @@ -+import contextlib - import os - - import pytest - from datetime import datetime -+from pathlib import Path, PurePosixPath, PureWindowsPath - from unittest import mock - - from django import VERSION as DJANGO_VERSION -@@ -15,14 +17,20 @@ except ImportError: - from werkzeug.test import Client - - from sentry_sdk import start_transaction -+from sentry_sdk._types import TYPE_CHECKING - from sentry_sdk.consts import SPANDATA - from sentry_sdk.integrations.django import DjangoIntegration --from sentry_sdk.tracing_utils import record_sql_queries -+from sentry_sdk.tracing_utils import _get_frame_module_abs_path, record_sql_queries -+from sentry_sdk.utils import _module_in_list - - from tests.conftest import unpack_werkzeug_response - from tests.integrations.django.utils import pytest_mark_django_db_decorator - from tests.integrations.django.myapp.wsgi import application - -+if TYPE_CHECKING: -+ from types import FrameType -+ from typing import Optional -+ - - @pytest.fixture - def client(): -@@ -283,7 +291,10 @@ def test_query_source_with_in_app_exclude(sentry_init, client, capture_events): - - @pytest.mark.forked - @pytest_mark_django_db_decorator(transaction=True) --def test_query_source_with_in_app_include(sentry_init, client, capture_events): -+@pytest.mark.parametrize("django_outside_of_project_root", [False, True]) -+def test_query_source_with_in_app_include( -+ sentry_init, client, capture_events, django_outside_of_project_root -+): - sentry_init( - integrations=[DjangoIntegration()], - send_default_pii=True, -@@ -301,8 +312,43 @@ def test_query_source_with_in_app_include(sentry_init, client, capture_events): - - events = capture_events() - -- _, status, _ = unpack_werkzeug_response(client.get(reverse("postgres_select_orm"))) -- assert status == "200 OK" -+ # Simulate Django installation outside of the project root -+ original_get_frame_module_abs_path = _get_frame_module_abs_path -+ -+ def patched_get_frame_module_abs_path_function(frame): -+ # type: (FrameType) -> Optional[str] -+ result = original_get_frame_module_abs_path(frame) -+ if result is None: -+ return result -+ -+ namespace = frame.f_globals.get("__name__") -+ if _module_in_list(namespace, ["django"]): -+ # Since the result is used as `str` only and not accessed, -+ # it is sufficient to generate non-existent path -+ # that would be located outside of the project root. -+ # Use UNC path for simplicity on Windows. -+ non_existent_prefix = ( -+ PureWindowsPath("//outside-of-project-root") -+ if os.name == "nt" -+ else PurePosixPath("/outside-of-project-root") -+ ) -+ result = str(non_existent_prefix.joinpath(*Path(result).parts[1:])) -+ return result -+ -+ patched_get_frame_module_abs_path = ( -+ mock.patch( -+ "sentry_sdk.tracing_utils._get_frame_module_abs_path", -+ patched_get_frame_module_abs_path_function, -+ ) -+ if django_outside_of_project_root -+ else contextlib.suppress() -+ ) -+ -+ with patched_get_frame_module_abs_path: -+ _, status, _ = unpack_werkzeug_response( -+ client.get(reverse("postgres_select_orm")) -+ ) -+ assert status == "200 OK" - - (event,) = events - for span in event["spans"]: --- -2.45.2 - diff --git a/0001-fix-tracing-Fix-add_query_source-with-modules-outsid.patch b/0001-fix-tracing-Fix-add_query_source-with-modules-outsid.patch deleted file mode 100644 index 3eda4db..0000000 --- a/0001-fix-tracing-Fix-add_query_source-with-modules-outsid.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 7ff39563c20b83809ae2378479d0324880231b33 Mon Sep 17 00:00:00 2001 -From: Roman Inflianskas -Date: Thu, 18 Jul 2024 16:57:21 +0300 -Subject: [PATCH 2/2] fix(tracing): Fix `add_query_source` with modules outside - of project root - -Fix: https://github.com/getsentry/sentry-python/issues/3312 - -Previously, when packages added in `in_app_include` were installed -to a location outside of the project root directory, span from -those packages were not extended with OTel compatible source code -information. Cases include running Python from virtualenv created -outside of the project root directory or Python packages installed into -the system using package managers. This resulted in an inconsistency: -spans from the same project would be different, depending on the -deployment method. - -In this change, the logic was slightly changed to avoid these -discrepancies and conform to the requirements, described in the PR with -better setting of in-app in stack frames: -https://github.com/getsentry/sentry-python/pull/1894#issue-1579192436. -Note that the `_module_in_list` function returns `False` if `name` is -`None` or `items` are falsy, hence extra check before function call can -be omitted to simplify code. ---- - sentry_sdk/tracing_utils.py | 22 ++++++++-------------- - sentry_sdk/utils.py | 6 +++--- - 2 files changed, 11 insertions(+), 17 deletions(-) - -diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py -index 47e14ecd..bfcf8d0a 100644 ---- a/sentry_sdk/tracing_utils.py -+++ b/sentry_sdk/tracing_utils.py -@@ -22,6 +22,7 @@ from sentry_sdk.utils import ( - is_sentry_url, - _is_external_source, - _module_in_list, -+ _is_in_project_root, - ) - from sentry_sdk._types import TYPE_CHECKING - -@@ -218,21 +219,14 @@ def add_query_source(span): - is_sentry_sdk_frame = namespace is not None and namespace.startswith( - "sentry_sdk." - ) -+ should_be_included = _module_in_list(namespace, in_app_include) -+ should_be_excluded = _is_external_source(abs_path) or _module_in_list( -+ namespace, in_app_exclude -+ ) - -- should_be_included = not _is_external_source(abs_path) -- if namespace is not None: -- if in_app_exclude and _module_in_list(namespace, in_app_exclude): -- should_be_included = False -- if in_app_include and _module_in_list(namespace, in_app_include): -- # in_app_include takes precedence over in_app_exclude, so doing it -- # at the end -- should_be_included = True -- -- if ( -- abs_path is not None -- and abs_path.startswith(project_root) -- and should_be_included -- and not is_sentry_sdk_frame -+ if not is_sentry_sdk_frame and ( -+ should_be_included -+ or (_is_in_project_root(abs_path, project_root) and not should_be_excluded) - ): - break - -diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py -index fcbff45e..22df7aef 100644 ---- a/sentry_sdk/utils.py -+++ b/sentry_sdk/utils.py -@@ -1043,7 +1043,7 @@ def event_from_exception( - - - def _module_in_list(name, items): -- # type: (str, Optional[List[str]]) -> bool -+ # type: (Optional[str], Optional[List[str]]) -> bool - if name is None: - return False - -@@ -1070,8 +1070,8 @@ def _is_external_source(abs_path): - - - def _is_in_project_root(abs_path, project_root): -- # type: (str, Optional[str]) -> bool -- if project_root is None: -+ # type: (Optional[str], Optional[str]) -> bool -+ if abs_path is None or project_root is None: - return False - - # check if path is in the project root --- -2.45.2 - diff --git a/0001-tests-reorder-to-unpin-pytest.patch b/0001-tests-reorder-to-unpin-pytest.patch new file mode 100644 index 0000000..da27295 --- /dev/null +++ b/0001-tests-reorder-to-unpin-pytest.patch @@ -0,0 +1,111 @@ +From 3ba07652d54b6fe45239c9c9b4ea1e9edac5c976 Mon Sep 17 00:00:00 2001 +From: Roman Inflianskas +Date: Wed, 2 Oct 2024 14:15:13 +0300 +Subject: [PATCH] tests: reorder to unpin pytest + +Fix: #3035 + +Separate forked tests from normal tests within each module during +collection phase to avoid issues, caused by `pytest-forked`. + +Workaround to unpin pytest. See: +https://github.com/pytest-dev/pytest/issues/9621, +https://github.com/pytest-dev/pytest-forked/issues/67, and specifically: +https://github.com/pytest-dev/pytest-forked/issues/67#issuecomment-1964718720 +--- + requirements-devenv.txt | 1 - + tests/conftest.py | 38 ++++++++++++++++++++++++++++++++++++++ + tox.ini | 10 ---------- + 3 files changed, 38 insertions(+), 11 deletions(-) + +diff --git a/requirements-devenv.txt b/requirements-devenv.txt +index 29d3f15e..5696567a 100644 +--- a/requirements-devenv.txt ++++ b/requirements-devenv.txt +@@ -1,5 +1,4 @@ + -r requirements-linting.txt + -r requirements-testing.txt + mockupdb # required by `pymongo` tests that are enabled by `pymongo` from linter requirements +-pytest<7.0.0 # https://github.com/pytest-dev/pytest/issues/9621; see tox.ini + pytest-asyncio +diff --git a/tests/conftest.py b/tests/conftest.py +index 64527c1e..bad96e92 100644 +--- a/tests/conftest.py ++++ b/tests/conftest.py +@@ -645,3 +645,41 @@ class ApproxDict(dict): + + def __ne__(self, other): + return not self.__eq__(other) ++ ++ ++def pytest_collection_modifyitems(config, items): ++ """ ++ Rearrange test items so that isolated tests come before normal tests within their respective modules. ++ Swap the last isolated test with the last normal test if necessary. ++ ++ Workaround to unpin pytest. See: ++ https://github.com/pytest-dev/pytest/issues/9621, ++ https://github.com/pytest-dev/pytest-forked/issues/67, and specifically: ++ https://github.com/pytest-dev/pytest-forked/issues/67#issuecomment-1964718720 ++ """ ++ module_states = {} ++ ++ for idx in range(len(items)): ++ item = items[idx] ++ current_module = item.module.__name__ ++ ++ if current_module not in module_states: ++ module_states[current_module] = {"forked": [], "normal": []} ++ ++ if "forked" in item.keywords: ++ module_states[current_module]["forked"].append(idx) ++ else: ++ module_states[current_module]["normal"].append(idx) ++ ++ # Swap the last forked test with the last normal test if necessary ++ for states in module_states.values(): ++ if states["forked"] and states["normal"]: ++ last_forked_idx = states["forked"][-1] ++ last_normal_idx = states["normal"][-1] ++ ++ if last_forked_idx > last_normal_idx: ++ # Swap the items ++ items[last_forked_idx], items[last_normal_idx] = ( ++ items[last_normal_idx], ++ items[last_forked_idx], ++ ) +diff --git a/tox.ini b/tox.ini +index 2f351d7e..7af1c7b8 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -290,19 +290,10 @@ deps = + # === Common === + py3.8-common: hypothesis + {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 + {py3.12}-gevent: gevent +- # 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}-gevent: pytest<7.0.0 + + # === Integrations === + +@@ -372,7 +363,6 @@ deps = + celery-latest: Celery + + celery: newrelic +- celery: pytest<7 + {py3.7}-celery: importlib-metadata<5.0 + + # Chalice +-- +2.46.2 + diff --git a/python-sentry-sdk.spec b/python-sentry-sdk.spec index 7b712d8..51ee355 100644 --- a/python-sentry-sdk.spec +++ b/python-sentry-sdk.spec @@ -44,7 +44,7 @@ %bcond network_tests 0 %global forgeurl https://github.com/getsentry/sentry-python -Version: 2.13.0 +Version: 2.15.0 %global tag %{version} %forgemeta @@ -54,10 +54,8 @@ Summary: The new Python SDK for Sentry.io License: MIT URL: https://sentry.io/for/python/ Source0: %{forgesource} -# Patches for testing and fixing logic for handling `in_app_include` in `add_query_source` -# Upstream PR: https://github.com/getsentry/sentry-python/pull/3313 -Patch0: 0000-test-tracing-Test-add_query_source-with-modules-outs.patch -Patch1: 0001-fix-tracing-Fix-add_query_source-with-modules-outsid.patch +# Upstream PR: https://github.com/getsentry/sentry-python/pull/3598 +Patch0: 0001-tests-reorder-to-unpin-pytest.patch BuildArch: noarch BuildRequires: python3-devel @@ -269,7 +267,7 @@ diff <(echo "$defined_extra") <(echo "$setup_py_extra") sed -r -i 's/psycopg2-binary/psycopg2/' tox.ini # Unpin all test dependencies to make the installation happen. -sed -r -i 's/(pytest)<7\.0\.0/\1/' tox.ini +sed -r -i 's/(pytest)<7.*/\1/' tox.ini sed -r -i 's/(Werkzeug)<2\.1\.0/\1/' tox.ini sed -r -i 's/(gevent)>=22\.10\.0, <22\.11\.0/\1/' tox.ini sed -r -i 's/(anyio)<4\.0\.0/\1/' tox.ini diff --git a/sources b/sources index 7623977..5bc7543 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (sentry-python-2.13.0.tar.gz) = 4dd48d8acd1a132d93e08bb44028b7ec88b75bb821acbc3376391527e113c38abd74b4bc535b9a1f79fa3647bbafb22de85a6a553dc2c61fbd6095ffb39c6f32 +SHA512 (sentry-python-2.15.0.tar.gz) = 975b45acecbea4c7e6a6eb5b77627f43a515f15e6ac3164bfbff49bf45fa5be0f9443a0c4f1ab9cc05b152707cdf0c3d97df3cf327daa2690a32b1b3f977c5ba