Update to v2.15.0 (fedora#2310848)

This commit is contained in:
Roman Inflianskas 2024-10-02 12:45:15 +03:00
parent 30fb39586a
commit 2f8f40f1d8
No known key found for this signature in database
6 changed files with 117 additions and 342 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -1,238 +0,0 @@
From da164dad9eea765f2156cc44ef9a86a848aff4d9 Mon Sep 17 00:00:00 2001
From: Roman Inflianskas <rominf@pm.me>
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

View file

@ -1,97 +0,0 @@
From 7ff39563c20b83809ae2378479d0324880231b33 Mon Sep 17 00:00:00 2001
From: Roman Inflianskas <rominf@pm.me>
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

View file

@ -0,0 +1,111 @@
From 3ba07652d54b6fe45239c9c9b4ea1e9edac5c976 Mon Sep 17 00:00:00 2001
From: Roman Inflianskas <rominf@pm.me>
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

View file

@ -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

View file

@ -1 +1 @@
SHA512 (sentry-python-2.13.0.tar.gz) = 4dd48d8acd1a132d93e08bb44028b7ec88b75bb821acbc3376391527e113c38abd74b4bc535b9a1f79fa3647bbafb22de85a6a553dc2c61fbd6095ffb39c6f32
SHA512 (sentry-python-2.15.0.tar.gz) = 975b45acecbea4c7e6a6eb5b77627f43a515f15e6ac3164bfbff49bf45fa5be0f9443a0c4f1ab9cc05b152707cdf0c3d97df3cf327daa2690a32b1b3f977c5ba