From bd86719301b187c38a360f98e9900053b88c69fb Mon Sep 17 00:00:00 2001 From: Yacine Date: Thu, 15 Apr 2021 16:01:59 -0400 Subject: [PATCH 01/40] Add decorator for Sentry tracing --- sentry_sdk/decorators.py | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 sentry_sdk/decorators.py diff --git a/sentry_sdk/decorators.py b/sentry_sdk/decorators.py new file mode 100644 index 0000000000..7f92fcf50d --- /dev/null +++ b/sentry_sdk/decorators.py @@ -0,0 +1,89 @@ +import logging +from typing import Optional +from functools import wraps +import inspect + +import sentry_sdk + +logger = logging.getLogger(__name__) + + +def sentry_traced( + func=None, + *, + transaction_name: Optional[str] = None, + op: Optional[str] = None, +): + """ + Function decorator to start a child under the existing current transaction or + under a new transaction, if it doesn't exist. + + Args: + transaction_name: the name of the new transaction if no transaction already + exists. If a transaction is found in current scope, this name is ignored. + If no transaction is found and no transaction name is provided, no action + is taken. + op: the name of the child. Defaults to the decorated function name. + + Returns: + a decorated function executing within a Sentry transaction child + + Usage: + @sentry_traced + def my_function(): + ... + + @sentry_traced(transaction_name="new_tx") + async def my_async_function(): + ... + + @sentry_trace(op="child_name") + def my_function(): + ... + """ + + def start_child_decorator(func): + def _transaction_and_op(): + # Set transaction + transaction = sentry_sdk.Hub.current.scope.transaction + # If no current transaction we create one + if transaction_name and transaction is None: + transaction = sentry_sdk.start_transaction(name=transaction_name) + # Child name - defaults to the decorated function name + op_ = op or func.__name__ + return transaction, op_ + + # Asynchronous case + if inspect.iscoroutinefunction(func): + + @wraps(func) + async def func_with_tracing(*args, **kwargs): + transaction, op_ = _transaction_and_op() + # If no transaction, do nothing + if transaction is None: + return await func(*args, **kwargs) + # If we have a transaction, we decorate the function! + with transaction.start_child(op=op_): + return await func(*args, **kwargs) + + # Synchronous case + else: + + @wraps(func) + def func_with_tracing(*args, **kwargs): + transaction, op_ = _transaction_and_op() + # If no transaction, do nothing + if transaction is None: + return func(*args, **kwargs) + # If we have a transaction, we decorate the function! + with transaction.start_child(op=op_): + return func(*args, **kwargs) + + return func_with_tracing + + # This patterns allows usage of both @sentry_traced and @sentry_traced(...) + # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 + if func: + return start_child_decorator(func) + else: + return start_child_decorator From 23d23defb8b28e66db3cb2f7a9e387990c0524b4 Mon Sep 17 00:00:00 2001 From: Yacine Date: Thu, 30 Dec 2021 10:05:52 -0500 Subject: [PATCH 02/40] Update decorators.py --- sentry_sdk/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/decorators.py b/sentry_sdk/decorators.py index 7f92fcf50d..bde718c2ad 100644 --- a/sentry_sdk/decorators.py +++ b/sentry_sdk/decorators.py @@ -37,7 +37,7 @@ def my_function(): async def my_async_function(): ... - @sentry_trace(op="child_name") + @sentry_traced(op="child_name") def my_function(): ... """ From c0bc0d828cea92e15c4f6d3e2eea8f4160b4d597 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 3 Mar 2023 14:52:24 +0100 Subject: [PATCH 03/40] Linting --- sentry_sdk/decorators.py | 89 ------------------------------ sentry_sdk/tracing.py | 113 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 105 insertions(+), 97 deletions(-) delete mode 100644 sentry_sdk/decorators.py diff --git a/sentry_sdk/decorators.py b/sentry_sdk/decorators.py deleted file mode 100644 index bde718c2ad..0000000000 --- a/sentry_sdk/decorators.py +++ /dev/null @@ -1,89 +0,0 @@ -import logging -from typing import Optional -from functools import wraps -import inspect - -import sentry_sdk - -logger = logging.getLogger(__name__) - - -def sentry_traced( - func=None, - *, - transaction_name: Optional[str] = None, - op: Optional[str] = None, -): - """ - Function decorator to start a child under the existing current transaction or - under a new transaction, if it doesn't exist. - - Args: - transaction_name: the name of the new transaction if no transaction already - exists. If a transaction is found in current scope, this name is ignored. - If no transaction is found and no transaction name is provided, no action - is taken. - op: the name of the child. Defaults to the decorated function name. - - Returns: - a decorated function executing within a Sentry transaction child - - Usage: - @sentry_traced - def my_function(): - ... - - @sentry_traced(transaction_name="new_tx") - async def my_async_function(): - ... - - @sentry_traced(op="child_name") - def my_function(): - ... - """ - - def start_child_decorator(func): - def _transaction_and_op(): - # Set transaction - transaction = sentry_sdk.Hub.current.scope.transaction - # If no current transaction we create one - if transaction_name and transaction is None: - transaction = sentry_sdk.start_transaction(name=transaction_name) - # Child name - defaults to the decorated function name - op_ = op or func.__name__ - return transaction, op_ - - # Asynchronous case - if inspect.iscoroutinefunction(func): - - @wraps(func) - async def func_with_tracing(*args, **kwargs): - transaction, op_ = _transaction_and_op() - # If no transaction, do nothing - if transaction is None: - return await func(*args, **kwargs) - # If we have a transaction, we decorate the function! - with transaction.start_child(op=op_): - return await func(*args, **kwargs) - - # Synchronous case - else: - - @wraps(func) - def func_with_tracing(*args, **kwargs): - transaction, op_ = _transaction_and_op() - # If no transaction, do nothing - if transaction is None: - return func(*args, **kwargs) - # If we have a transaction, we decorate the function! - with transaction.start_child(op=op_): - return func(*args, **kwargs) - - return func_with_tracing - - # This patterns allows usage of both @sentry_traced and @sentry_traced(...) - # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 - if func: - return start_child_decorator(func) - else: - return start_child_decorator diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index efcfc165db..3cb01f2828 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -1,3 +1,5 @@ +from functools import wraps +import inspect import uuid import random @@ -12,15 +14,16 @@ if TYPE_CHECKING: import typing - from typing import Optional from typing import Any from typing import Dict + from typing import Iterator from typing import List + from typing import Optional from typing import Tuple - from typing import Iterator import sentry_sdk.profiler - from sentry_sdk._types import Event, SamplingContext, MeasurementUnit + from sentry_sdk._types import Event, MeasurementUnit, SamplingContext + BAGGAGE_HEADER_NAME = "baggage" SENTRY_TRACE_HEADER_NAME = "sentry-trace" @@ -226,7 +229,7 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): trace_id=self.trace_id, parent_span_id=self.span_id, containing_transaction=self.containing_transaction, - **kwargs + **kwargs, ) span_recorder = ( @@ -246,7 +249,7 @@ def new_span(self, **kwargs): def continue_from_environ( cls, environ, # type: typing.Mapping[str, str] - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> Transaction """ @@ -269,7 +272,7 @@ def continue_from_environ( def continue_from_headers( cls, headers, # type: typing.Mapping[str, str] - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> Transaction """ @@ -323,7 +326,7 @@ def iter_headers(self): def from_traceparent( cls, traceparent, # type: Optional[str] - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> Optional[Transaction] """ @@ -492,7 +495,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs # type: Any + **kwargs, # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release. @@ -803,6 +806,100 @@ def finish(self, hub=None, end_timestamp=None): pass +def trace( + func=None, + *, + transaction_name=None, + op=None, +): + # type: (Any, Optional[str], Optional[str]) -> Any + """ + Decorator to start a child span under the existing current transaction or + under a new transaction, if it doesn't exist. + + Args: + transaction_name: the name of the new transaction if no transaction already + exists. If a transaction is found in current scope, this name is ignored. + If no transaction is found and no transaction name is provided, no action + is taken. + op: the name of the child. Defaults to the decorated function name. + + Returns: + a decorated function executing within a Sentry transaction child + + Usage: + from sentry_sdk.tracing import trace + + @trace + def my_function(): + ... + + @trace(transaction_name="new_tx") + async def my_async_function(): + ... + + @trace(op="child_name") + def my_function(): + ... + """ + + def start_child_decorator(func): + # type: (Any) -> Any + + def _transaction_and_op(): + # type: () -> Tuple[Optional[Transaction], str] + + # Set transaction + transaction = sentry_sdk.Hub.current.scope.transaction + # If no current transaction we create one + if transaction_name and transaction is None: + transaction = sentry_sdk.start_transaction(name=transaction_name) + # Child name - defaults to the decorated function name + op_ = op or func.__name__ + return transaction, op_ + + # Asynchronous case + if inspect.iscoroutinefunction(func): + + @wraps(func) + async def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction, op_ = _transaction_and_op() + # If no transaction, do nothing + if transaction is None: + return await func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child(op=op_): + return await func(*args, **kwargs) + + # Synchronous case + else: + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction, op_ = _transaction_and_op() + # If no transaction, do nothing + if transaction is None: + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child(op=op_): + return func(*args, **kwargs) + + return func_with_tracing + + # This patterns allows usage of both @sentry_traced and @sentry_traced(...) + # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 + if func: + return start_child_decorator(func) + else: + return start_child_decorator + + # Circular imports from sentry_sdk.tracing_utils import ( From 9d1fb9ab7c29137891ab33cb0a9847d6d0d8ebd1 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 3 Mar 2023 15:02:23 +0100 Subject: [PATCH 04/40] Fixed syntax errors --- sentry_sdk/tracing.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 3cb01f2828..b78eadcd43 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -229,7 +229,7 @@ def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): trace_id=self.trace_id, parent_span_id=self.span_id, containing_transaction=self.containing_transaction, - **kwargs, + **kwargs ) span_recorder = ( @@ -249,7 +249,7 @@ def new_span(self, **kwargs): def continue_from_environ( cls, environ, # type: typing.Mapping[str, str] - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> Transaction """ @@ -272,7 +272,7 @@ def continue_from_environ( def continue_from_headers( cls, headers, # type: typing.Mapping[str, str] - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> Transaction """ @@ -326,7 +326,7 @@ def iter_headers(self): def from_traceparent( cls, traceparent, # type: Optional[str] - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> Optional[Transaction] """ @@ -495,7 +495,7 @@ def __init__( parent_sampled=None, # type: Optional[bool] baggage=None, # type: Optional[Baggage] source=TRANSACTION_SOURCE_CUSTOM, # type: str - **kwargs, # type: Any + **kwargs # type: Any ): # type: (...) -> None # TODO: consider removing this in a future release. From b795e19732a4ebefd39c20857cfd9ec76d155339 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 3 Mar 2023 15:16:52 +0100 Subject: [PATCH 05/40] Fixed syntax error --- sentry_sdk/tracing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index b78eadcd43..a991c355f8 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -808,7 +808,6 @@ def finish(self, hub=None, end_timestamp=None): def trace( func=None, - *, transaction_name=None, op=None, ): From e839e4c116a73d91335a1b4e73acaa56912415e3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 3 Mar 2023 15:25:56 +0100 Subject: [PATCH 06/40] Try to make it work in Python 2.x --- sentry_sdk/tracing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index a991c355f8..f2abca5d7d 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -8,6 +8,7 @@ import sentry_sdk from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.utils import logger, nanosecond_time +from sentry_sdk._compat import PY2 from sentry_sdk._types import TYPE_CHECKING @@ -858,8 +859,7 @@ def _transaction_and_op(): return transaction, op_ # Asynchronous case - if inspect.iscoroutinefunction(func): - + if inspect.iscoroutinefunction(func) and not PY2: @wraps(func) async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any From 99d548523caf9277c31c9c7dba4609799b651597 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 10:50:03 +0100 Subject: [PATCH 07/40] Removed transaction generation and made OP set to . --- sentry_sdk/__init__.py | 2 ++ sentry_sdk/tracing.py | 68 ++++++++++++++---------------------------- 2 files changed, 25 insertions(+), 45 deletions(-) diff --git a/sentry_sdk/__init__.py b/sentry_sdk/__init__.py index 4d40efacce..5975115de9 100644 --- a/sentry_sdk/__init__.py +++ b/sentry_sdk/__init__.py @@ -7,6 +7,8 @@ from sentry_sdk.consts import VERSION # noqa +from sentry_sdk.tracing import trace # noqa + __all__ = [ # noqa "Hub", "Scope", diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index f2abca5d7d..c0e790bad8 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -6,7 +6,7 @@ from datetime import datetime, timedelta import sentry_sdk -from sentry_sdk.consts import INSTRUMENTER +from sentry_sdk.consts import INSTRUMENTER, OP from sentry_sdk.utils import logger, nanosecond_time from sentry_sdk._compat import PY2 from sentry_sdk._types import TYPE_CHECKING @@ -807,70 +807,47 @@ def finish(self, hub=None, end_timestamp=None): pass -def trace( - func=None, - transaction_name=None, - op=None, -): - # type: (Any, Optional[str], Optional[str]) -> Any +def trace(func=None): + # type: (Any) -> Any """ - Decorator to start a child span under the existing current transaction or - under a new transaction, if it doesn't exist. - - Args: - transaction_name: the name of the new transaction if no transaction already - exists. If a transaction is found in current scope, this name is ignored. - If no transaction is found and no transaction name is provided, no action - is taken. - op: the name of the child. Defaults to the decorated function name. - - Returns: - a decorated function executing within a Sentry transaction child + Decorator to start a child span under the existing current transaction. + If there is no current transaction, than nothing will be traced. Usage: - from sentry_sdk.tracing import trace + import sentry_sdk - @trace - def my_function(): - ... + @sentry_sdk.trace + def my_function(): + ... - @trace(transaction_name="new_tx") - async def my_async_function(): - ... - - @trace(op="child_name") - def my_function(): - ... + @senrty_sdk.trace + async def my_async_function(): + ... """ def start_child_decorator(func): # type: (Any) -> Any - def _transaction_and_op(): - # type: () -> Tuple[Optional[Transaction], str] - - # Set transaction + def _get_transaction(): + # type: () -> Optional[Transaction] transaction = sentry_sdk.Hub.current.scope.transaction - # If no current transaction we create one - if transaction_name and transaction is None: - transaction = sentry_sdk.start_transaction(name=transaction_name) - # Child name - defaults to the decorated function name - op_ = op or func.__name__ - return transaction, op_ + return transaction # Asynchronous case if inspect.iscoroutinefunction(func) and not PY2: + @wraps(func) async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction, op_ = _transaction_and_op() + transaction = _get_transaction() + # If no transaction, do nothing if transaction is None: return await func(*args, **kwargs) - # If we have a transaction, we decorate the function! - with transaction.start_child(op=op_): + # If we have a transaction, we wrap the function. + with transaction.start_child(op=OP.FUNCTION): return await func(*args, **kwargs) # Synchronous case @@ -880,13 +857,14 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction, op_ = _transaction_and_op() + transaction = _get_transaction() + # If no transaction, do nothing if transaction is None: return func(*args, **kwargs) # If we have a transaction, we decorate the function! - with transaction.start_child(op=op_): + with transaction.start_child(op=OP.FUNCTION): return func(*args, **kwargs) return func_with_tracing From 1a6958ff0d108c3ecdd336271826e6dd25d50201 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 11:07:19 +0100 Subject: [PATCH 08/40] Added function name as descripion to span. --- sentry_sdk/tracing.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index c0e790bad8..63bd5825a3 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -7,7 +7,7 @@ import sentry_sdk from sentry_sdk.consts import INSTRUMENTER, OP -from sentry_sdk.utils import logger, nanosecond_time +from sentry_sdk.utils import logger, nanosecond_time, qualname_from_function from sentry_sdk._compat import PY2 from sentry_sdk._types import TYPE_CHECKING @@ -847,7 +847,10 @@ async def func_with_tracing(*args, **kwargs): return await func(*args, **kwargs) # If we have a transaction, we wrap the function. - with transaction.start_child(op=OP.FUNCTION): + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): return await func(*args, **kwargs) # Synchronous case @@ -864,7 +867,10 @@ def func_with_tracing(*args, **kwargs): return func(*args, **kwargs) # If we have a transaction, we decorate the function! - with transaction.start_child(op=OP.FUNCTION): + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): return func(*args, **kwargs) return func_with_tracing From f8138b85b2204b471699346131f4eac420904ee8 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 14:18:49 +0100 Subject: [PATCH 09/40] Try to make it run in Python 2 --- sentry_sdk/tracing.py | 64 +++-------------------- sentry_sdk/tracing_utils.py | 80 ++++++++++++++++++++++++----- sentry_sdk/tracing_utils_python2.py | 46 +++++++++++++++++ 3 files changed, 121 insertions(+), 69 deletions(-) create mode 100644 sentry_sdk/tracing_utils_python2.py diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 63bd5825a3..6854c80c47 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -1,13 +1,11 @@ -from functools import wraps -import inspect import uuid import random from datetime import datetime, timedelta import sentry_sdk -from sentry_sdk.consts import INSTRUMENTER, OP -from sentry_sdk.utils import logger, nanosecond_time, qualname_from_function +from sentry_sdk.consts import INSTRUMENTER +from sentry_sdk.utils import logger, nanosecond_time from sentry_sdk._compat import PY2 from sentry_sdk._types import TYPE_CHECKING @@ -824,63 +822,17 @@ def my_function(): async def my_async_function(): ... """ - - def start_child_decorator(func): - # type: (Any) -> Any - - def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - - # Asynchronous case - if inspect.iscoroutinefunction(func) and not PY2: - - @wraps(func) - async def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - return await func(*args, **kwargs) - - # If we have a transaction, we wrap the function. - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return await func(*args, **kwargs) - - # Synchronous case - else: - - @wraps(func) - def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - return func(*args, **kwargs) - - # If we have a transaction, we decorate the function! - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return func(*args, **kwargs) - - return func_with_tracing + if PY2: + from sentry_sdk.tracing_utils_python2 import start_child_span_decorator + else: + from sentry_sdk.tracing_utils import start_child_span_decorator # This patterns allows usage of both @sentry_traced and @sentry_traced(...) # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 if func: - return start_child_decorator(func) + return start_child_span_decorator(func) else: - return start_child_decorator + return start_child_span_decorator # Circular imports diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 64155defdf..83edb67d9a 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1,21 +1,22 @@ -import re import contextlib +import inspect import math - -from numbers import Real +import re from decimal import Decimal +from functools import wraps +from numbers import Real import sentry_sdk +from sentry_sdk._compat import PY2, iteritems +from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP - from sentry_sdk.utils import ( - capture_internal_exceptions, Dsn, + capture_internal_exceptions, logger, + qualname_from_function, to_string, ) -from sentry_sdk._compat import PY2, iteritems -from sentry_sdk._types import TYPE_CHECKING if PY2: from collections import Mapping @@ -26,12 +27,7 @@ if TYPE_CHECKING: import typing - - from typing import Any - from typing import Dict - from typing import Generator - from typing import Optional - from typing import Union + from typing import Any, Dict, Generator, Optional, Union SENTRY_TRACE_REGEX = re.compile( @@ -395,6 +391,64 @@ def should_propagate_trace(hub, url): return False +def start_child_span_decorator(func): + # type: (Any) -> Any + """ + Decorator to add child spans for functions. + + For Python 2 there is duplicated code here: ``sentry_sdk.tracing_utils.start_child_span_decorator()``. + + See also ``sentry_sdk.tracing.trace()``. + """ + + def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + # Asynchronous case + if inspect.iscoroutinefunction(func): + + @wraps(func) + async def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + return await func(*args, **kwargs) + + # If we have a transaction, we wrap the function. + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return await func(*args, **kwargs) + + # Synchronous case + else: + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return func(*args, **kwargs) + + return func_with_tracing + + # Circular imports from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES diff --git a/sentry_sdk/tracing_utils_python2.py b/sentry_sdk/tracing_utils_python2.py new file mode 100644 index 0000000000..28cc8fb06d --- /dev/null +++ b/sentry_sdk/tracing_utils_python2.py @@ -0,0 +1,46 @@ +from functools import wraps + +import sentry_sdk +from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.consts import OP +from sentry_sdk.tracing import Transaction +from sentry_sdk.utils import qualname_from_function + +if TYPE_CHECKING: + from typing import Any, Optional + + +def start_child_span_decorator(func): + # type: (Any) -> Any + """ + Decorator to add child spans for functions. + + This is the Python 2 compatible version of the decorator. + Duplicated code from ``sentry_sdk.tracing_utils.start_child_span_decorator``. + + See also ``sentry_sdk.tracing.trace()``. + """ + + def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return func(*args, **kwargs) + + return func_with_tracing From 05a7caa68e4f72e3d0e4a93f60cbc307fadd7e50 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 14:39:20 +0100 Subject: [PATCH 10/40] Trying something else. --- sentry_sdk/tracing.py | 2 +- sentry_sdk/tracing_utils.py | 61 ------------------------ sentry_sdk/tracing_utils_python2.py | 6 ++- sentry_sdk/tracing_utils_python3.py | 72 +++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 64 deletions(-) create mode 100644 sentry_sdk/tracing_utils_python3.py diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 6854c80c47..30c6bc608a 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -825,7 +825,7 @@ async def my_async_function(): if PY2: from sentry_sdk.tracing_utils_python2 import start_child_span_decorator else: - from sentry_sdk.tracing_utils import start_child_span_decorator + from sentry_sdk.tracing_utils_python3 import start_child_span_decorator # This patterns allows usage of both @sentry_traced and @sentry_traced(...) # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 83edb67d9a..bba43e42a9 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1,9 +1,7 @@ import contextlib -import inspect import math import re from decimal import Decimal -from functools import wraps from numbers import Real import sentry_sdk @@ -14,7 +12,6 @@ Dsn, capture_internal_exceptions, logger, - qualname_from_function, to_string, ) @@ -391,64 +388,6 @@ def should_propagate_trace(hub, url): return False -def start_child_span_decorator(func): - # type: (Any) -> Any - """ - Decorator to add child spans for functions. - - For Python 2 there is duplicated code here: ``sentry_sdk.tracing_utils.start_child_span_decorator()``. - - See also ``sentry_sdk.tracing.trace()``. - """ - - def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - - # Asynchronous case - if inspect.iscoroutinefunction(func): - - @wraps(func) - async def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - return await func(*args, **kwargs) - - # If we have a transaction, we wrap the function. - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return await func(*args, **kwargs) - - # Synchronous case - else: - - @wraps(func) - def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - return func(*args, **kwargs) - - # If we have a transaction, we decorate the function! - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return func(*args, **kwargs) - - return func_with_tracing - - # Circular imports from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES diff --git a/sentry_sdk/tracing_utils_python2.py b/sentry_sdk/tracing_utils_python2.py index 28cc8fb06d..726acee3a7 100644 --- a/sentry_sdk/tracing_utils_python2.py +++ b/sentry_sdk/tracing_utils_python2.py @@ -3,12 +3,14 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP -from sentry_sdk.tracing import Transaction from sentry_sdk.utils import qualname_from_function + if TYPE_CHECKING: from typing import Any, Optional + from sentry_sdk.tracing import Transaction + def start_child_span_decorator(func): # type: (Any) -> Any @@ -16,7 +18,7 @@ def start_child_span_decorator(func): Decorator to add child spans for functions. This is the Python 2 compatible version of the decorator. - Duplicated code from ``sentry_sdk.tracing_utils.start_child_span_decorator``. + Duplicated code from ``sentry_sdk.tracing_utils_python3.start_child_span_decorator``. See also ``sentry_sdk.tracing.trace()``. """ diff --git a/sentry_sdk/tracing_utils_python3.py b/sentry_sdk/tracing_utils_python3.py new file mode 100644 index 0000000000..ff2ed05338 --- /dev/null +++ b/sentry_sdk/tracing_utils_python3.py @@ -0,0 +1,72 @@ +import inspect +from functools import wraps + +import sentry_sdk +from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.consts import OP +from sentry_sdk.utils import qualname_from_function + + +if TYPE_CHECKING: + from typing import Any, Optional + + from sentry_sdk.tracing import Transaction + + +def start_child_span_decorator(func): + # type: (Any) -> Any + """ + Decorator to add child spans for functions. + + This is the Python 3 compatible version of the decorator. + For Python 2 there is duplicated code here: ``sentry_sdk.tracing_utils_python2.start_child_span_decorator()``. + + See also ``sentry_sdk.tracing.trace()``. + """ + + def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + # Asynchronous case + if inspect.iscoroutinefunction(func): + + @wraps(func) + async def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + return await func(*args, **kwargs) + + # If we have a transaction, we wrap the function. + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return await func(*args, **kwargs) + + # Synchronous case + else: + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return func(*args, **kwargs) + + return func_with_tracing From 0160be0237d76471f934ba6fa59fe925e17dc078 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 15:03:02 +0100 Subject: [PATCH 11/40] Reverted changes to tracing_utils --- sentry_sdk/tracing_utils.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index bba43e42a9..64155defdf 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1,19 +1,21 @@ +import re import contextlib import math -import re -from decimal import Decimal + from numbers import Real +from decimal import Decimal import sentry_sdk -from sentry_sdk._compat import PY2, iteritems -from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP + from sentry_sdk.utils import ( - Dsn, capture_internal_exceptions, + Dsn, logger, to_string, ) +from sentry_sdk._compat import PY2, iteritems +from sentry_sdk._types import TYPE_CHECKING if PY2: from collections import Mapping @@ -24,7 +26,12 @@ if TYPE_CHECKING: import typing - from typing import Any, Dict, Generator, Optional, Union + + from typing import Any + from typing import Dict + from typing import Generator + from typing import Optional + from typing import Union SENTRY_TRACE_REGEX = re.compile( From 1589a564b353a0625affca14980176081f64ef2c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 6 Mar 2023 15:27:33 +0100 Subject: [PATCH 12/40] Added warning message in case there is no transaction --- sentry_sdk/tracing_utils_python2.py | 7 ++++++- sentry_sdk/tracing_utils_python3.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing_utils_python2.py b/sentry_sdk/tracing_utils_python2.py index 726acee3a7..1cc4bca962 100644 --- a/sentry_sdk/tracing_utils_python2.py +++ b/sentry_sdk/tracing_utils_python2.py @@ -3,7 +3,7 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP -from sentry_sdk.utils import qualname_from_function +from sentry_sdk.utils import logger, qualname_from_function if TYPE_CHECKING: @@ -36,6 +36,11 @@ def func_with_tracing(*args, **kwargs): # If no transaction, do nothing if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) return func(*args, **kwargs) # If we have a transaction, we decorate the function! diff --git a/sentry_sdk/tracing_utils_python3.py b/sentry_sdk/tracing_utils_python3.py index ff2ed05338..8a57c7596d 100644 --- a/sentry_sdk/tracing_utils_python3.py +++ b/sentry_sdk/tracing_utils_python3.py @@ -4,7 +4,7 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP -from sentry_sdk.utils import qualname_from_function +from sentry_sdk.utils import logger, qualname_from_function if TYPE_CHECKING: @@ -40,6 +40,11 @@ async def func_with_tracing(*args, **kwargs): # If no transaction, do nothing if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) return await func(*args, **kwargs) # If we have a transaction, we wrap the function. @@ -60,6 +65,11 @@ def func_with_tracing(*args, **kwargs): # If no transaction, do nothing if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) return func(*args, **kwargs) # If we have a transaction, we decorate the function! From 639e190b65c25f73339bbd46dbe04f9f1e654fb5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 7 Mar 2023 13:09:56 +0100 Subject: [PATCH 13/40] Update sentry_sdk/tracing.py Co-authored-by: Daniel Griesser --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 30c6bc608a..e2e1cf5462 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -818,7 +818,7 @@ def trace(func=None): def my_function(): ... - @senrty_sdk.trace + @sentry_sdk.trace async def my_async_function(): ... """ From 0b26cf1a48f76920bcbf9ba9f773fe9182a63c82 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 11:30:06 +0100 Subject: [PATCH 14/40] Added tests --- sentry_sdk/tracing_utils_python2.py | 11 +-- sentry_sdk/tracing_utils_python3.py | 11 +-- tests/tracing/test_decorator.py | 103 ++++++++++++++++++++++++ tests/tracing/test_decorator_python2.py | 50 ++++++++++++ 4 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 tests/tracing/test_decorator.py create mode 100644 tests/tracing/test_decorator_python2.py diff --git a/sentry_sdk/tracing_utils_python2.py b/sentry_sdk/tracing_utils_python2.py index 1cc4bca962..9b5bdc372a 100644 --- a/sentry_sdk/tracing_utils_python2.py +++ b/sentry_sdk/tracing_utils_python2.py @@ -12,6 +12,12 @@ from sentry_sdk.tracing import Transaction +def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + def start_child_span_decorator(func): # type: (Any) -> Any """ @@ -23,11 +29,6 @@ def start_child_span_decorator(func): See also ``sentry_sdk.tracing.trace()``. """ - def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - @wraps(func) def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any diff --git a/sentry_sdk/tracing_utils_python3.py b/sentry_sdk/tracing_utils_python3.py index 8a57c7596d..4d62007412 100644 --- a/sentry_sdk/tracing_utils_python3.py +++ b/sentry_sdk/tracing_utils_python3.py @@ -13,6 +13,12 @@ from sentry_sdk.tracing import Transaction +def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + def start_child_span_decorator(func): # type: (Any) -> Any """ @@ -24,11 +30,6 @@ def start_child_span_decorator(func): See also ``sentry_sdk.tracing.trace()``. """ - def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - # Asynchronous case if inspect.iscoroutinefunction(func): diff --git a/tests/tracing/test_decorator.py b/tests/tracing/test_decorator.py new file mode 100644 index 0000000000..b60da95175 --- /dev/null +++ b/tests/tracing/test_decorator.py @@ -0,0 +1,103 @@ +import mock +import pytest +import sys + +from sentry_sdk.tracing_utils_python3 import ( + start_child_span_decorator as start_child_span_decorator_python3, +) +from sentry_sdk.utils import logger + +if sys.version_info < (3, 6): + pytest.skip("Async decorator only works on Python 3.6+", allow_module_level=True) + + +def my_example_function(): + return "return_of_sync_function" + + +async def my_async_example_function(): + return "return_of_async_function" + + +def test_trace_decorator_sync_py3(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_python3._get_transaction", + return_value=fake_transaction, + ): + result = my_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_python3(my_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator.my_example_function" + ) + assert result2 == "return_of_sync_function" + + +def test_trace_decorator_sync_py3_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_python3._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = my_example_function() + fake_warning.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_python3(my_example_function)() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator.my_example_function", + ) + assert result2 == "return_of_sync_function" + + +@pytest.mark.asyncio +async def test_trace_decorator_async_py3(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_python3._get_transaction", + return_value=fake_transaction, + ): + result = await my_async_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_async_function" + + result2 = await start_child_span_decorator_python3(my_async_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator.my_async_example_function" + ) + assert result2 == "return_of_async_function" + + +@pytest.mark.asyncio +async def test_trace_decorator_async_py3_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_python3._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = await my_async_example_function() + fake_warning.assert_not_called() + assert result == "return_of_async_function" + + result2 = await start_child_span_decorator_python3( + my_async_example_function + )() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator.my_async_example_function", + ) + assert result2 == "return_of_async_function" diff --git a/tests/tracing/test_decorator_python2.py b/tests/tracing/test_decorator_python2.py new file mode 100644 index 0000000000..d63efa410d --- /dev/null +++ b/tests/tracing/test_decorator_python2.py @@ -0,0 +1,50 @@ +import mock + +from sentry_sdk.tracing_utils_python2 import ( + start_child_span_decorator as start_child_span_decorator_python2, +) +from sentry_sdk.utils import logger + + +def my_example_function(): + return "return_of_sync_function" + + +def test_trace_decorator_py2(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_python2._get_transaction", + return_value=fake_transaction, + ): + result = my_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_python2(my_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator_python2.my_example_function" + ) + assert result2 == "return_of_sync_function" + + +def test_trace_decorator_py2_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_python2._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = my_example_function() + fake_warning.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_python2(my_example_function)() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator_python2.my_example_function", + ) + assert result2 == "return_of_sync_function" From 6635d621406cc6cc89fe5fbfbfbfdc190aafeb9a Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:10:19 +0100 Subject: [PATCH 15/40] Updated how to run tests --- sentry_sdk/tracing_utils_python2.py | 54 ------------- sentry_sdk/tracing_utils_python3.py | 83 ------------------- tests/tracing/test_decorator.py | 103 ------------------------ tests/tracing/test_decorator_python2.py | 50 ------------ tox.ini | 3 +- 5 files changed, 2 insertions(+), 291 deletions(-) delete mode 100644 sentry_sdk/tracing_utils_python2.py delete mode 100644 sentry_sdk/tracing_utils_python3.py delete mode 100644 tests/tracing/test_decorator.py delete mode 100644 tests/tracing/test_decorator_python2.py diff --git a/sentry_sdk/tracing_utils_python2.py b/sentry_sdk/tracing_utils_python2.py deleted file mode 100644 index 9b5bdc372a..0000000000 --- a/sentry_sdk/tracing_utils_python2.py +++ /dev/null @@ -1,54 +0,0 @@ -from functools import wraps - -import sentry_sdk -from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.consts import OP -from sentry_sdk.utils import logger, qualname_from_function - - -if TYPE_CHECKING: - from typing import Any, Optional - - from sentry_sdk.tracing import Transaction - - -def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - - -def start_child_span_decorator(func): - # type: (Any) -> Any - """ - Decorator to add child spans for functions. - - This is the Python 2 compatible version of the decorator. - Duplicated code from ``sentry_sdk.tracing_utils_python3.start_child_span_decorator``. - - See also ``sentry_sdk.tracing.trace()``. - """ - - @wraps(func) - def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - logger.warning( - "No transaction found. Not creating a child span for %s. " - "Please start a Sentry transaction before calling this function.", - qualname_from_function(func), - ) - return func(*args, **kwargs) - - # If we have a transaction, we decorate the function! - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return func(*args, **kwargs) - - return func_with_tracing diff --git a/sentry_sdk/tracing_utils_python3.py b/sentry_sdk/tracing_utils_python3.py deleted file mode 100644 index 4d62007412..0000000000 --- a/sentry_sdk/tracing_utils_python3.py +++ /dev/null @@ -1,83 +0,0 @@ -import inspect -from functools import wraps - -import sentry_sdk -from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.consts import OP -from sentry_sdk.utils import logger, qualname_from_function - - -if TYPE_CHECKING: - from typing import Any, Optional - - from sentry_sdk.tracing import Transaction - - -def _get_transaction(): - # type: () -> Optional[Transaction] - transaction = sentry_sdk.Hub.current.scope.transaction - return transaction - - -def start_child_span_decorator(func): - # type: (Any) -> Any - """ - Decorator to add child spans for functions. - - This is the Python 3 compatible version of the decorator. - For Python 2 there is duplicated code here: ``sentry_sdk.tracing_utils_python2.start_child_span_decorator()``. - - See also ``sentry_sdk.tracing.trace()``. - """ - - # Asynchronous case - if inspect.iscoroutinefunction(func): - - @wraps(func) - async def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - logger.warning( - "No transaction found. Not creating a child span for %s. " - "Please start a Sentry transaction before calling this function.", - qualname_from_function(func), - ) - return await func(*args, **kwargs) - - # If we have a transaction, we wrap the function. - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return await func(*args, **kwargs) - - # Synchronous case - else: - - @wraps(func) - def func_with_tracing(*args, **kwargs): - # type: (*Any, **Any) -> Any - - transaction = _get_transaction() - - # If no transaction, do nothing - if transaction is None: - logger.warning( - "No transaction found. Not creating a child span for %s. " - "Please start a Sentry transaction before calling this function.", - qualname_from_function(func), - ) - return func(*args, **kwargs) - - # If we have a transaction, we decorate the function! - with transaction.start_child( - op=OP.FUNCTION, - description=qualname_from_function(func), - ): - return func(*args, **kwargs) - - return func_with_tracing diff --git a/tests/tracing/test_decorator.py b/tests/tracing/test_decorator.py deleted file mode 100644 index b60da95175..0000000000 --- a/tests/tracing/test_decorator.py +++ /dev/null @@ -1,103 +0,0 @@ -import mock -import pytest -import sys - -from sentry_sdk.tracing_utils_python3 import ( - start_child_span_decorator as start_child_span_decorator_python3, -) -from sentry_sdk.utils import logger - -if sys.version_info < (3, 6): - pytest.skip("Async decorator only works on Python 3.6+", allow_module_level=True) - - -def my_example_function(): - return "return_of_sync_function" - - -async def my_async_example_function(): - return "return_of_async_function" - - -def test_trace_decorator_sync_py3(): - fake_start_child = mock.MagicMock() - fake_transaction = mock.MagicMock() - fake_transaction.start_child = fake_start_child - - with mock.patch( - "sentry_sdk.tracing_utils_python3._get_transaction", - return_value=fake_transaction, - ): - result = my_example_function() - fake_start_child.assert_not_called() - assert result == "return_of_sync_function" - - result2 = start_child_span_decorator_python3(my_example_function)() - fake_start_child.assert_called_once_with( - op="function", description="test_decorator.my_example_function" - ) - assert result2 == "return_of_sync_function" - - -def test_trace_decorator_sync_py3_no_trx(): - fake_transaction = None - - with mock.patch( - "sentry_sdk.tracing_utils_python3._get_transaction", - return_value=fake_transaction, - ): - with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: - result = my_example_function() - fake_warning.assert_not_called() - assert result == "return_of_sync_function" - - result2 = start_child_span_decorator_python3(my_example_function)() - fake_warning.assert_called_once_with( - "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", - "test_decorator.my_example_function", - ) - assert result2 == "return_of_sync_function" - - -@pytest.mark.asyncio -async def test_trace_decorator_async_py3(): - fake_start_child = mock.MagicMock() - fake_transaction = mock.MagicMock() - fake_transaction.start_child = fake_start_child - - with mock.patch( - "sentry_sdk.tracing_utils_python3._get_transaction", - return_value=fake_transaction, - ): - result = await my_async_example_function() - fake_start_child.assert_not_called() - assert result == "return_of_async_function" - - result2 = await start_child_span_decorator_python3(my_async_example_function)() - fake_start_child.assert_called_once_with( - op="function", description="test_decorator.my_async_example_function" - ) - assert result2 == "return_of_async_function" - - -@pytest.mark.asyncio -async def test_trace_decorator_async_py3_no_trx(): - fake_transaction = None - - with mock.patch( - "sentry_sdk.tracing_utils_python3._get_transaction", - return_value=fake_transaction, - ): - with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: - result = await my_async_example_function() - fake_warning.assert_not_called() - assert result == "return_of_async_function" - - result2 = await start_child_span_decorator_python3( - my_async_example_function - )() - fake_warning.assert_called_once_with( - "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", - "test_decorator.my_async_example_function", - ) - assert result2 == "return_of_async_function" diff --git a/tests/tracing/test_decorator_python2.py b/tests/tracing/test_decorator_python2.py deleted file mode 100644 index d63efa410d..0000000000 --- a/tests/tracing/test_decorator_python2.py +++ /dev/null @@ -1,50 +0,0 @@ -import mock - -from sentry_sdk.tracing_utils_python2 import ( - start_child_span_decorator as start_child_span_decorator_python2, -) -from sentry_sdk.utils import logger - - -def my_example_function(): - return "return_of_sync_function" - - -def test_trace_decorator_py2(): - fake_start_child = mock.MagicMock() - fake_transaction = mock.MagicMock() - fake_transaction.start_child = fake_start_child - - with mock.patch( - "sentry_sdk.tracing_utils_python2._get_transaction", - return_value=fake_transaction, - ): - result = my_example_function() - fake_start_child.assert_not_called() - assert result == "return_of_sync_function" - - result2 = start_child_span_decorator_python2(my_example_function)() - fake_start_child.assert_called_once_with( - op="function", description="test_decorator_python2.my_example_function" - ) - assert result2 == "return_of_sync_function" - - -def test_trace_decorator_py2_no_trx(): - fake_transaction = None - - with mock.patch( - "sentry_sdk.tracing_utils_python2._get_transaction", - return_value=fake_transaction, - ): - with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: - result = my_example_function() - fake_warning.assert_not_called() - assert result == "return_of_sync_function" - - result2 = start_child_span_decorator_python2(my_example_function)() - fake_warning.assert_called_once_with( - "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", - "test_decorator_python2.my_example_function", - ) - assert result2 == "return_of_sync_function" diff --git a/tox.ini b/tox.ini index 45facf42c0..2a9a275ff9 100644 --- a/tox.ini +++ b/tox.ini @@ -494,7 +494,8 @@ commands = ; Running `py.test` as an executable suffers from an import error ; when loading tests in scenarios. In particular, django fails to ; load the settings from the test module. - python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} + {py2} python -m pytest --ignore-glob='*py3.py' --durations=5 -vvv {env:TESTPATH} {posargs} + {py3} python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} [testenv:linters] commands = From 10fe566dc1d59225a9ed54b9fe117fd009bfaa25 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:14:05 +0100 Subject: [PATCH 16/40] Fixed invocation of tests --- sentry_sdk/tracing_utils_py2.py | 54 +++++++++++++++ sentry_sdk/tracing_utils_py3.py | 83 ++++++++++++++++++++++ tests/tracing/test_decorator_py2.py | 50 ++++++++++++++ tests/tracing/test_decorator_py3.py | 103 ++++++++++++++++++++++++++++ tox.ini | 4 +- 5 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 sentry_sdk/tracing_utils_py2.py create mode 100644 sentry_sdk/tracing_utils_py3.py create mode 100644 tests/tracing/test_decorator_py2.py create mode 100644 tests/tracing/test_decorator_py3.py diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py new file mode 100644 index 0000000000..9b5bdc372a --- /dev/null +++ b/sentry_sdk/tracing_utils_py2.py @@ -0,0 +1,54 @@ +from functools import wraps + +import sentry_sdk +from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.consts import OP +from sentry_sdk.utils import logger, qualname_from_function + + +if TYPE_CHECKING: + from typing import Any, Optional + + from sentry_sdk.tracing import Transaction + + +def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + +def start_child_span_decorator(func): + # type: (Any) -> Any + """ + Decorator to add child spans for functions. + + This is the Python 2 compatible version of the decorator. + Duplicated code from ``sentry_sdk.tracing_utils_python3.start_child_span_decorator``. + + See also ``sentry_sdk.tracing.trace()``. + """ + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return func(*args, **kwargs) + + return func_with_tracing diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py new file mode 100644 index 0000000000..4d62007412 --- /dev/null +++ b/sentry_sdk/tracing_utils_py3.py @@ -0,0 +1,83 @@ +import inspect +from functools import wraps + +import sentry_sdk +from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.consts import OP +from sentry_sdk.utils import logger, qualname_from_function + + +if TYPE_CHECKING: + from typing import Any, Optional + + from sentry_sdk.tracing import Transaction + + +def _get_transaction(): + # type: () -> Optional[Transaction] + transaction = sentry_sdk.Hub.current.scope.transaction + return transaction + + +def start_child_span_decorator(func): + # type: (Any) -> Any + """ + Decorator to add child spans for functions. + + This is the Python 3 compatible version of the decorator. + For Python 2 there is duplicated code here: ``sentry_sdk.tracing_utils_python2.start_child_span_decorator()``. + + See also ``sentry_sdk.tracing.trace()``. + """ + + # Asynchronous case + if inspect.iscoroutinefunction(func): + + @wraps(func) + async def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) + return await func(*args, **kwargs) + + # If we have a transaction, we wrap the function. + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return await func(*args, **kwargs) + + # Synchronous case + else: + + @wraps(func) + def func_with_tracing(*args, **kwargs): + # type: (*Any, **Any) -> Any + + transaction = _get_transaction() + + # If no transaction, do nothing + if transaction is None: + logger.warning( + "No transaction found. Not creating a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(func), + ) + return func(*args, **kwargs) + + # If we have a transaction, we decorate the function! + with transaction.start_child( + op=OP.FUNCTION, + description=qualname_from_function(func), + ): + return func(*args, **kwargs) + + return func_with_tracing diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py new file mode 100644 index 0000000000..21f09a56f1 --- /dev/null +++ b/tests/tracing/test_decorator_py2.py @@ -0,0 +1,50 @@ +import mock + +from sentry_sdk.tracing_utils_py2 import ( + start_child_span_decorator as start_child_span_decorator_py2, +) +from sentry_sdk.utils import logger + + +def my_example_function(): + return "return_of_sync_function" + + +def test_trace_decorator_py2(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_py2._get_transaction", + return_value=fake_transaction, + ): + result = my_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_py2(my_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator_py2.my_example_function" + ) + assert result2 == "return_of_sync_function" + + +def test_trace_decorator_py2_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_py2._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = my_example_function() + fake_warning.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_py2(my_example_function)() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator_py2.my_example_function", + ) + assert result2 == "return_of_sync_function" diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py new file mode 100644 index 0000000000..178ef26100 --- /dev/null +++ b/tests/tracing/test_decorator_py3.py @@ -0,0 +1,103 @@ +import mock +import pytest +import sys + +from sentry_sdk.tracing_utils_py3 import ( + start_child_span_decorator as start_child_span_decorator_py3, +) +from sentry_sdk.utils import logger + +if sys.version_info < (3, 6): + pytest.skip("Async decorator only works on Python 3.6+", allow_module_level=True) + + +def my_example_function(): + return "return_of_sync_function" + + +async def my_async_example_function(): + return "return_of_async_function" + + +def test_trace_decorator_sync_py3(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_py3._get_transaction", + return_value=fake_transaction, + ): + result = my_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_py3(my_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator.my_example_function" + ) + assert result2 == "return_of_sync_function" + + +def test_trace_decorator_sync_py3_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_py3._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = my_example_function() + fake_warning.assert_not_called() + assert result == "return_of_sync_function" + + result2 = start_child_span_decorator_py3(my_example_function)() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator.my_example_function", + ) + assert result2 == "return_of_sync_function" + + +@pytest.mark.asyncio +async def test_trace_decorator_async_py3(): + fake_start_child = mock.MagicMock() + fake_transaction = mock.MagicMock() + fake_transaction.start_child = fake_start_child + + with mock.patch( + "sentry_sdk.tracing_utils_py3._get_transaction", + return_value=fake_transaction, + ): + result = await my_async_example_function() + fake_start_child.assert_not_called() + assert result == "return_of_async_function" + + result2 = await start_child_span_decorator_py3(my_async_example_function)() + fake_start_child.assert_called_once_with( + op="function", description="test_decorator.my_async_example_function" + ) + assert result2 == "return_of_async_function" + + +@pytest.mark.asyncio +async def test_trace_decorator_async_py3_no_trx(): + fake_transaction = None + + with mock.patch( + "sentry_sdk.tracing_utils_py3._get_transaction", + return_value=fake_transaction, + ): + with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: + result = await my_async_example_function() + fake_warning.assert_not_called() + assert result == "return_of_async_function" + + result2 = await start_child_span_decorator_py3( + my_async_example_function + )() + fake_warning.assert_called_once_with( + "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", + "test_decorator.my_async_example_function", + ) + assert result2 == "return_of_async_function" diff --git a/tox.ini b/tox.ini index 2a9a275ff9..05e74f6465 100644 --- a/tox.ini +++ b/tox.ini @@ -494,8 +494,8 @@ commands = ; Running `py.test` as an executable suffers from an import error ; when loading tests in scenarios. In particular, django fails to ; load the settings from the test module. - {py2} python -m pytest --ignore-glob='*py3.py' --durations=5 -vvv {env:TESTPATH} {posargs} - {py3} python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} + {py2.7} python -m pytest --ignore-glob='*py3.py' --durations=5 -vvv {env:TESTPATH} {posargs} + {py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11} python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} [testenv:linters] commands = From b2930472b1ed27c337478e9c198602677c781e2c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:22:26 +0100 Subject: [PATCH 17/40] Fixed invocation (again) --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 05e74f6465..e2fefba5c2 100644 --- a/tox.ini +++ b/tox.ini @@ -494,8 +494,8 @@ commands = ; Running `py.test` as an executable suffers from an import error ; when loading tests in scenarios. In particular, django fails to ; load the settings from the test module. - {py2.7} python -m pytest --ignore-glob='*py3.py' --durations=5 -vvv {env:TESTPATH} {posargs} - {py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11} python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} + {py2.7}: python -m pytest --ignore-glob='*py3.py' --durations=5 -vvv {env:TESTPATH} {posargs} + {py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}: python -m pytest --durations=5 -vvv {env:TESTPATH} {posargs} [testenv:linters] commands = From b40578a1d81152c1d89e73665eb4dfeaad630c46 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:31:16 +0100 Subject: [PATCH 18/40] Fixed tests --- tests/tracing/test_decorator_py3.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 178ef26100..57e4aa74db 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -34,7 +34,7 @@ def test_trace_decorator_sync_py3(): result2 = start_child_span_decorator_py3(my_example_function)() fake_start_child.assert_called_once_with( - op="function", description="test_decorator.my_example_function" + op="function", description="test_decorator_py3.my_example_function" ) assert result2 == "return_of_sync_function" @@ -54,7 +54,7 @@ def test_trace_decorator_sync_py3_no_trx(): result2 = start_child_span_decorator_py3(my_example_function)() fake_warning.assert_called_once_with( "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", - "test_decorator.my_example_function", + "test_decorator_py3.my_example_function", ) assert result2 == "return_of_sync_function" @@ -75,7 +75,7 @@ async def test_trace_decorator_async_py3(): result2 = await start_child_span_decorator_py3(my_async_example_function)() fake_start_child.assert_called_once_with( - op="function", description="test_decorator.my_async_example_function" + op="function", description="test_decorator_py3.my_async_example_function" ) assert result2 == "return_of_async_function" @@ -98,6 +98,6 @@ async def test_trace_decorator_async_py3_no_trx(): )() fake_warning.assert_called_once_with( "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", - "test_decorator.my_async_example_function", + "test_decorator_py3.my_async_example_function", ) assert result2 == "return_of_async_function" From 333286f5009e9125b661b04bfb70219264b1e8f3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:34:45 +0100 Subject: [PATCH 19/40] Linting --- tests/tracing/test_decorator_py3.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 57e4aa74db..7a85b8c045 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -93,9 +93,7 @@ async def test_trace_decorator_async_py3_no_trx(): fake_warning.assert_not_called() assert result == "return_of_async_function" - result2 = await start_child_span_decorator_py3( - my_async_example_function - )() + result2 = await start_child_span_decorator_py3(my_async_example_function)() fake_warning.assert_called_once_with( "No transaction found. Not creating a child span for %s. Please start a Sentry transaction before calling this function.", "test_decorator_py3.my_async_example_function", From 3e0e7698148dd287f0e3dfb6cb6aa27cd91a1250 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:44:11 +0100 Subject: [PATCH 20/40] Fixed typing --- sentry_sdk/tracing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index e2e1cf5462..ae5191136e 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -823,9 +823,9 @@ async def my_async_function(): ... """ if PY2: - from sentry_sdk.tracing_utils_python2 import start_child_span_decorator + from sentry_sdk.tracing_utils_python2 import start_child_span_decorator # type: ignore else: - from sentry_sdk.tracing_utils_python3 import start_child_span_decorator + from sentry_sdk.tracing_utils_python3 import start_child_span_decorator # type: ignore # This patterns allows usage of both @sentry_traced and @sentry_traced(...) # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 From 1bdf047270127be9125361756bd345d7276b4f27 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 12:56:03 +0100 Subject: [PATCH 21/40] . --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index ae5191136e..5c3064c77e 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -823,7 +823,7 @@ async def my_async_function(): ... """ if PY2: - from sentry_sdk.tracing_utils_python2 import start_child_span_decorator # type: ignore + from sentry_sdk.tracing_utils_python2 import start_child_span_decorator else: from sentry_sdk.tracing_utils_python3 import start_child_span_decorator # type: ignore From 95e877e15234bf24e2a0a4080b238a0b8014e685 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 13:33:20 +0100 Subject: [PATCH 22/40] Create span under current span --- sentry_sdk/tracing.py | 4 ++-- sentry_sdk/tracing_utils_py2.py | 14 +++++++++----- sentry_sdk/tracing_utils_py3.py | 20 ++++++++++++-------- tests/tracing/test_decorator_py2.py | 4 ++-- tests/tracing/test_decorator_py3.py | 8 ++++---- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 5c3064c77e..edbe1834fc 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -823,9 +823,9 @@ async def my_async_function(): ... """ if PY2: - from sentry_sdk.tracing_utils_python2 import start_child_span_decorator + from sentry_sdk.tracing_utils_py2 import start_child_span_decorator else: - from sentry_sdk.tracing_utils_python3 import start_child_span_decorator # type: ignore + from sentry_sdk.tracing_utils_py3 import start_child_span_decorator # type: ignore # This patterns allows usage of both @sentry_traced and @sentry_traced(...) # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index 9b5bdc372a..b8fe54ba3a 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -7,13 +7,17 @@ if TYPE_CHECKING: - from typing import Any, Optional + from typing import Any, Optional, Union - from sentry_sdk.tracing import Transaction + from sentry_sdk.tracing import Span, Transaction -def _get_transaction(): - # type: () -> Optional[Transaction] +def _get_running_span_or_transaction(): + # type: () -> Optional[Union[Span, Transaction]] + current_span = sentry_sdk.Hub.current.scope.span + if current_span is not None: + return current_span + transaction = sentry_sdk.Hub.current.scope.transaction return transaction @@ -33,7 +37,7 @@ def start_child_span_decorator(func): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction = _get_transaction() + transaction = _get_running_span_or_transaction() # If no transaction, do nothing if transaction is None: diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index 4d62007412..f86138c5c3 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -8,13 +8,17 @@ if TYPE_CHECKING: - from typing import Any, Optional + from typing import Any, Optional, Union - from sentry_sdk.tracing import Transaction + from sentry_sdk.tracing import Span, Transaction -def _get_transaction(): - # type: () -> Optional[Transaction] +def _get_running_span_or_transaction(): + # type: () -> Optional[Union[Span, Transaction]] + current_span = sentry_sdk.Hub.current.scope.span + if current_span is not None: + return current_span + transaction = sentry_sdk.Hub.current.scope.transaction return transaction @@ -37,10 +41,10 @@ def start_child_span_decorator(func): async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction = _get_transaction() + span_or_trx = _get_running_span_or_transaction() # If no transaction, do nothing - if transaction is None: + if span_or_trx is None: logger.warning( "No transaction found. Not creating a child span for %s. " "Please start a Sentry transaction before calling this function.", @@ -49,7 +53,7 @@ async def func_with_tracing(*args, **kwargs): return await func(*args, **kwargs) # If we have a transaction, we wrap the function. - with transaction.start_child( + with span_or_trx.start_child( op=OP.FUNCTION, description=qualname_from_function(func), ): @@ -62,7 +66,7 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction = _get_transaction() + transaction = _get_running_span_or_transaction() # If no transaction, do nothing if transaction is None: diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index 21f09a56f1..92fd716c35 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -16,7 +16,7 @@ def test_trace_decorator_py2(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py2._get_transaction", + "sentry_sdk.tracing_utils_py2._get_running_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -34,7 +34,7 @@ def test_trace_decorator_py2_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py2._get_transaction", + "sentry_sdk.tracing_utils_py2._get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 7a85b8c045..8a63e94819 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -25,7 +25,7 @@ def test_trace_decorator_sync_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3._get_transaction", + "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -43,7 +43,7 @@ def test_trace_decorator_sync_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3._get_transaction", + "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: @@ -66,7 +66,7 @@ async def test_trace_decorator_async_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3._get_transaction", + "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", return_value=fake_transaction, ): result = await my_async_example_function() @@ -85,7 +85,7 @@ async def test_trace_decorator_async_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3._get_transaction", + "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: From 4c7f8ebd9a179eda671ff827a497578cda509ea6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 13:46:07 +0100 Subject: [PATCH 23/40] . --- sentry_sdk/tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index edbe1834fc..8939fce5d6 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -825,7 +825,7 @@ async def my_async_function(): if PY2: from sentry_sdk.tracing_utils_py2 import start_child_span_decorator else: - from sentry_sdk.tracing_utils_py3 import start_child_span_decorator # type: ignore + from sentry_sdk.tracing_utils_py3 import start_child_span_decorator # This patterns allows usage of both @sentry_traced and @sentry_traced(...) # See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278 From 6cf968a539c64e791f7d45baf3a063da571078fa Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 15:06:41 +0100 Subject: [PATCH 24/40] More test coverage --- sentry_sdk/tracing_utils_py2.py | 9 +++++---- sentry_sdk/tracing_utils_py3.py | 9 +++++---- tests/tracing/test_decorator_py2.py | 19 +++++++++++++++++++ tests/tracing/test_decorator_py3.py | 20 ++++++++++++++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index b8fe54ba3a..86dce68083 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -9,16 +9,17 @@ if TYPE_CHECKING: from typing import Any, Optional, Union + from sentry_sdk import Hub from sentry_sdk.tracing import Span, Transaction -def _get_running_span_or_transaction(): - # type: () -> Optional[Union[Span, Transaction]] - current_span = sentry_sdk.Hub.current.scope.span +def _get_running_span_or_transaction(hub): + # type: (Hub) -> Optional[Union[Span, Transaction]] + current_span = hub.scope.span if current_span is not None: return current_span - transaction = sentry_sdk.Hub.current.scope.transaction + transaction = hub.scope.transaction return transaction diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index f86138c5c3..efe95c4922 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -10,16 +10,17 @@ if TYPE_CHECKING: from typing import Any, Optional, Union + from sentry_sdk import Hub from sentry_sdk.tracing import Span, Transaction -def _get_running_span_or_transaction(): - # type: () -> Optional[Union[Span, Transaction]] - current_span = sentry_sdk.Hub.current.scope.span +def _get_running_span_or_transaction(hub): + # type: (Hub) -> Optional[Union[Span, Transaction]] + current_span = hub.scope.span if current_span is not None: return current_span - transaction = sentry_sdk.Hub.current.scope.transaction + transaction = hub.scope.transaction return transaction diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index 92fd716c35..a7ef93bacd 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -1,7 +1,9 @@ import mock +import sentry_sdk from sentry_sdk.tracing_utils_py2 import ( start_child_span_decorator as start_child_span_decorator_py2, + _get_running_span_or_transaction, ) from sentry_sdk.utils import logger @@ -10,6 +12,23 @@ def my_example_function(): return "return_of_sync_function" +def test_get_running_span_or_transaction(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.span = mock.MagicMock() + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span + + fake_hub.scope.span = None + fake_hub.scope.transaction = mock.MagicMock() + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction + + fake_hub.scope.span = None + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) == None + + def test_trace_decorator_py2(): fake_start_child = mock.MagicMock() fake_transaction = mock.MagicMock() diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 8a63e94819..6d1ae4f126 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -2,8 +2,10 @@ import pytest import sys +import sentry_sdk from sentry_sdk.tracing_utils_py3 import ( start_child_span_decorator as start_child_span_decorator_py3, + _get_running_span_or_transaction, ) from sentry_sdk.utils import logger @@ -19,6 +21,24 @@ async def my_async_example_function(): return "return_of_async_function" +def test_get_running_span_or_transaction(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.span = mock.MagicMock() + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span + + fake_hub.scope.span = None + fake_hub.scope.transaction = mock.MagicMock() + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction + + fake_hub.scope.span = None + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) == None + + + def test_trace_decorator_sync_py3(): fake_start_child = mock.MagicMock() fake_transaction = mock.MagicMock() From 0ec4f56ba8d7b5b7d4dd4c1aa63407dab1a5b69d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 15:18:07 +0100 Subject: [PATCH 25/40] Cleanup --- sentry_sdk/tracing_utils.py | 12 ++++++++++++ sentry_sdk/tracing_utils_py2.py | 24 +++++------------------- sentry_sdk/tracing_utils_py3.py | 28 ++++++---------------------- tests/tracing/test_decorator_py2.py | 19 ------------------- tests/tracing/test_decorator_py3.py | 20 -------------------- tests/tracing/test_misc.py | 22 +++++++++++++++++++++- 6 files changed, 44 insertions(+), 81 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 64155defdf..6095eb1290 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -33,6 +33,8 @@ from typing import Optional from typing import Union + from sentry_sdk import Hub + SENTRY_TRACE_REGEX = re.compile( "^[ \t]*" # whitespace @@ -395,6 +397,16 @@ def should_propagate_trace(hub, url): return False +def _get_running_span_or_transaction(hub): + # type: (Hub) -> Optional[Union[Span, Transaction]] + current_span = hub.scope.span + if current_span is not None: + return current_span + + transaction = hub.scope.transaction + return transaction + + # Circular imports from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index 86dce68083..390fe18eb2 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -2,25 +2,13 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.tracing_utils import _get_running_span_or_transaction from sentry_sdk.consts import OP from sentry_sdk.utils import logger, qualname_from_function if TYPE_CHECKING: - from typing import Any, Optional, Union - - from sentry_sdk import Hub - from sentry_sdk.tracing import Span, Transaction - - -def _get_running_span_or_transaction(hub): - # type: (Hub) -> Optional[Union[Span, Transaction]] - current_span = hub.scope.span - if current_span is not None: - return current_span - - transaction = hub.scope.transaction - return transaction + from typing import Any def start_child_span_decorator(func): @@ -38,10 +26,9 @@ def start_child_span_decorator(func): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction = _get_running_span_or_transaction() + span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) - # If no transaction, do nothing - if transaction is None: + if span_or_trx is None: logger.warning( "No transaction found. Not creating a child span for %s. " "Please start a Sentry transaction before calling this function.", @@ -49,8 +36,7 @@ def func_with_tracing(*args, **kwargs): ) return func(*args, **kwargs) - # If we have a transaction, we decorate the function! - with transaction.start_child( + with span_or_trx.start_child( op=OP.FUNCTION, description=qualname_from_function(func), ): diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index efe95c4922..5b0a58bb11 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -4,24 +4,12 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP +from sentry_sdk.tracing_utils import _get_running_span_or_transaction from sentry_sdk.utils import logger, qualname_from_function if TYPE_CHECKING: - from typing import Any, Optional, Union - - from sentry_sdk import Hub - from sentry_sdk.tracing import Span, Transaction - - -def _get_running_span_or_transaction(hub): - # type: (Hub) -> Optional[Union[Span, Transaction]] - current_span = hub.scope.span - if current_span is not None: - return current_span - - transaction = hub.scope.transaction - return transaction + from typing import Any def start_child_span_decorator(func): @@ -42,9 +30,8 @@ def start_child_span_decorator(func): async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = _get_running_span_or_transaction() + span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) - # If no transaction, do nothing if span_or_trx is None: logger.warning( "No transaction found. Not creating a child span for %s. " @@ -53,7 +40,6 @@ async def func_with_tracing(*args, **kwargs): ) return await func(*args, **kwargs) - # If we have a transaction, we wrap the function. with span_or_trx.start_child( op=OP.FUNCTION, description=qualname_from_function(func), @@ -67,10 +53,9 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - transaction = _get_running_span_or_transaction() + span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) - # If no transaction, do nothing - if transaction is None: + if span_or_trx is None: logger.warning( "No transaction found. Not creating a child span for %s. " "Please start a Sentry transaction before calling this function.", @@ -78,8 +63,7 @@ def func_with_tracing(*args, **kwargs): ) return func(*args, **kwargs) - # If we have a transaction, we decorate the function! - with transaction.start_child( + with span_or_trx.start_child( op=OP.FUNCTION, description=qualname_from_function(func), ): diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index a7ef93bacd..92fd716c35 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -1,9 +1,7 @@ import mock -import sentry_sdk from sentry_sdk.tracing_utils_py2 import ( start_child_span_decorator as start_child_span_decorator_py2, - _get_running_span_or_transaction, ) from sentry_sdk.utils import logger @@ -12,23 +10,6 @@ def my_example_function(): return "return_of_sync_function" -def test_get_running_span_or_transaction(): - fake_hub = mock.MagicMock() - fake_hub.scope = mock.MagicMock() - - fake_hub.scope.span = mock.MagicMock() - fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span - - fake_hub.scope.span = None - fake_hub.scope.transaction = mock.MagicMock() - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction - - fake_hub.scope.span = None - fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) == None - - def test_trace_decorator_py2(): fake_start_child = mock.MagicMock() fake_transaction = mock.MagicMock() diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 6d1ae4f126..8a63e94819 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -2,10 +2,8 @@ import pytest import sys -import sentry_sdk from sentry_sdk.tracing_utils_py3 import ( start_child_span_decorator as start_child_span_decorator_py3, - _get_running_span_or_transaction, ) from sentry_sdk.utils import logger @@ -21,24 +19,6 @@ async def my_async_example_function(): return "return_of_async_function" -def test_get_running_span_or_transaction(): - fake_hub = mock.MagicMock() - fake_hub.scope = mock.MagicMock() - - fake_hub.scope.span = mock.MagicMock() - fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span - - fake_hub.scope.span = None - fake_hub.scope.transaction = mock.MagicMock() - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction - - fake_hub.scope.span = None - fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) == None - - - def test_trace_decorator_sync_py3(): fake_start_child = mock.MagicMock() fake_transaction = mock.MagicMock() diff --git a/tests/tracing/test_misc.py b/tests/tracing/test_misc.py index 007dcb9151..1929ccbbe0 100644 --- a/tests/tracing/test_misc.py +++ b/tests/tracing/test_misc.py @@ -8,7 +8,10 @@ from sentry_sdk import Hub, start_span, start_transaction, set_measurement from sentry_sdk.consts import MATCH_ALL from sentry_sdk.tracing import Span, Transaction -from sentry_sdk.tracing_utils import should_propagate_trace +from sentry_sdk.tracing_utils import ( + should_propagate_trace, + _get_running_span_or_transaction, +) try: from unittest import mock # python 3.3 and above @@ -306,3 +309,20 @@ def test_should_propagate_trace( hub.client.options = {"trace_propagation_targets": trace_propagation_targets} assert should_propagate_trace(hub, url) == expected_propagation_decision + + +def test_get_running_span_or_transaction(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.span = mock.MagicMock() + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span + + fake_hub.scope.span = None + fake_hub.scope.transaction = mock.MagicMock() + assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction + + fake_hub.scope.span = None + fake_hub.scope.transaction = None + assert _get_running_span_or_transaction(fake_hub) is None From a882f54de04e928238d5dd4a499fed90c81a13e4 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 15:22:30 +0100 Subject: [PATCH 26/40] Fixed typing --- sentry_sdk/tracing_utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 6095eb1290..1663d8ae8d 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -33,8 +33,6 @@ from typing import Optional from typing import Union - from sentry_sdk import Hub - SENTRY_TRACE_REGEX = re.compile( "^[ \t]*" # whitespace @@ -398,7 +396,7 @@ def should_propagate_trace(hub, url): def _get_running_span_or_transaction(hub): - # type: (Hub) -> Optional[Union[Span, Transaction]] + # type: (sentry_sdk.Hub) -> Optional[Union[Span, Transaction]] current_span = hub.scope.span if current_span is not None: return current_span From e51a93f793acb9105d162dc434132c00b708f8e5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 8 Mar 2023 15:40:09 +0100 Subject: [PATCH 27/40] Better naming --- sentry_sdk/tracing_utils.py | 2 +- sentry_sdk/tracing_utils_py2.py | 4 ++-- sentry_sdk/tracing_utils_py3.py | 6 +++--- tests/tracing/test_decorator_py2.py | 4 ++-- tests/tracing/test_decorator_py3.py | 8 ++++---- tests/tracing/test_misc.py | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 1663d8ae8d..5ca1f548b9 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -395,7 +395,7 @@ def should_propagate_trace(hub, url): return False -def _get_running_span_or_transaction(hub): +def get_running_span_or_transaction(hub): # type: (sentry_sdk.Hub) -> Optional[Union[Span, Transaction]] current_span = hub.scope.span if current_span is not None: diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index 390fe18eb2..413cfda4c9 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -2,7 +2,7 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.tracing_utils import _get_running_span_or_transaction +from sentry_sdk.tracing_utils import get_running_span_or_transaction from sentry_sdk.consts import OP from sentry_sdk.utils import logger, qualname_from_function @@ -26,7 +26,7 @@ def start_child_span_decorator(func): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index 5b0a58bb11..fb991dbeca 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -4,7 +4,7 @@ import sentry_sdk from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP -from sentry_sdk.tracing_utils import _get_running_span_or_transaction +from sentry_sdk.tracing_utils import get_running_span_or_transaction from sentry_sdk.utils import logger, qualname_from_function @@ -30,7 +30,7 @@ def start_child_span_decorator(func): async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( @@ -53,7 +53,7 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = _get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index 92fd716c35..36f5dd3805 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -16,7 +16,7 @@ def test_trace_decorator_py2(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py2._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_running_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -34,7 +34,7 @@ def test_trace_decorator_py2_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py2._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 8a63e94819..101ee9c27c 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -25,7 +25,7 @@ def test_trace_decorator_sync_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -43,7 +43,7 @@ def test_trace_decorator_sync_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: @@ -66,7 +66,7 @@ async def test_trace_decorator_async_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", return_value=fake_transaction, ): result = await my_async_example_function() @@ -85,7 +85,7 @@ async def test_trace_decorator_async_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3._get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_misc.py b/tests/tracing/test_misc.py index 1929ccbbe0..deefbce7af 100644 --- a/tests/tracing/test_misc.py +++ b/tests/tracing/test_misc.py @@ -10,7 +10,7 @@ from sentry_sdk.tracing import Span, Transaction from sentry_sdk.tracing_utils import ( should_propagate_trace, - _get_running_span_or_transaction, + get_running_span_or_transaction, ) try: @@ -317,12 +317,12 @@ def test_get_running_span_or_transaction(): fake_hub.scope.span = mock.MagicMock() fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.span + assert get_running_span_or_transaction(fake_hub) == fake_hub.scope.span fake_hub.scope.span = None fake_hub.scope.transaction = mock.MagicMock() - assert _get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction + assert get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction fake_hub.scope.span = None fake_hub.scope.transaction = None - assert _get_running_span_or_transaction(fake_hub) is None + assert get_running_span_or_transaction(fake_hub) is None From 4af3170ae8e3b88fbdcfb85299855937e57db6f5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 14 Mar 2023 11:04:30 +0100 Subject: [PATCH 28/40] Added top level api to get current span and transaction --- sentry_sdk/api.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_api.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/test_api.py diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 1681ef48a0..ad83551eac 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -53,6 +53,9 @@ def overload(x): "set_user", "set_level", "set_measurement", + "get_current_span", + "get_current_transaction", + "get_current_span_or_transaction", ] @@ -228,3 +231,44 @@ def set_measurement(name, value, unit=""): transaction = Hub.current.scope.transaction if transaction is not None: transaction.set_measurement(name, value, unit) + + +def get_current_span(hub=None): + # type: (Optional[Hub]) -> Optional[Span] + """ + Returns the currently active span if there is one running, otherwise `None` + """ + if hub is None: + hub = Hub.current + + current_span = hub.scope.span + return current_span + + +def get_current_transaction(hub=None): + # type: (Optional[Hub]) -> Optional[Transaction] + """ + Returns the currently active transaction if there is one running, otherwise `None` + """ + if hub is None: + hub = Hub.current + + transaction = hub.scope.transaction + return transaction + + +def get_current_span_or_transaction(hub=None): + # type: (Optional[Hub]) -> Optional[Union[Span, Transaction]] + """ + Returns the currently active span if there is one running, + otherwise the currently active transaction if there is one running, + otherwise `None` + """ + if hub is None: + hub = Hub.current + + current_span = get_current_span(hub) + if current_span is not None: + return current_span + + return get_current_transaction(hub) diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000000..de5e586b7f --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,46 @@ +import mock + +from sentry_sdk import ( + get_current_span_or_transaction, + get_current_span, + get_current_transaction, +) + + +def test_get_current_span(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.span = mock.MagicMock() + assert get_current_span(fake_hub) == fake_hub.scope.span + + fake_hub.scope.span = None + assert get_current_span(fake_hub) is None + + +def test_get_current_transaction(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.transaction = mock.MagicMock() + assert get_current_transaction(fake_hub) == fake_hub.scope.transaction + + fake_hub.scope.transaction = None + assert get_current_transaction(fake_hub) is None + + +def test_get_current_span_or_transaction(): + fake_hub = mock.MagicMock() + fake_hub.scope = mock.MagicMock() + + fake_hub.scope.span = mock.MagicMock() + fake_hub.scope.transaction = None + assert get_current_span_or_transaction(fake_hub) == fake_hub.scope.span + + fake_hub.scope.span = None + fake_hub.scope.transaction = mock.MagicMock() + assert get_current_span_or_transaction(fake_hub) == fake_hub.scope.transaction + + fake_hub.scope.span = None + fake_hub.scope.transaction = None + assert get_current_span_or_transaction(fake_hub) is None From 4efec57145187b527a577cbdfd526376a5c18496 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 14 Mar 2023 11:23:41 +0100 Subject: [PATCH 29/40] Split out function in separate PR --- sentry_sdk/tracing_utils.py | 10 ---------- sentry_sdk/tracing_utils_py2.py | 4 ++-- sentry_sdk/tracing_utils_py3.py | 6 +++--- tests/tracing/test_decorator_py2.py | 4 ++-- tests/tracing/test_decorator_py3.py | 8 ++++---- tests/tracing/test_misc.py | 22 +--------------------- 6 files changed, 12 insertions(+), 42 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 5ca1f548b9..64155defdf 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -395,16 +395,6 @@ def should_propagate_trace(hub, url): return False -def get_running_span_or_transaction(hub): - # type: (sentry_sdk.Hub) -> Optional[Union[Span, Transaction]] - current_span = hub.scope.span - if current_span is not None: - return current_span - - transaction = hub.scope.transaction - return transaction - - # Circular imports from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index 413cfda4c9..5d50fdeaaa 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -1,8 +1,8 @@ from functools import wraps import sentry_sdk +from sentry_sdk import get_current_span_or_transaction from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.tracing_utils import get_running_span_or_transaction from sentry_sdk.consts import OP from sentry_sdk.utils import logger, qualname_from_function @@ -26,7 +26,7 @@ def start_child_span_decorator(func): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index fb991dbeca..be07dc57b1 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -2,9 +2,9 @@ from functools import wraps import sentry_sdk +from sentry_sdk import get_current_span_or_transaction from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP -from sentry_sdk.tracing_utils import get_running_span_or_transaction from sentry_sdk.utils import logger, qualname_from_function @@ -30,7 +30,7 @@ def start_child_span_decorator(func): async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( @@ -53,7 +53,7 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_running_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index 36f5dd3805..6dbf4b8768 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -16,7 +16,7 @@ def test_trace_decorator_py2(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py2.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_current_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -34,7 +34,7 @@ def test_trace_decorator_py2_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py2.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_current_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 101ee9c27c..798883f282 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -25,7 +25,7 @@ def test_trace_decorator_sync_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", return_value=fake_transaction, ): result = my_example_function() @@ -43,7 +43,7 @@ def test_trace_decorator_sync_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: @@ -66,7 +66,7 @@ async def test_trace_decorator_async_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", return_value=fake_transaction, ): result = await my_async_example_function() @@ -85,7 +85,7 @@ async def test_trace_decorator_async_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3.get_running_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_misc.py b/tests/tracing/test_misc.py index deefbce7af..007dcb9151 100644 --- a/tests/tracing/test_misc.py +++ b/tests/tracing/test_misc.py @@ -8,10 +8,7 @@ from sentry_sdk import Hub, start_span, start_transaction, set_measurement from sentry_sdk.consts import MATCH_ALL from sentry_sdk.tracing import Span, Transaction -from sentry_sdk.tracing_utils import ( - should_propagate_trace, - get_running_span_or_transaction, -) +from sentry_sdk.tracing_utils import should_propagate_trace try: from unittest import mock # python 3.3 and above @@ -309,20 +306,3 @@ def test_should_propagate_trace( hub.client.options = {"trace_propagation_targets": trace_propagation_targets} assert should_propagate_trace(hub, url) == expected_propagation_decision - - -def test_get_running_span_or_transaction(): - fake_hub = mock.MagicMock() - fake_hub.scope = mock.MagicMock() - - fake_hub.scope.span = mock.MagicMock() - fake_hub.scope.transaction = None - assert get_running_span_or_transaction(fake_hub) == fake_hub.scope.span - - fake_hub.scope.span = None - fake_hub.scope.transaction = mock.MagicMock() - assert get_running_span_or_transaction(fake_hub) == fake_hub.scope.transaction - - fake_hub.scope.span = None - fake_hub.scope.transaction = None - assert get_running_span_or_transaction(fake_hub) is None From a1e49c84c1e4cbbc4f6b141e4024e8c2b532b5db Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 14 Mar 2023 15:48:48 +0100 Subject: [PATCH 30/40] Always install pytest-asyncio in Python3 to run new async tests in core test suite --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index e2fefba5c2..adfde03c57 100644 --- a/tox.ini +++ b/tox.ini @@ -164,6 +164,8 @@ deps = py3.8: hypothesis + {py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}: pytest-asyncio + linters: -r linter-requirements.txt # Gevent From 326570954803d060b86d506ea808bd1f08ab4f2f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 09:47:06 +0100 Subject: [PATCH 31/40] Updated test config to make it possible to have dependencies only needed for common tests --- .github/workflows/test-common.yml | 34 ++++++++++++++---- .../workflows/test-integration-aiohttp.yml | 2 +- .github/workflows/test-integration-arq.yml | 2 +- .github/workflows/test-integration-asgi.yml | 2 +- .../workflows/test-integration-aws_lambda.yml | 2 +- .github/workflows/test-integration-beam.yml | 2 +- .github/workflows/test-integration-boto3.yml | 2 +- .github/workflows/test-integration-bottle.yml | 2 +- .github/workflows/test-integration-celery.yml | 2 +- .../workflows/test-integration-chalice.yml | 2 +- ...est-integration-cloud_resource_context.yml | 2 +- .github/workflows/test-integration-django.yml | 2 +- .github/workflows/test-integration-falcon.yml | 2 +- .../workflows/test-integration-fastapi.yml | 2 +- .github/workflows/test-integration-flask.yml | 2 +- .github/workflows/test-integration-gcp.yml | 2 +- .github/workflows/test-integration-gevent.yml | 2 +- .github/workflows/test-integration-httpx.yml | 2 +- .github/workflows/test-integration-huey.yml | 2 +- .../test-integration-opentelemetry.yml | 2 +- .../workflows/test-integration-pure_eval.yml | 2 +- .../workflows/test-integration-pymongo.yml | 2 +- .../workflows/test-integration-pyramid.yml | 2 +- .github/workflows/test-integration-quart.yml | 2 +- .github/workflows/test-integration-redis.yml | 2 +- .../test-integration-rediscluster.yml | 2 +- .../workflows/test-integration-requests.yml | 2 +- .github/workflows/test-integration-rq.yml | 2 +- .github/workflows/test-integration-sanic.yml | 2 +- .../workflows/test-integration-sqlalchemy.yml | 2 +- .../workflows/test-integration-starlette.yml | 2 +- .../workflows/test-integration-starlite.yml | 2 +- .../workflows/test-integration-tornado.yml | 2 +- .../workflows/test-integration-trytond.yml | 2 +- scripts/split-tox-gh-actions/ci-yaml.txt | 2 +- .../split-tox-gh-actions.py | 6 +++- tox.ini | 36 +++++++++---------- 37 files changed, 84 insertions(+), 60 deletions(-) diff --git a/.github/workflows/test-common.yml b/.github/workflows/test-common.yml index fee76bec60..0937f5eb55 100644 --- a/.github/workflows/test-common.yml +++ b/.github/workflows/test-common.yml @@ -1,4 +1,4 @@ -name: Test Common +name: Test common on: push: @@ -8,6 +8,12 @@ on: pull_request: +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + permissions: contents: read @@ -18,18 +24,20 @@ env: jobs: test: - name: Test Python ${{ matrix.python-version }}, ${{ matrix.os }} + name: common, python ${{ matrix.python-version }}, ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 45 - continue-on-error: true + strategy: + fail-fast: false matrix: + python-version: ["2.7","3.4","3.5","3.6","3.7","3.8","3.9","3.10","3.11"] # 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 # see https://github.com/actions/setup-python/issues/544#issuecomment-1332535877 os: [ubuntu-20.04] - python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] + steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 @@ -38,16 +46,28 @@ jobs: - name: Setup Test Env run: | - pip install codecov tox + pip install codecov "tox>=3,<4" - - name: Run Tests + - name: Test common timeout-minutes: 45 shell: bash run: | set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "py${{ matrix.python-version }}$" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch --ignore=tests/integrations + ./scripts/runtox.sh "py${{ matrix.python-version }}-common" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml + + check_required_tests: + name: All common tests passed or skipped + needs: test + # Always run this, even if a dependent job failed + if: always() + runs-on: ubuntu-20.04 + steps: + - name: Check for failures + if: contains(needs.test.result, 'failure') + run: | + echo "One of the dependent jobs have failed. You may need to re-run it." && exit 1 diff --git a/.github/workflows/test-integration-aiohttp.yml b/.github/workflows/test-integration-aiohttp.yml index 7ec01b12db..7d27b7ab2b 100644 --- a/.github/workflows/test-integration-aiohttp.yml +++ b/.github/workflows/test-integration-aiohttp.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-aiohttp" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-aiohttp" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-arq.yml b/.github/workflows/test-integration-arq.yml index 2eee836bc1..d4e69133f8 100644 --- a/.github/workflows/test-integration-arq.yml +++ b/.github/workflows/test-integration-arq.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-arq" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-arq" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-asgi.yml b/.github/workflows/test-integration-asgi.yml index 39f63d6e89..9d1ecd2d79 100644 --- a/.github/workflows/test-integration-asgi.yml +++ b/.github/workflows/test-integration-asgi.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-asgi" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-asgi" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-aws_lambda.yml b/.github/workflows/test-integration-aws_lambda.yml index 22ed7f4945..3f58e0a271 100644 --- a/.github/workflows/test-integration-aws_lambda.yml +++ b/.github/workflows/test-integration-aws_lambda.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-aws_lambda" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-aws_lambda" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-beam.yml b/.github/workflows/test-integration-beam.yml index 03a484537c..688ea59d98 100644 --- a/.github/workflows/test-integration-beam.yml +++ b/.github/workflows/test-integration-beam.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-beam" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-beam" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-boto3.yml b/.github/workflows/test-integration-boto3.yml index cbb4ec7db1..5ac47b11a6 100644 --- a/.github/workflows/test-integration-boto3.yml +++ b/.github/workflows/test-integration-boto3.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-boto3" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-boto3" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-bottle.yml b/.github/workflows/test-integration-bottle.yml index 60979bf5dd..ba98aa24fe 100644 --- a/.github/workflows/test-integration-bottle.yml +++ b/.github/workflows/test-integration-bottle.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-bottle" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-bottle" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-celery.yml b/.github/workflows/test-integration-celery.yml index 7042f8d493..4631d53b91 100644 --- a/.github/workflows/test-integration-celery.yml +++ b/.github/workflows/test-integration-celery.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-celery" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-celery" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-chalice.yml b/.github/workflows/test-integration-chalice.yml index d8240fe024..f9ec86e447 100644 --- a/.github/workflows/test-integration-chalice.yml +++ b/.github/workflows/test-integration-chalice.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-chalice" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-chalice" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-cloud_resource_context.yml b/.github/workflows/test-integration-cloud_resource_context.yml index d4e2a25be8..bbc99d2ffd 100644 --- a/.github/workflows/test-integration-cloud_resource_context.yml +++ b/.github/workflows/test-integration-cloud_resource_context.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-cloud_resource_context" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-cloud_resource_context" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-django.yml b/.github/workflows/test-integration-django.yml index 2e462a723a..165c99e8b0 100644 --- a/.github/workflows/test-integration-django.yml +++ b/.github/workflows/test-integration-django.yml @@ -73,7 +73,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-django" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-django" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-falcon.yml b/.github/workflows/test-integration-falcon.yml index 259006f106..07af9c87c7 100644 --- a/.github/workflows/test-integration-falcon.yml +++ b/.github/workflows/test-integration-falcon.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-falcon" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-falcon" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-fastapi.yml b/.github/workflows/test-integration-fastapi.yml index 1b6e4e24b5..a3983594fb 100644 --- a/.github/workflows/test-integration-fastapi.yml +++ b/.github/workflows/test-integration-fastapi.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-fastapi" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-fastapi" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-flask.yml b/.github/workflows/test-integration-flask.yml index 91e50a4eac..b4b37e80ab 100644 --- a/.github/workflows/test-integration-flask.yml +++ b/.github/workflows/test-integration-flask.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-flask" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-flask" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-gcp.yml b/.github/workflows/test-integration-gcp.yml index ca6275a537..5fe59bdb67 100644 --- a/.github/workflows/test-integration-gcp.yml +++ b/.github/workflows/test-integration-gcp.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-gcp" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-gcp" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-gevent.yml b/.github/workflows/test-integration-gevent.yml index ce22867c50..8c993da6df 100644 --- a/.github/workflows/test-integration-gevent.yml +++ b/.github/workflows/test-integration-gevent.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-gevent" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-gevent" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-httpx.yml b/.github/workflows/test-integration-httpx.yml index d8ac90e7bf..1154d1586e 100644 --- a/.github/workflows/test-integration-httpx.yml +++ b/.github/workflows/test-integration-httpx.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-httpx" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-httpx" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-huey.yml b/.github/workflows/test-integration-huey.yml index 4226083299..12eeb52e0b 100644 --- a/.github/workflows/test-integration-huey.yml +++ b/.github/workflows/test-integration-huey.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-huey" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-huey" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-opentelemetry.yml b/.github/workflows/test-integration-opentelemetry.yml index 7c2caa07a5..ccbe4d2a63 100644 --- a/.github/workflows/test-integration-opentelemetry.yml +++ b/.github/workflows/test-integration-opentelemetry.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-opentelemetry" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-opentelemetry" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-pure_eval.yml b/.github/workflows/test-integration-pure_eval.yml index 2f72e39bf4..813749bf98 100644 --- a/.github/workflows/test-integration-pure_eval.yml +++ b/.github/workflows/test-integration-pure_eval.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-pure_eval" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-pure_eval" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-pymongo.yml b/.github/workflows/test-integration-pymongo.yml index b65fe7f74f..49bb67e7fe 100644 --- a/.github/workflows/test-integration-pymongo.yml +++ b/.github/workflows/test-integration-pymongo.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-pymongo" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-pymongo" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-pyramid.yml b/.github/workflows/test-integration-pyramid.yml index bb8faeab84..1c1fc8d416 100644 --- a/.github/workflows/test-integration-pyramid.yml +++ b/.github/workflows/test-integration-pyramid.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-pyramid" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-pyramid" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-quart.yml b/.github/workflows/test-integration-quart.yml index b6ca340ac6..5de9f92b35 100644 --- a/.github/workflows/test-integration-quart.yml +++ b/.github/workflows/test-integration-quart.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-quart" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-quart" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-redis.yml b/.github/workflows/test-integration-redis.yml index 7d5eb18fb9..c612ca4ca3 100644 --- a/.github/workflows/test-integration-redis.yml +++ b/.github/workflows/test-integration-redis.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-redis" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-redis" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-rediscluster.yml b/.github/workflows/test-integration-rediscluster.yml index 453d4984a9..102838def1 100644 --- a/.github/workflows/test-integration-rediscluster.yml +++ b/.github/workflows/test-integration-rediscluster.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-rediscluster" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-rediscluster" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-requests.yml b/.github/workflows/test-integration-requests.yml index d07b8a7ec1..f4fcc1a170 100644 --- a/.github/workflows/test-integration-requests.yml +++ b/.github/workflows/test-integration-requests.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-requests" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-requests" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-rq.yml b/.github/workflows/test-integration-rq.yml index 78b0b44e29..132a87b35c 100644 --- a/.github/workflows/test-integration-rq.yml +++ b/.github/workflows/test-integration-rq.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-rq" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-rq" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-sanic.yml b/.github/workflows/test-integration-sanic.yml index aae23aad58..cbdfb3e142 100644 --- a/.github/workflows/test-integration-sanic.yml +++ b/.github/workflows/test-integration-sanic.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-sanic" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-sanic" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-sqlalchemy.yml b/.github/workflows/test-integration-sqlalchemy.yml index 9bdb5064ce..c9b011571d 100644 --- a/.github/workflows/test-integration-sqlalchemy.yml +++ b/.github/workflows/test-integration-sqlalchemy.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-sqlalchemy" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-sqlalchemy" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-starlette.yml b/.github/workflows/test-integration-starlette.yml index 8ebe2442d0..464e603693 100644 --- a/.github/workflows/test-integration-starlette.yml +++ b/.github/workflows/test-integration-starlette.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-starlette" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-starlette" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-starlite.yml b/.github/workflows/test-integration-starlite.yml index 8a40f7d48c..f36ec659fb 100644 --- a/.github/workflows/test-integration-starlite.yml +++ b/.github/workflows/test-integration-starlite.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-starlite" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-starlite" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-tornado.yml b/.github/workflows/test-integration-tornado.yml index 05055b1e9d..32f66a6ab3 100644 --- a/.github/workflows/test-integration-tornado.yml +++ b/.github/workflows/test-integration-tornado.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-tornado" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-tornado" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/.github/workflows/test-integration-trytond.yml b/.github/workflows/test-integration-trytond.yml index b8d6497e6d..83456a4235 100644 --- a/.github/workflows/test-integration-trytond.yml +++ b/.github/workflows/test-integration-trytond.yml @@ -55,7 +55,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-trytond" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-trytond" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/scripts/split-tox-gh-actions/ci-yaml.txt b/scripts/split-tox-gh-actions/ci-yaml.txt index b9ecdf39e7..7f3fa6b037 100644 --- a/scripts/split-tox-gh-actions/ci-yaml.txt +++ b/scripts/split-tox-gh-actions/ci-yaml.txt @@ -47,7 +47,7 @@ jobs: set -x # print commands that are executed coverage erase - ./scripts/runtox.sh "${{ matrix.python-version }}-{{ framework }}" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch + ./scripts/runtox.sh "py${{ matrix.python-version }}-{{ framework }}" --cov=tests --cov=sentry_sdk --cov-report= --cov-branch coverage combine .coverage* coverage xml -i codecov --file coverage.xml diff --git a/scripts/split-tox-gh-actions/split-tox-gh-actions.py b/scripts/split-tox-gh-actions/split-tox-gh-actions.py index 62f79d5fb7..3cefbda695 100755 --- a/scripts/split-tox-gh-actions/split-tox-gh-actions.py +++ b/scripts/split-tox-gh-actions/split-tox-gh-actions.py @@ -71,7 +71,11 @@ def write_yaml_file( out += template_line.replace("{{ framework }}", current_framework) # write rendered template - outfile_name = OUT_DIR / f"test-integration-{current_framework}.yml" + if current_framework == "common": + outfile_name = OUT_DIR / f"test-{current_framework}.yml" + else: + outfile_name = OUT_DIR / f"test-integration-{current_framework}.yml" + print(f"Writing {outfile_name}") f = open(outfile_name, "w") f.writelines(out) diff --git a/tox.ini b/tox.ini index adfde03c57..7a5066dc36 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,8 @@ [tox] envlist = - # === Core === - {py2.7,py3.4,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11} + # === Common === + {py2.7,py3.4,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common # === Integrations === # General format is {pythonversion}-{integrationname}-v{frameworkversion} @@ -159,24 +159,14 @@ deps = # with the -r flag -r test-requirements.txt - py3.4: colorama==0.4.1 - py3.4: watchdog==0.10.7 - - py3.8: hypothesis - - {py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}: pytest-asyncio + py3.4-common: colorama==0.4.1 + py3.4-common: watchdog==0.10.7 + py3.8-common: hypothesis linters: -r linter-requirements.txt - # Gevent - # See http://www.gevent.org/install.html#older-versions-of-python - # for justification of the versions pinned below - py3.4-gevent: gevent==1.4.0 - py3.5-gevent: gevent==20.9.0 - # See https://stackoverflow.com/questions/51496550/runtime-warning-greenlet-greenlet-size-changed - # for justification why greenlet is pinned here - py3.5-gevent: greenlet==0.4.17 - {py2.7,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-gevent: gevent>=22.10.0, <22.11.0 + # Common + {py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common: pytest-asyncio # AIOHTTP aiohttp-v3.4: aiohttp>=3.4.0,<3.5.0 @@ -291,6 +281,16 @@ deps = flask-v1.1: Flask>=1.1,<1.2 flask-v2.0: Flask>=2.0,<2.1 + # Gevent + # See http://www.gevent.org/install.html#older-versions-of-python + # for justification of the versions pinned below + py3.4-gevent: gevent==1.4.0 + py3.5-gevent: gevent==20.9.0 + # See https://stackoverflow.com/questions/51496550/runtime-warning-greenlet-greenlet-size-changed + # for justification why greenlet is pinned here + py3.5-gevent: greenlet==0.4.17 + {py2.7,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-gevent: gevent>=22.10.0, <22.11.0 + # HTTPX httpx: pytest-httpx httpx-v0.16: httpx>=0.16,<0.17 @@ -411,7 +411,7 @@ deps = setenv = PYTHONDONTWRITEBYTECODE=1 - TESTPATH=tests + common: TESTPATH=tests aiohttp: TESTPATH=tests/integrations/aiohttp arq: TESTPATH=tests/integrations/arq asgi: TESTPATH=tests/integrations/asgi From 933535f425edacd3a2c19d97b241ec78560f210e Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 11:37:15 +0100 Subject: [PATCH 32/40] Fixed minimum Python versions the tests should run in. --- tests/integrations/asyncio/test_asyncio.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integrations/asyncio/test_asyncio.py b/tests/integrations/asyncio/test_asyncio.py index f29a793e04..db9d242ef0 100644 --- a/tests/integrations/asyncio/test_asyncio.py +++ b/tests/integrations/asyncio/test_asyncio.py @@ -9,8 +9,8 @@ from sentry_sdk.integrations.asyncio import AsyncioIntegration -minimum_python_36 = pytest.mark.skipif( - sys.version_info < (3, 6), reason="ASGI is only supported in Python >= 3.6" +minimum_python_37 = pytest.mark.skipif( + sys.version_info < (3, 7), reason="Asyncio tests need Python >= 3.7" ) @@ -34,7 +34,7 @@ def event_loop(request): loop.close() -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_create_task( sentry_init, @@ -79,7 +79,7 @@ async def test_create_task( ) -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_gather( sentry_init, @@ -122,7 +122,7 @@ async def test_gather( ) -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_exception( sentry_init, @@ -157,7 +157,7 @@ async def test_exception( assert error_event["exception"]["values"][0]["mechanism"]["type"] == "asyncio" -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_task_result(sentry_init): sentry_init( From c124a7dca424af869cc348c74407f7df67c16457 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 11:46:43 +0100 Subject: [PATCH 33/40] Dont run common tests in py3.4 (like it was before) --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7a5066dc36..a305758d70 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ [tox] envlist = # === Common === - {py2.7,py3.4,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common + {py2.7,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common # === Integrations === # General format is {pythonversion}-{integrationname}-v{frameworkversion} From d5e10fb2db43be6c52aae4bf2fa4a86c6228e1d5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 11:50:59 +0100 Subject: [PATCH 34/40] Updated test matrix --- .github/workflows/test-common.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-common.yml b/.github/workflows/test-common.yml index 0937f5eb55..a2774939dc 100644 --- a/.github/workflows/test-common.yml +++ b/.github/workflows/test-common.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["2.7","3.4","3.5","3.6","3.7","3.8","3.9","3.10","3.11"] + python-version: ["2.7","3.5","3.6","3.7","3.8","3.9","3.10","3.11"] # 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 From 2ede736188b036040bcb2529fd0b753c0e538d10 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 12:05:01 +0100 Subject: [PATCH 35/40] Trying normal fixture --- tests/integrations/asyncio/__init__.py | 3 --- tests/integrations/asyncio/test_asyncio.py | 15 ++++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/integrations/asyncio/__init__.py b/tests/integrations/asyncio/__init__.py index 1b887a03fe..e69de29bb2 100644 --- a/tests/integrations/asyncio/__init__.py +++ b/tests/integrations/asyncio/__init__.py @@ -1,3 +0,0 @@ -import pytest - -pytest.importorskip("pytest_asyncio") diff --git a/tests/integrations/asyncio/test_asyncio.py b/tests/integrations/asyncio/test_asyncio.py index db9d242ef0..8581e65e5e 100644 --- a/tests/integrations/asyncio/test_asyncio.py +++ b/tests/integrations/asyncio/test_asyncio.py @@ -9,8 +9,9 @@ from sentry_sdk.integrations.asyncio import AsyncioIntegration -minimum_python_37 = pytest.mark.skipif( - sys.version_info < (3, 7), reason="Asyncio tests need Python >= 3.7" + +minimum_python_36 = pytest.mark.skipif( + sys.version_info < (3, 6), reason="Asyncio tests need Python >= 3.6" ) @@ -26,7 +27,7 @@ async def boom(): 1 / 0 -@pytest_asyncio.fixture(scope="session") +@pytest.fixture(scope="session") def event_loop(request): """Create an instance of the default event loop for each test case.""" loop = asyncio.get_event_loop_policy().new_event_loop() @@ -34,7 +35,7 @@ def event_loop(request): loop.close() -@minimum_python_37 +@minimum_python_36 @pytest.mark.asyncio async def test_create_task( sentry_init, @@ -79,7 +80,7 @@ async def test_create_task( ) -@minimum_python_37 +@minimum_python_36 @pytest.mark.asyncio async def test_gather( sentry_init, @@ -122,7 +123,7 @@ async def test_gather( ) -@minimum_python_37 +@minimum_python_36 @pytest.mark.asyncio async def test_exception( sentry_init, @@ -157,7 +158,7 @@ async def test_exception( assert error_event["exception"]["values"][0]["mechanism"]["type"] == "asyncio" -@minimum_python_37 +@minimum_python_36 @pytest.mark.asyncio async def test_task_result(sentry_init): sentry_init( From cefd2bc4b21e5413e46682ffe4daf549561894e5 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 13:28:09 +0100 Subject: [PATCH 36/40] Run asyncio tests only in python >3.6 --- tests/integrations/asyncio/__init__.py | 7 +++++++ .../{test_asyncio.py => test_asyncio_py3.py} | 14 ++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) rename tests/integrations/asyncio/{test_asyncio.py => test_asyncio_py3.py} (95%) diff --git a/tests/integrations/asyncio/__init__.py b/tests/integrations/asyncio/__init__.py index e69de29bb2..67b114b624 100644 --- a/tests/integrations/asyncio/__init__.py +++ b/tests/integrations/asyncio/__init__.py @@ -0,0 +1,7 @@ +# import os +# import pytest + +# test_path = os.environ.get("TESTPATH") + +# if "asyncio" not in test_path: +# pytest.skip() diff --git a/tests/integrations/asyncio/test_asyncio.py b/tests/integrations/asyncio/test_asyncio_py3.py similarity index 95% rename from tests/integrations/asyncio/test_asyncio.py rename to tests/integrations/asyncio/test_asyncio_py3.py index 8581e65e5e..98106ed01f 100644 --- a/tests/integrations/asyncio/test_asyncio.py +++ b/tests/integrations/asyncio/test_asyncio_py3.py @@ -2,16 +2,14 @@ import sys import pytest -import pytest_asyncio import sentry_sdk from sentry_sdk.consts import OP from sentry_sdk.integrations.asyncio import AsyncioIntegration - -minimum_python_36 = pytest.mark.skipif( - sys.version_info < (3, 6), reason="Asyncio tests need Python >= 3.6" +minimum_python_37 = pytest.mark.skipif( + sys.version_info < (3, 7), reason="Asyncio tests need Python >= 3.7" ) @@ -35,7 +33,7 @@ def event_loop(request): loop.close() -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_create_task( sentry_init, @@ -80,7 +78,7 @@ async def test_create_task( ) -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_gather( sentry_init, @@ -123,7 +121,7 @@ async def test_gather( ) -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_exception( sentry_init, @@ -158,7 +156,7 @@ async def test_exception( assert error_event["exception"]["values"][0]["mechanism"]["type"] == "asyncio" -@minimum_python_36 +@minimum_python_37 @pytest.mark.asyncio async def test_task_result(sentry_init): sentry_init( From 535eb74d3e301c0bab43981de3c4ab714e0235e2 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 13:44:50 +0100 Subject: [PATCH 37/40] Remove check because it is not really useful and breaks when asyncio stuff is installed --- tests/integrations/asyncio/__init__.py | 7 ------- tests/integrations/stdlib/test_httplib.py | 3 --- 2 files changed, 10 deletions(-) diff --git a/tests/integrations/asyncio/__init__.py b/tests/integrations/asyncio/__init__.py index 67b114b624..e69de29bb2 100644 --- a/tests/integrations/asyncio/__init__.py +++ b/tests/integrations/asyncio/__init__.py @@ -1,7 +0,0 @@ -# import os -# import pytest - -# test_path = os.environ.get("TESTPATH") - -# if "asyncio" not in test_path: -# pytest.skip() diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 6998db9d7d..c5eade4a03 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -85,9 +85,6 @@ def before_breadcrumb(crumb, hint): "http.query": "", } - if platform.python_implementation() != "PyPy": - assert sys.getrefcount(response) == 2 - def test_empty_realurl(sentry_init, capture_events): """ From c1b6a6b275ee31a05606f19c90bb6a8d2084137e Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 13:51:25 +0100 Subject: [PATCH 38/40] Fixed some tests --- tests/integrations/stdlib/test_httplib.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index c5eade4a03..f6ace42ba2 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -1,6 +1,4 @@ -import platform import random -import sys import pytest @@ -67,7 +65,7 @@ def before_breadcrumb(crumb, hint): events = capture_events() url = "http://localhost:{}/some/random/url".format(PORT) - response = urlopen(url) + urlopen(url) capture_message("Testing!") From 62e5719b5b222d831f769bc1945e447b48e1a466 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 14:45:16 +0100 Subject: [PATCH 39/40] Use get_current_span, because it also returns the transaction if no span yet. --- sentry_sdk/tracing_utils_py2.py | 4 ++-- sentry_sdk/tracing_utils_py3.py | 6 +++--- tests/tracing/test_decorator_py2.py | 4 ++-- tests/tracing/test_decorator_py3.py | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/tracing_utils_py2.py b/sentry_sdk/tracing_utils_py2.py index 5d50fdeaaa..738ced24d1 100644 --- a/sentry_sdk/tracing_utils_py2.py +++ b/sentry_sdk/tracing_utils_py2.py @@ -1,7 +1,7 @@ from functools import wraps import sentry_sdk -from sentry_sdk import get_current_span_or_transaction +from sentry_sdk import get_current_span from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP from sentry_sdk.utils import logger, qualname_from_function @@ -26,7 +26,7 @@ def start_child_span_decorator(func): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/sentry_sdk/tracing_utils_py3.py b/sentry_sdk/tracing_utils_py3.py index be07dc57b1..f126d979d3 100644 --- a/sentry_sdk/tracing_utils_py3.py +++ b/sentry_sdk/tracing_utils_py3.py @@ -2,7 +2,7 @@ from functools import wraps import sentry_sdk -from sentry_sdk import get_current_span_or_transaction +from sentry_sdk import get_current_span from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.consts import OP from sentry_sdk.utils import logger, qualname_from_function @@ -30,7 +30,7 @@ def start_child_span_decorator(func): async def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( @@ -53,7 +53,7 @@ async def func_with_tracing(*args, **kwargs): def func_with_tracing(*args, **kwargs): # type: (*Any, **Any) -> Any - span_or_trx = get_current_span_or_transaction(sentry_sdk.Hub.current) + span_or_trx = get_current_span(sentry_sdk.Hub.current) if span_or_trx is None: logger.warning( diff --git a/tests/tracing/test_decorator_py2.py b/tests/tracing/test_decorator_py2.py index 6dbf4b8768..e0e60f90e7 100644 --- a/tests/tracing/test_decorator_py2.py +++ b/tests/tracing/test_decorator_py2.py @@ -16,7 +16,7 @@ def test_trace_decorator_py2(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py2.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_current_span", return_value=fake_transaction, ): result = my_example_function() @@ -34,7 +34,7 @@ def test_trace_decorator_py2_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py2.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py2.get_current_span", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: diff --git a/tests/tracing/test_decorator_py3.py b/tests/tracing/test_decorator_py3.py index 798883f282..2c4bf779f2 100644 --- a/tests/tracing/test_decorator_py3.py +++ b/tests/tracing/test_decorator_py3.py @@ -25,7 +25,7 @@ def test_trace_decorator_sync_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span", return_value=fake_transaction, ): result = my_example_function() @@ -43,7 +43,7 @@ def test_trace_decorator_sync_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: @@ -66,7 +66,7 @@ async def test_trace_decorator_async_py3(): fake_transaction.start_child = fake_start_child with mock.patch( - "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span", return_value=fake_transaction, ): result = await my_async_example_function() @@ -85,7 +85,7 @@ async def test_trace_decorator_async_py3_no_trx(): fake_transaction = None with mock.patch( - "sentry_sdk.tracing_utils_py3.get_current_span_or_transaction", + "sentry_sdk.tracing_utils_py3.get_current_span", return_value=fake_transaction, ): with mock.patch.object(logger, "warning", mock.Mock()) as fake_warning: From 524fb883f38a7fb0d0f21b286ae504401ce51641 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 15 Mar 2023 15:05:04 +0100 Subject: [PATCH 40/40] Linting --- sentry_sdk/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 6500239d29..2827d17a0e 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -241,4 +241,3 @@ def get_current_span(hub=None): current_span = hub.scope.span return current_span -