From f2a4a3aedf53126e747149f05bf7e5eef2e6f761 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 29 Nov 2023 16:08:06 +0100 Subject: [PATCH 01/25] Moved get_integration from Hub to Client --- sentry_sdk/client.py | 19 +++++++++++++++++++ sentry_sdk/hub.py | 11 +---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 8aad751470..846fc0a7b6 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -43,7 +43,10 @@ from typing import Dict from typing import Optional from typing import Sequence + from typing import Type + from typing import Union + from sentry_sdk.integrations import Integration from sentry_sdk.scope import Scope from sentry_sdk._types import Event, Hint from sentry_sdk.session import Session @@ -653,6 +656,22 @@ def capture_session( else: self.session_flusher.add_session(session) + def get_integration( + self, name_or_class # type: Union[str, Type[Integration]] + ): + # type: (...) -> Any + """Returns the integration for this client by name or class. + If the client does not have that integration then `None` is returned. + """ + if isinstance(name_or_class, str): + integration_name = name_or_class + elif name_or_class.identifier is not None: + integration_name = name_or_class.identifier + else: + raise ValueError("Integration has no name") + + return self.integrations.get(integration_name) + def close( self, timeout=None, # type: Optional[float] diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 2525dc56f1..5777704bb0 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -294,18 +294,9 @@ def get_integration( If the return value is not `None` the hub is guaranteed to have a client attached. """ - if isinstance(name_or_class, str): - integration_name = name_or_class - elif name_or_class.identifier is not None: - integration_name = name_or_class.identifier - else: - raise ValueError("Integration has no name") - client = self.client if client is not None: - rv = client.integrations.get(integration_name) - if rv is not None: - return rv + return client.get_integration(name_or_class) @property def client(self): From 4869365e344711032b8b5bf9cf369fc9dcf47478 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 29 Nov 2023 16:22:03 +0100 Subject: [PATCH 02/25] Moved add_breadcrumb from Hub to Scope --- sentry_sdk/hub.py | 29 ++++------------------------- sentry_sdk/scope.py | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 5777704bb0..2a4ad8041f 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -3,7 +3,7 @@ from contextlib import contextmanager -from sentry_sdk._compat import datetime_utcnow, with_metaclass +from sentry_sdk._compat import with_metaclass from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.scope import Scope from sentry_sdk.client import Client @@ -421,31 +421,10 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): logger.info("Dropped breadcrumb because no client bound") return - crumb = dict(crumb or ()) # type: Breadcrumb - crumb.update(kwargs) - if not crumb: - return - - hint = dict(hint or ()) # type: Hint - - if crumb.get("timestamp") is None: - crumb["timestamp"] = datetime_utcnow() - if crumb.get("type") is None: - crumb["type"] = "default" - - if client.options["before_breadcrumb"] is not None: - new_crumb = client.options["before_breadcrumb"](crumb, hint) - else: - new_crumb = crumb - - if new_crumb is not None: - scope._breadcrumbs.append(new_crumb) - else: - logger.info("before breadcrumb dropped breadcrumb (%s)", crumb) + kwargs["before_breadcrumb"] = client.options.get("before_breadcrumb") + kwargs["max_breadcrumbs"] = client.options.get("max_breadcrumbs") - max_breadcrumbs = client.options["max_breadcrumbs"] # type: int - while len(scope._breadcrumbs) > max_breadcrumbs: - scope._breadcrumbs.popleft() + scope.add_breadcrumb(crumb, hint, **kwargs) def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): # type: (Optional[Span], str, Any) -> Span diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 5096eccce0..f3e5eb1f22 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -5,6 +5,7 @@ import uuid from sentry_sdk.attachments import Attachment +from sentry_sdk._compat import datetime_utcnow from sentry_sdk._functools import wraps from sentry_sdk.tracing_utils import ( Baggage, @@ -20,7 +21,7 @@ from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.utils import logger, capture_internal_exceptions -from sentry_sdk.consts import FALSE_VALUES +from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES if TYPE_CHECKING: @@ -36,6 +37,7 @@ from sentry_sdk._types import ( Breadcrumb, + BreadcrumbHint, Event, EventProcessor, ErrorProcessor, @@ -517,6 +519,44 @@ def add_attachment( ) ) + def add_breadcrumb(self, crumb=None, hint=None, **kwargs): + # type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None + """ + Adds a breadcrumb. + + :param crumb: Dictionary with the data as the sentry v7/v8 protocol expects. + + :param hint: An optional value that can be used by `before_breadcrumb` + to customize the breadcrumbs that are emitted. + """ + before_breadcrumb = kwargs.pop("before_breadcrumb") + max_breadcrumbs = kwargs.pop("max_breadcrumbs", DEFAULT_MAX_BREADCRUMBS) + + crumb = dict(crumb or ()) # type: Breadcrumb + crumb.update(kwargs) + if not crumb: + return + + hint = dict(hint or ()) # type: Hint + + if crumb.get("timestamp") is None: + crumb["timestamp"] = datetime_utcnow() + if crumb.get("type") is None: + crumb["type"] = "default" + + if before_breadcrumb is not None: + new_crumb = before_breadcrumb(crumb, hint) + else: + new_crumb = crumb + + if new_crumb is not None: + self._breadcrumbs.append(new_crumb) + else: + logger.info("before breadcrumb dropped breadcrumb (%s)", crumb) + + while len(self._breadcrumbs) > max_breadcrumbs: + self._breadcrumbs.popleft() + def add_event_processor( self, func # type: EventProcessor ): From e5f9e9d829405884f108eb07ff08a338e31230f3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 29 Nov 2023 16:46:51 +0100 Subject: [PATCH 03/25] Moved session functions from Hub to Scope --- sentry_sdk/hub.py | 23 +++++--------------- sentry_sdk/scope.py | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 2a4ad8041f..380686c013 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -15,7 +15,6 @@ BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME, ) -from sentry_sdk.session import Session from sentry_sdk.tracing_utils import ( has_tracing_enabled, normalize_incoming_data, @@ -682,12 +681,9 @@ def start_session( ): # type: (...) -> None """Starts a new session.""" - self.end_session() client, scope = self._stack[-1] - scope._session = Session( - release=client.options["release"] if client else None, - environment=client.options["environment"] if client else None, - user=scope._user, + scope.start_session( + client=client, session_mode=session_mode, ) @@ -695,13 +691,7 @@ def end_session(self): # type: (...) -> None """Ends the current session if there is one.""" client, scope = self._stack[-1] - session = scope._session - self.scope._session = None - - if session is not None: - session.close() - if client is not None: - client.capture_session(session) + scope.end_session(client=client) def stop_auto_session_tracking(self): # type: (...) -> None @@ -710,9 +700,8 @@ def stop_auto_session_tracking(self): This temporarily session tracking for the current scope when called. To resume session tracking call `resume_auto_session_tracking`. """ - self.end_session() client, scope = self._stack[-1] - scope._force_auto_session_tracking = False + scope.stop_auto_session_tracking(client=client) def resume_auto_session_tracking(self): # type: (...) -> None @@ -720,8 +709,8 @@ def resume_auto_session_tracking(self): disabled earlier. This requires that generally automatic session tracking is enabled. """ - client, scope = self._stack[-1] - scope._force_auto_session_tracking = None + scope = self._stack[-1][1] + scope.resume_auto_session_tracking() def flush( self, diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index f3e5eb1f22..422a9cf633 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -557,6 +557,59 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): while len(self._breadcrumbs) > max_breadcrumbs: self._breadcrumbs.popleft() + def start_session( + self, + *args, + **kwargs, + ): + # type: (*Any, **Any) -> None + """Starts a new session.""" + client = kwargs.pop("client", None) + session_mode = kwargs.pop("session_mode", "application") + + self.end_session(client=client) + + self._session = Session( + release=client.options["release"] if client else None, + environment=client.options["environment"] if client else None, + user=self._user, + session_mode=session_mode, + ) + + def end_session(self, *args, **kwargs): + # type: (*Any, **Any) -> None + """Ends the current session if there is one.""" + client = kwargs.pop("client", None) + + session = self._session + self._session = None + + if session is not None: + session.close() + if client is not None: + client.capture_session(session) + + def stop_auto_session_tracking(self, *args, **kwargs): + # type: (*Any, **Any) -> None + """Stops automatic session tracking. + + This temporarily session tracking for the current scope when called. + To resume session tracking call `resume_auto_session_tracking`. + """ + client = kwargs.pop("client", None) + + self.end_session(client=client) + + self._force_auto_session_tracking = False + + def resume_auto_session_tracking(self): + # type: (...) -> None + """Resumes automatic session tracking for the current scope if + disabled earlier. This requires that generally automatic session + tracking is enabled. + """ + self._force_auto_session_tracking = None + def add_event_processor( self, func # type: EventProcessor ): From a338099466ddd9ea9a8146f7918888a1e9a66381 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 29 Nov 2023 16:55:26 +0100 Subject: [PATCH 04/25] Fixed import --- sentry_sdk/scope.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 422a9cf633..d1e4c53b46 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -6,7 +6,9 @@ from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow +from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES from sentry_sdk._functools import wraps +from sentry_sdk.session import Session from sentry_sdk.tracing_utils import ( Baggage, extract_sentrytrace_data, @@ -21,9 +23,6 @@ from sentry_sdk._types import TYPE_CHECKING from sentry_sdk.utils import logger, capture_internal_exceptions -from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES - - if TYPE_CHECKING: from typing import Any from typing import Dict @@ -48,7 +47,6 @@ from sentry_sdk.profiler import Profile from sentry_sdk.tracing import Span - from sentry_sdk.session import Session F = TypeVar("F", bound=Callable[..., Any]) T = TypeVar("T") From d900a1bda9a800e4bd70cfb7b8f9572717bfe2f6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 29 Nov 2023 17:04:34 +0100 Subject: [PATCH 05/25] Fixed Python 2.7 syntax --- sentry_sdk/scope.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index d1e4c53b46..0fc836774a 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -555,11 +555,7 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): while len(self._breadcrumbs) > max_breadcrumbs: self._breadcrumbs.popleft() - def start_session( - self, - *args, - **kwargs, - ): + def start_session(self, *args, **kwargs): # type: (*Any, **Any) -> None """Starts a new session.""" client = kwargs.pop("client", None) From fe77d0499ec10077875e60ba112c25db8b7d2459 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 09:12:24 +0100 Subject: [PATCH 06/25] Give the client to the scope function. Want to establish a pattern. --- sentry_sdk/hub.py | 3 +-- sentry_sdk/scope.py | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 380686c013..032ccd09e7 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -420,8 +420,7 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): logger.info("Dropped breadcrumb because no client bound") return - kwargs["before_breadcrumb"] = client.options.get("before_breadcrumb") - kwargs["max_breadcrumbs"] = client.options.get("max_breadcrumbs") + kwargs["client"] = client scope.add_breadcrumb(crumb, hint, **kwargs) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 0fc836774a..8e9724b4c5 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -6,7 +6,7 @@ from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow -from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES +from sentry_sdk.consts import FALSE_VALUES from sentry_sdk._functools import wraps from sentry_sdk.session import Session from sentry_sdk.tracing_utils import ( @@ -527,8 +527,12 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): :param hint: An optional value that can be used by `before_breadcrumb` to customize the breadcrumbs that are emitted. """ - before_breadcrumb = kwargs.pop("before_breadcrumb") - max_breadcrumbs = kwargs.pop("max_breadcrumbs", DEFAULT_MAX_BREADCRUMBS) + client = kwargs.pop("client", None) + if client is None: + return + + before_breadcrumb = client.options.get("before_breadcrumb") + max_breadcrumbs = client.options.get("max_breadcrumbs") crumb = dict(crumb or ()) # type: Breadcrumb crumb.update(kwargs) From b15613a22e25e8d183d8517504fe6ac625a57f60 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 11:00:36 +0100 Subject: [PATCH 07/25] Moved capture_* functions from Hub to Client --- sentry_sdk/client.py | 91 +++++++++++++++++++++++++++++++++++++++++++- sentry_sdk/hub.py | 82 ++++++++++++++++++--------------------- 2 files changed, 128 insertions(+), 45 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 846fc0a7b6..3e81454639 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -1,5 +1,7 @@ from importlib import import_module +import copy import os +import sys import uuid import random import socket @@ -9,6 +11,8 @@ capture_internal_exceptions, current_stacktrace, disable_capture_event, + event_from_exception, + exc_info_from_error, format_timestamp, get_sdk_name, get_type_name, @@ -48,7 +52,7 @@ from sentry_sdk.integrations import Integration from sentry_sdk.scope import Scope - from sentry_sdk._types import Event, Hint + from sentry_sdk._types import Event, ExcInfo, Hint from sentry_sdk.session import Session @@ -143,6 +147,24 @@ def _get_options(*args, **kwargs): return rv +def _update_scope(base, scope_change, scope_kwargs): + # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope + if scope_change and scope_kwargs: + raise TypeError("cannot provide scope and kwargs") + if scope_change is not None: + final_scope = copy.copy(base) + if callable(scope_change): + scope_change(final_scope) + else: + final_scope.update_from_scope(scope_change) + elif scope_kwargs: + final_scope = copy.copy(base) + final_scope.update_from_kwargs(**scope_kwargs) + else: + final_scope = base + return final_scope + + try: # Python 3.6+ module_not_found_error = ModuleNotFoundError @@ -544,6 +566,7 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Scope] + **scope_args # type: Any ): # type: (...) -> Optional[str] """Captures an event. @@ -555,11 +578,17 @@ def capture_event( :param scope: An optional scope to use for determining whether this event should be captured. + :param scope_args: For supported `**scope_args` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help. """ if disable_capture_event.get(False): return None + top_scope = scope_args.pop("top_scope") + scope = _update_scope(top_scope, scope, scope_args) + if hint is None: hint = {} event_id = event.get("event_id") @@ -647,6 +676,66 @@ def capture_event( return event_id + def capture_message(self, message, level=None, scope=None, **scope_args): + # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] + """ + Captures a message. + + :param message: The string to send as the message. + + :param level: If no level is provided, the default level is `info`. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + + :param scope_args: For supported `**scope_args` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). + """ + if level is None: + level = "info" + + return self.capture_event( + {"message": message, "level": level}, scope=scope, **scope_args + ) + + def capture_exception(self, error=None, scope=None, **scope_args): + # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + """Captures an exception. + + :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. + + :param scope_args: For supported `**scope_args` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). + """ + if error is not None: + exc_info = exc_info_from_error(error) + else: + exc_info = sys.exc_info() + + event, hint = event_from_exception(exc_info, client_options=self.options) + + try: + return self.capture_event(event, hint=hint, scope=scope, **scope_args) + except Exception: + self._capture_internal_exception(sys.exc_info()) + + return None + + def _capture_internal_exception( + self, exc_info # type: Any + ): + # type: (...) -> Any + """ + Capture an exception that is likely caused by a bug in the SDK + itself. + + These exceptions do not end up in Sentry and are just logged instead. + """ + logger.error("Internal error in sentry_sdk", exc_info=exc_info) + def capture_session( self, session # type: Session ): diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 032ccd09e7..5c23495371 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -21,8 +21,6 @@ ) from sentry_sdk.utils import ( - exc_info_from_error, - event_from_exception, logger, ContextVar, ) @@ -65,24 +63,6 @@ def overload(x): _local = ContextVar("sentry_current_hub") -def _update_scope(base, scope_change, scope_kwargs): - # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope - if scope_change and scope_kwargs: - raise TypeError("cannot provide scope and kwargs") - if scope_change is not None: - final_scope = copy.copy(base) - if callable(scope_change): - scope_change(final_scope) - else: - final_scope.update_from_scope(scope_change) - elif scope_kwargs: - final_scope = copy.copy(base) - final_scope.update_from_kwargs(**scope_kwargs) - else: - final_scope = base - return final_scope - - def _should_send_default_pii(): # type: () -> bool client = Hub.current.client @@ -333,20 +313,26 @@ def capture_event(self, event, hint=None, scope=None, **scope_args): :py:meth:`sentry_sdk.Scope.update_from_kwargs`. """ client, top_scope = self._stack[-1] - scope = _update_scope(top_scope, scope, scope_args) - if client is not None: - is_transaction = event.get("type") == "transaction" - rv = client.capture_event(event, hint, scope) - if rv is not None and not is_transaction: - self._last_event_id = rv - return rv - return None + if client is None: + return None + + scope_args["top_scope"] = top_scope + + last_event_id = client.capture_event(event, hint, scope, **scope_args) + + is_transaction = event.get("type") == "transaction" + if last_event_id is not None and not is_transaction: + self._last_event_id = last_event_id + + return last_event_id def capture_message(self, message, level=None, scope=None, **scope_args): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. + Alias of :py:meth:`sentry_sdk.Client.capture_message`. + :param message: The string to send as the message. :param level: If no level is provided, the default level is `info`. @@ -358,18 +344,27 @@ def capture_message(self, message, level=None, scope=None, **scope_args): :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ - if self.client is None: + client, top_scope = self._stack[-1] + if client is None: return None - if level is None: - level = "info" - return self.capture_event( - {"message": message, "level": level}, scope=scope, **scope_args + + scope_args["top_scope"] = top_scope + + last_event_id = client.capture_message( + message, level=level, scope=scope, **scope_args ) + if last_event_id is not None: + self._last_event_id = last_event_id + + return last_event_id + def capture_exception(self, error=None, scope=None, **scope_args): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. + Alias of :py:meth:`sentry_sdk.Client.capture_exception`. + :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. :param scope_args: For supported `**scope_args` see @@ -377,21 +372,18 @@ def capture_exception(self, error=None, scope=None, **scope_args): :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ - client = self.client + client, top_scope = self._stack[-1] if client is None: return None - if error is not None: - exc_info = exc_info_from_error(error) - else: - exc_info = sys.exc_info() - event, hint = event_from_exception(exc_info, client_options=client.options) - try: - return self.capture_event(event, hint=hint, scope=scope, **scope_args) - except Exception: - self._capture_internal_exception(sys.exc_info()) + scope_args["top_scope"] = top_scope - return None + last_event_id = client.capture_exception(error, scope=scope, **scope_args) + + if last_event_id is not None: + self._last_event_id = last_event_id + + return last_event_id def _capture_internal_exception( self, exc_info # type: Any @@ -401,6 +393,8 @@ def _capture_internal_exception( Capture an exception that is likely caused by a bug in the SDK itself. + Duplicated in :py:meth:`sentry_sdk.Client._capture_internal_exception`. + These exceptions do not end up in Sentry and are just logged instead. """ logger.error("Internal error in sentry_sdk", exc_info=exc_info) From 8331bd08ad6416344286235c19d1c076602b982f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 11:01:47 +0100 Subject: [PATCH 08/25] Renamed scope_args to scope_kwargs --- sentry_sdk/api.py | 12 ++++++------ sentry_sdk/client.py | 20 ++++++++++---------- sentry_sdk/hub.py | 24 ++++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index f0c6a87432..741a418f0b 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -82,10 +82,10 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Any] - **scope_args # type: Any + **scope_wkargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_event(event, hint, scope=scope, **scope_args) + return Hub.current.capture_event(event, hint, scope=scope, **scope_wkargs) @hubmethod @@ -93,20 +93,20 @@ def capture_message( message, # type: str level=None, # type: Optional[str] scope=None, # type: Optional[Any] - **scope_args # type: Any + **scope_wkargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_message(message, level, scope=scope, **scope_args) + return Hub.current.capture_message(message, level, scope=scope, **scope_wkargs) @hubmethod def capture_exception( error=None, # type: Optional[Union[BaseException, ExcInfo]] scope=None, # type: Optional[Any] - **scope_args # type: Any + **scope_wkargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_exception(error, scope=scope, **scope_args) + return Hub.current.capture_exception(error, scope=scope, **scope_wkargs) @hubmethod diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 3e81454639..c307b40651 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -566,7 +566,7 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Scope] - **scope_args # type: Any + **scope_wkargs # type: Any ): # type: (...) -> Optional[str] """Captures an event. @@ -578,7 +578,7 @@ def capture_event( :param scope: An optional scope to use for determining whether this event should be captured. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help. @@ -586,8 +586,8 @@ def capture_event( if disable_capture_event.get(False): return None - top_scope = scope_args.pop("top_scope") - scope = _update_scope(top_scope, scope, scope_args) + top_scope = scope_wkargs.pop("top_scope") + scope = _update_scope(top_scope, scope, scope_wkargs) if hint is None: hint = {} @@ -676,7 +676,7 @@ def capture_event( return event_id - def capture_message(self, message, level=None, scope=None, **scope_args): + def capture_message(self, message, level=None, scope=None, **scope_wkargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. @@ -687,7 +687,7 @@ def capture_message(self, message, level=None, scope=None, **scope_args): :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -696,16 +696,16 @@ def capture_message(self, message, level=None, scope=None, **scope_args): level = "info" return self.capture_event( - {"message": message, "level": level}, scope=scope, **scope_args + {"message": message, "level": level}, scope=scope, **scope_wkargs ) - def capture_exception(self, error=None, scope=None, **scope_args): + def capture_exception(self, error=None, scope=None, **scope_wkargs): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -718,7 +718,7 @@ def capture_exception(self, error=None, scope=None, **scope_args): event, hint = event_from_exception(exc_info, client_options=self.options) try: - return self.capture_event(event, hint=hint, scope=scope, **scope_args) + return self.capture_event(event, hint=hint, scope=scope, **scope_wkargs) except Exception: self._capture_internal_exception(sys.exc_info()) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 5c23495371..fe64d7c959 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -302,23 +302,23 @@ def bind_client( top = self._stack[-1] self._stack[-1] = (new, top[1]) - def capture_event(self, event, hint=None, scope=None, **scope_args): + def capture_event(self, event, hint=None, scope=None, **scope_wkargs): # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] """ Captures an event. Alias of :py:meth:`sentry_sdk.Client.capture_event`. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. """ client, top_scope = self._stack[-1] if client is None: return None - scope_args["top_scope"] = top_scope + scope_wkargs["top_scope"] = top_scope - last_event_id = client.capture_event(event, hint, scope, **scope_args) + last_event_id = client.capture_event(event, hint, scope, **scope_wkargs) is_transaction = event.get("type") == "transaction" if last_event_id is not None and not is_transaction: @@ -326,7 +326,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_args): return last_event_id - def capture_message(self, message, level=None, scope=None, **scope_args): + def capture_message(self, message, level=None, scope=None, **scope_wkargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. @@ -339,7 +339,7 @@ def capture_message(self, message, level=None, scope=None, **scope_args): :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -348,10 +348,10 @@ def capture_message(self, message, level=None, scope=None, **scope_args): if client is None: return None - scope_args["top_scope"] = top_scope + scope_wkargs["top_scope"] = top_scope last_event_id = client.capture_message( - message, level=level, scope=scope, **scope_args + message, level=level, scope=scope, **scope_wkargs ) if last_event_id is not None: @@ -359,7 +359,7 @@ def capture_message(self, message, level=None, scope=None, **scope_args): return last_event_id - def capture_exception(self, error=None, scope=None, **scope_args): + def capture_exception(self, error=None, scope=None, **scope_wkargs): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. @@ -367,7 +367,7 @@ def capture_exception(self, error=None, scope=None, **scope_args): :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - :param scope_args: For supported `**scope_args` see + :param scope_wkargs: For supported `**scope_wkargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -376,9 +376,9 @@ def capture_exception(self, error=None, scope=None, **scope_args): if client is None: return None - scope_args["top_scope"] = top_scope + scope_wkargs["top_scope"] = top_scope - last_event_id = client.capture_exception(error, scope=scope, **scope_args) + last_event_id = client.capture_exception(error, scope=scope, **scope_wkargs) if last_event_id is not None: self._last_event_id = last_event_id From ce1759f5a8c48a6537f3bd7dad1cdee3a0e661f7 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 11:10:04 +0100 Subject: [PATCH 09/25] oops --- sentry_sdk/api.py | 12 ++++++------ sentry_sdk/client.py | 20 ++++++++++---------- sentry_sdk/hub.py | 24 ++++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 741a418f0b..ffa525ca66 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -82,10 +82,10 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Any] - **scope_wkargs # type: Any + **scope_kwargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_event(event, hint, scope=scope, **scope_wkargs) + return Hub.current.capture_event(event, hint, scope=scope, **scope_kwargs) @hubmethod @@ -93,20 +93,20 @@ def capture_message( message, # type: str level=None, # type: Optional[str] scope=None, # type: Optional[Any] - **scope_wkargs # type: Any + **scope_kwargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_message(message, level, scope=scope, **scope_wkargs) + return Hub.current.capture_message(message, level, scope=scope, **scope_kwargs) @hubmethod def capture_exception( error=None, # type: Optional[Union[BaseException, ExcInfo]] scope=None, # type: Optional[Any] - **scope_wkargs # type: Any + **scope_kwargs # type: Any ): # type: (...) -> Optional[str] - return Hub.current.capture_exception(error, scope=scope, **scope_wkargs) + return Hub.current.capture_exception(error, scope=scope, **scope_kwargs) @hubmethod diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index c307b40651..b57007fd87 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -566,7 +566,7 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Scope] - **scope_wkargs # type: Any + **scope_kwargs # type: Any ): # type: (...) -> Optional[str] """Captures an event. @@ -578,7 +578,7 @@ def capture_event( :param scope: An optional scope to use for determining whether this event should be captured. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help. @@ -586,8 +586,8 @@ def capture_event( if disable_capture_event.get(False): return None - top_scope = scope_wkargs.pop("top_scope") - scope = _update_scope(top_scope, scope, scope_wkargs) + top_scope = scope_kwargs.pop("top_scope") + scope = _update_scope(top_scope, scope, scope_kwargs) if hint is None: hint = {} @@ -676,7 +676,7 @@ def capture_event( return event_id - def capture_message(self, message, level=None, scope=None, **scope_wkargs): + def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. @@ -687,7 +687,7 @@ def capture_message(self, message, level=None, scope=None, **scope_wkargs): :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -696,16 +696,16 @@ def capture_message(self, message, level=None, scope=None, **scope_wkargs): level = "info" return self.capture_event( - {"message": message, "level": level}, scope=scope, **scope_wkargs + {"message": message, "level": level}, scope=scope, **scope_kwargs ) - def capture_exception(self, error=None, scope=None, **scope_wkargs): + def capture_exception(self, error=None, scope=None, **scope_kwargs): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -718,7 +718,7 @@ def capture_exception(self, error=None, scope=None, **scope_wkargs): event, hint = event_from_exception(exc_info, client_options=self.options) try: - return self.capture_event(event, hint=hint, scope=scope, **scope_wkargs) + return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) except Exception: self._capture_internal_exception(sys.exc_info()) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index fe64d7c959..41c062c918 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -302,23 +302,23 @@ def bind_client( top = self._stack[-1] self._stack[-1] = (new, top[1]) - def capture_event(self, event, hint=None, scope=None, **scope_wkargs): + def capture_event(self, event, hint=None, scope=None, **scope_kwargs): # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] """ Captures an event. Alias of :py:meth:`sentry_sdk.Client.capture_event`. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. """ client, top_scope = self._stack[-1] if client is None: return None - scope_wkargs["top_scope"] = top_scope + scope_kwargs["top_scope"] = top_scope - last_event_id = client.capture_event(event, hint, scope, **scope_wkargs) + last_event_id = client.capture_event(event, hint, scope, **scope_kwargs) is_transaction = event.get("type") == "transaction" if last_event_id is not None and not is_transaction: @@ -326,7 +326,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_wkargs): return last_event_id - def capture_message(self, message, level=None, scope=None, **scope_wkargs): + def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. @@ -339,7 +339,7 @@ def capture_message(self, message, level=None, scope=None, **scope_wkargs): :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -348,10 +348,10 @@ def capture_message(self, message, level=None, scope=None, **scope_wkargs): if client is None: return None - scope_wkargs["top_scope"] = top_scope + scope_kwargs["top_scope"] = top_scope last_event_id = client.capture_message( - message, level=level, scope=scope, **scope_wkargs + message, level=level, scope=scope, **scope_kwargs ) if last_event_id is not None: @@ -359,7 +359,7 @@ def capture_message(self, message, level=None, scope=None, **scope_wkargs): return last_event_id - def capture_exception(self, error=None, scope=None, **scope_wkargs): + def capture_exception(self, error=None, scope=None, **scope_kwargs): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. @@ -367,7 +367,7 @@ def capture_exception(self, error=None, scope=None, **scope_wkargs): :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - :param scope_wkargs: For supported `**scope_wkargs` see + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). @@ -376,9 +376,9 @@ def capture_exception(self, error=None, scope=None, **scope_wkargs): if client is None: return None - scope_wkargs["top_scope"] = top_scope + scope_kwargs["top_scope"] = top_scope - last_event_id = client.capture_exception(error, scope=scope, **scope_wkargs) + last_event_id = client.capture_exception(error, scope=scope, **scope_kwargs) if last_event_id is not None: self._last_event_id = last_event_id From 9a08b6efc14efe530579d0486b492cefc7b07f03 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 11:27:21 +0100 Subject: [PATCH 10/25] preserve old behavior when called from client directly (not from hub) --- sentry_sdk/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index b57007fd87..a9f87e0a01 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -586,8 +586,9 @@ def capture_event( if disable_capture_event.get(False): return None - top_scope = scope_kwargs.pop("top_scope") - scope = _update_scope(top_scope, scope, scope_kwargs) + if scope_kwargs is not None and "top_scope" in scope_kwargs: + top_scope = scope_kwargs.pop("top_scope") + scope = _update_scope(top_scope, scope, scope_kwargs) if hint is None: hint = {} From f9b8d5aea989b14cb0d97ee4f6d2755c770e9af8 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 1 Dec 2023 12:23:51 +0100 Subject: [PATCH 11/25] update test --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index 5a7a5cff16..a99bb05765 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -365,7 +365,7 @@ def raise_it(exc_info): reraise(*exc_info) sentry_init(ignore_errors=[ZeroDivisionError], transport=_TestTransport()) - Hub.current._capture_internal_exception = raise_it + Hub.current.client._capture_internal_exception = raise_it def e(exc): try: From bb3250a564b47306a6a9c96c6b85a427d74e5e2f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Dec 2023 15:19:06 +0100 Subject: [PATCH 12/25] _update_scope belongs into the scope not the client --- sentry_sdk/client.py | 20 +------------------- sentry_sdk/scope.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index a9f87e0a01..1f3545cc0f 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -1,5 +1,4 @@ from importlib import import_module -import copy import os import sys import uuid @@ -7,6 +6,7 @@ import socket from sentry_sdk._compat import datetime_utcnow, string_types, text_type, iteritems +from sentry_sdk.scope import _update_scope from sentry_sdk.utils import ( capture_internal_exceptions, current_stacktrace, @@ -147,24 +147,6 @@ def _get_options(*args, **kwargs): return rv -def _update_scope(base, scope_change, scope_kwargs): - # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope - if scope_change and scope_kwargs: - raise TypeError("cannot provide scope and kwargs") - if scope_change is not None: - final_scope = copy.copy(base) - if callable(scope_change): - scope_change(final_scope) - else: - final_scope.update_from_scope(scope_change) - elif scope_kwargs: - final_scope = copy.copy(base) - final_scope.update_from_kwargs(**scope_kwargs) - else: - final_scope = base - return final_scope - - try: # Python 3.6+ module_not_found_error = ModuleNotFoundError diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 8e9724b4c5..5a0a69af39 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -81,6 +81,24 @@ def wrapper(self, *args, **kwargs): return wrapper # type: ignore +def _update_scope(base, scope_change, scope_kwargs): + # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope + if scope_change and scope_kwargs: + raise TypeError("cannot provide scope and kwargs") + if scope_change is not None: + final_scope = copy(base) + if callable(scope_change): + scope_change(final_scope) + else: + final_scope.update_from_scope(scope_change) + elif scope_kwargs: + final_scope = copy(base) + final_scope.update_from_kwargs(**scope_kwargs) + else: + final_scope = base + return final_scope + + class Scope(object): """The scope holds extra information that should be sent with all events that belong to it. From 3a22dd686a46dcf2f43a730c2daa7e0c36dfd9ff Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 11 Dec 2023 16:11:37 +0100 Subject: [PATCH 13/25] Made top_scope separate parameter --- sentry_sdk/client.py | 48 +++++++++++++++++++++++++++++++++----------- sentry_sdk/hub.py | 16 +++++++-------- sentry_sdk/scope.py | 4 ++++ 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 1f3545cc0f..91bfc459be 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -548,6 +548,7 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Scope] + top_scope=None, # type: Optional[Scope] **scope_kwargs # type: Any ): # type: (...) -> Optional[str] @@ -557,8 +558,9 @@ def capture_event( :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. - :param scope: An optional scope to use for determining whether this event - should be captured. + :param scope: An optional scope to use for determining whether this event should be captured. + + :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. @@ -568,8 +570,7 @@ def capture_event( if disable_capture_event.get(False): return None - if scope_kwargs is not None and "top_scope" in scope_kwargs: - top_scope = scope_kwargs.pop("top_scope") + if scope_kwargs is not None and top_scope is not None: scope = _update_scope(top_scope, scope, scope_kwargs) if hint is None: @@ -659,8 +660,15 @@ def capture_event( return event_id - def capture_message(self, message, level=None, scope=None, **scope_kwargs): - # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] + def capture_message( + self, + message, # type: str + level=None, # type: Optional[str] + scope=None, # type: Optional[Scope] + top_scope=None, # type: Optional[Scope] + **scope_kwargs # type: Any + ): + # type: (...) -> Optional[str] """ Captures a message. @@ -670,6 +678,8 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. @@ -679,17 +689,29 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): level = "info" return self.capture_event( - {"message": message, "level": level}, scope=scope, **scope_kwargs + {"message": message, "level": level}, + scope=scope, + top_scope=top_scope, + **scope_kwargs ) - def capture_exception(self, error=None, scope=None, **scope_kwargs): - # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + def capture_exception( + self, + error=None, # type: Optional[Union[BaseException, ExcInfo]] + scope=None, # type: Optional[Scope] + top_scope=None, # type: Optional[Scope] + **scope_kwargs # type: Any + ): + # type: (...) -> Optional[str] """Captures an exception. :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + + :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. + + :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ @@ -701,7 +723,9 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): event, hint = event_from_exception(exc_info, client_options=self.options) try: - return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) + return self.capture_event( + event, hint=hint, scope=scope, top_scope=top_scope, **scope_kwargs + ) except Exception: self._capture_internal_exception(sys.exc_info()) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 41c062c918..abdc523fb2 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -316,9 +316,9 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): if client is None: return None - scope_kwargs["top_scope"] = top_scope - - last_event_id = client.capture_event(event, hint, scope, **scope_kwargs) + last_event_id = client.capture_event( + event, hint, scope=scope, top_scope=top_scope, **scope_kwargs + ) is_transaction = event.get("type") == "transaction" if last_event_id is not None and not is_transaction: @@ -348,10 +348,8 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): if client is None: return None - scope_kwargs["top_scope"] = top_scope - last_event_id = client.capture_message( - message, level=level, scope=scope, **scope_kwargs + message, level=level, scope=scope, top_scope=top_scope, **scope_kwargs ) if last_event_id is not None: @@ -376,9 +374,9 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): if client is None: return None - scope_kwargs["top_scope"] = top_scope - - last_event_id = client.capture_exception(error, scope=scope, **scope_kwargs) + last_event_id = client.capture_exception( + error, scope=scope, top_scope=top_scope, **scope_kwargs + ) if last_event_id is not None: self._last_event_id = last_event_id diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 5a0a69af39..816153d7a9 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -85,17 +85,21 @@ def _update_scope(base, scope_change, scope_kwargs): # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope if scope_change and scope_kwargs: raise TypeError("cannot provide scope and kwargs") + if scope_change is not None: final_scope = copy(base) if callable(scope_change): scope_change(final_scope) else: final_scope.update_from_scope(scope_change) + elif scope_kwargs: final_scope = copy(base) final_scope.update_from_kwargs(**scope_kwargs) + else: final_scope = base + return final_scope From c78b1f55c77a5347827e2032190f93e5e0a1e2e8 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Dec 2023 14:06:42 +0100 Subject: [PATCH 14/25] Hub calls capture_* on scope that calls it on client. Later with new scopes the hub is removed and the root level API directly calls it on the scope --- sentry_sdk/hub.py | 18 ++++++++----- sentry_sdk/scope.py | 62 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index abdc523fb2..753849cd1a 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -316,8 +316,10 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): if client is None: return None - last_event_id = client.capture_event( - event, hint, scope=scope, top_scope=top_scope, **scope_kwargs + scope_kwargs["client"] = client + + last_event_id = top_scope.capture_event( + event, hint, scope=scope, **scope_kwargs ) is_transaction = event.get("type") == "transaction" @@ -348,8 +350,10 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): if client is None: return None - last_event_id = client.capture_message( - message, level=level, scope=scope, top_scope=top_scope, **scope_kwargs + scope_kwargs["client"] = client + + last_event_id = top_scope.capture_message( + message, level=level, scope=scope, **scope_kwargs ) if last_event_id is not None: @@ -374,9 +378,9 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): if client is None: return None - last_event_id = client.capture_exception( - error, scope=scope, top_scope=top_scope, **scope_kwargs - ) + scope_kwargs["client"] = client + + last_event_id = top_scope.capture_exception(error, scope=scope, **scope_kwargs) if last_event_id is not None: self._last_event_id = last_event_id diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 816153d7a9..fbdc8509b2 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -37,12 +37,13 @@ from sentry_sdk._types import ( Breadcrumb, BreadcrumbHint, + ErrorProcessor, Event, EventProcessor, - ErrorProcessor, ExcInfo, Hint, Type, + Union, ) from sentry_sdk.profiler import Profile @@ -581,6 +582,65 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): while len(self._breadcrumbs) > max_breadcrumbs: self._breadcrumbs.popleft() + def capture_event(self, event, hint=None, scope=None, **scope_kwargs): + # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + """ + Captures an event. + + Alias of :py:meth:`sentry_sdk.Client.capture_event`. + + :param scope_kwargs: For supported `**scope_kwargs` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + """ + client = scope_kwargs.pop("client") + + return client.capture_event( + event, hint, scope=scope, top_scope=self, **scope_kwargs + ) + + def capture_message(self, message, level=None, scope=None, **scope_kwargs): + # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] + """ + Captures a message. + + Alias of :py:meth:`sentry_sdk.Client.capture_message`. + + :param message: The string to send as the message. + + :param level: If no level is provided, the default level is `info`. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + + :param scope_kwargs: For supported `**scope_kwargs` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). + """ + client = scope_kwargs.pop("client") + + return client.capture_message( + message, level=level, scope=scope, top_scope=self, **scope_kwargs + ) + + def capture_exception(self, error=None, scope=None, **scope_kwargs): + # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + """Captures an exception. + + Alias of :py:meth:`sentry_sdk.Client.capture_exception`. + + :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. + + :param scope_kwargs: For supported `**scope_kwargs` see + :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). + """ + client = scope_kwargs.pop("client") + + return client.capture_exception( + error, scope=scope, top_scope=self, **scope_kwargs + ) + def start_session(self, *args, **kwargs): # type: (*Any, **Any) -> None """Starts a new session.""" From 269fd5661a66953511afe370ba9a543f3496475d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Dec 2023 15:59:48 +0100 Subject: [PATCH 15/25] Moved everything to scope and only have capture_event in client --- sentry_sdk/client.py | 94 +------------------------------------------- sentry_sdk/scope.py | 56 ++++++++++++++++++++------ 2 files changed, 45 insertions(+), 105 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index a42bf89d33..7cb90e3317 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -1,18 +1,14 @@ from importlib import import_module import os -import sys import uuid import random import socket from sentry_sdk._compat import datetime_utcnow, string_types, text_type, iteritems -from sentry_sdk.scope import _update_scope from sentry_sdk.utils import ( capture_internal_exceptions, current_stacktrace, disable_capture_event, - event_from_exception, - exc_info_from_error, format_timestamp, get_sdk_name, get_type_name, @@ -52,7 +48,7 @@ from sentry_sdk.integrations import Integration from sentry_sdk.scope import Scope - from sentry_sdk._types import Event, ExcInfo, Hint + from sentry_sdk._types import Event, Hint from sentry_sdk.session import Session @@ -556,8 +552,6 @@ def capture_event( event, # type: Event hint=None, # type: Optional[Hint] scope=None, # type: Optional[Scope] - top_scope=None, # type: Optional[Scope] - **scope_kwargs # type: Any ): # type: (...) -> Optional[str] """Captures an event. @@ -578,9 +572,6 @@ def capture_event( if disable_capture_event.get(False): return None - if scope_kwargs is not None and top_scope is not None: - scope = _update_scope(top_scope, scope, scope_kwargs) - if hint is None: hint = {} event_id = event.get("event_id") @@ -668,89 +659,6 @@ def capture_event( return event_id - def capture_message( - self, - message, # type: str - level=None, # type: Optional[str] - scope=None, # type: Optional[Scope] - top_scope=None, # type: Optional[Scope] - **scope_kwargs # type: Any - ): - # type: (...) -> Optional[str] - """ - Captures a message. - - :param message: The string to send as the message. - - :param level: If no level is provided, the default level is `info`. - - :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - - :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. - - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. - - :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). - """ - if level is None: - level = "info" - - return self.capture_event( - {"message": message, "level": level}, - scope=scope, - top_scope=top_scope, - **scope_kwargs - ) - - def capture_exception( - self, - error=None, # type: Optional[Union[BaseException, ExcInfo]] - scope=None, # type: Optional[Scope] - top_scope=None, # type: Optional[Scope] - **scope_kwargs # type: Any - ): - # type: (...) -> Optional[str] - """Captures an exception. - - :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. - - :param scope: An optional :py:class:`sentry_sdk.Scope` to use. - - :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. - - :param scope_kwargs: For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. - - :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). - """ - if error is not None: - exc_info = exc_info_from_error(error) - else: - exc_info = sys.exc_info() - - event, hint = event_from_exception(exc_info, client_options=self.options) - - try: - return self.capture_event( - event, hint=hint, scope=scope, top_scope=top_scope, **scope_kwargs - ) - except Exception: - self._capture_internal_exception(sys.exc_info()) - - return None - - def _capture_internal_exception( - self, exc_info # type: Any - ): - # type: (...) -> Any - """ - Capture an exception that is likely caused by a bug in the SDK - itself. - - These exceptions do not end up in Sentry and are just logged instead. - """ - logger.error("Internal error in sentry_sdk", exc_info=exc_info) - def capture_session( self, session # type: Session ): diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index fbdc8509b2..5d84f826db 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -2,6 +2,7 @@ from collections import deque from itertools import chain import os +import sys import uuid from sentry_sdk.attachments import Attachment @@ -21,7 +22,12 @@ Transaction, ) from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.utils import logger, capture_internal_exceptions +from sentry_sdk.utils import ( + event_from_exception, + exc_info_from_error, + logger, + capture_internal_exceptions, +) if TYPE_CHECKING: from typing import Any @@ -82,7 +88,7 @@ def wrapper(self, *args, **kwargs): return wrapper # type: ignore -def _update_scope(base, scope_change, scope_kwargs): +def _merge_scopes(base, scope_change, scope_kwargs): # type: (Scope, Optional[Any], Dict[str, Any]) -> Scope if scope_change and scope_kwargs: raise TypeError("cannot provide scope and kwargs") @@ -594,9 +600,10 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): """ client = scope_kwargs.pop("client") - return client.capture_event( - event, hint, scope=scope, top_scope=self, **scope_kwargs - ) + if scope_kwargs is not None: + scope = _merge_scopes(self, scope, scope_kwargs) + + return client.capture_event(event, hint, scope=scope) def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] @@ -616,10 +623,13 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ - client = scope_kwargs.pop("client") + if level is None: + level = "info" + + # the client is in scope_kwargs - return client.capture_message( - message, level=level, scope=scope, top_scope=self, **scope_kwargs + return self.capture_event( + {"message": message, "level": level}, scope=scope, **scope_kwargs ) def capture_exception(self, error=None, scope=None, **scope_kwargs): @@ -635,11 +645,33 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ - client = scope_kwargs.pop("client") + if error is not None: + exc_info = exc_info_from_error(error) + else: + exc_info = sys.exc_info() - return client.capture_exception( - error, scope=scope, top_scope=self, **scope_kwargs - ) + event, hint = event_from_exception(exc_info, client_options=self.options) + + try: + # the client is in scope_kwargs + + return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) + except Exception: + self._capture_internal_exception(sys.exc_info()) + + return None + + def _capture_internal_exception( + self, exc_info # type: Any + ): + # type: (...) -> Any + """ + Capture an exception that is likely caused by a bug in the SDK + itself. + + These exceptions do not end up in Sentry and are just logged instead. + """ + logger.error("Internal error in sentry_sdk", exc_info=exc_info) def start_session(self, *args, **kwargs): # type: (*Any, **Any) -> None From 035406552bd473e32da546dcbc0b6a0fef1495d7 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Dec 2023 16:03:08 +0100 Subject: [PATCH 16/25] small fix --- sentry_sdk/scope.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 5d84f826db..f5c8286067 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -650,7 +650,9 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): else: exc_info = sys.exc_info() - event, hint = event_from_exception(exc_info, client_options=self.options) + event, hint = event_from_exception( + exc_info, client_options=scope_kwargs["client"].options + ) try: # the client is in scope_kwargs From 3d5825e667902852ed19fb5898027799eafaf126 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Dec 2023 16:35:14 +0100 Subject: [PATCH 17/25] Updated test --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index a99bb05765..5a226fe638 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -365,7 +365,7 @@ def raise_it(exc_info): reraise(*exc_info) sentry_init(ignore_errors=[ZeroDivisionError], transport=_TestTransport()) - Hub.current.client._capture_internal_exception = raise_it + Hub.current.scope._capture_internal_exception = raise_it def e(exc): try: From 4e78d1f5bb93e52390797b6fc8ca3877573a694a Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 13 Dec 2023 16:53:20 +0100 Subject: [PATCH 18/25] Fixed test --- sentry_sdk/scope.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index f5c8286067..925208c4b9 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -143,6 +143,7 @@ class Scope(object): "_force_auto_session_tracking", "_profile", "_propagation_context", + "_capture_internal_exception", ) def __init__(self): From ba722163fb480c666f38393b7e5912cf66a977aa Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 10:06:56 +0100 Subject: [PATCH 19/25] Fixed test --- sentry_sdk/scope.py | 1 - tests/test_client.py | 34 +++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 925208c4b9..f5c8286067 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -143,7 +143,6 @@ class Scope(object): "_force_auto_session_tracking", "_profile", "_propagation_context", - "_capture_internal_exception", ) def __init__(self): diff --git a/tests/test_client.py b/tests/test_client.py index 5a226fe638..e02175ab92 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,7 +20,7 @@ ) from sentry_sdk.integrations.executing import ExecutingIntegration from sentry_sdk.transport import Transport -from sentry_sdk._compat import reraise, text_type, PY2 +from sentry_sdk._compat import text_type, PY2 from sentry_sdk.utils import HAS_CHAINED_EXCEPTIONS from sentry_sdk.utils import logger from sentry_sdk.serializer import MAX_DATABAG_BREADTH @@ -358,24 +358,28 @@ def test_simple_transport(sentry_init): def test_ignore_errors(sentry_init, capture_events): - class MyDivisionError(ZeroDivisionError): - pass - def raise_it(exc_info): - reraise(*exc_info) + with mock.patch( + "sentry_sdk.scope.Scope._capture_internal_exception" + ) as mock_capture_internal_exception: - sentry_init(ignore_errors=[ZeroDivisionError], transport=_TestTransport()) - Hub.current.scope._capture_internal_exception = raise_it + class MyDivisionError(ZeroDivisionError): + pass - def e(exc): - try: - raise exc - except Exception: - capture_exception() + sentry_init(ignore_errors=[ZeroDivisionError], transport=_TestTransport()) + + def e(exc): + try: + raise exc + except Exception: + capture_exception() + + e(ZeroDivisionError()) + e(MyDivisionError()) + e(ValueError()) - e(ZeroDivisionError()) - e(MyDivisionError()) - pytest.raises(EventCapturedError, lambda: e(ValueError())) + mock_capture_internal_exception.assert_called_once() + assert mock_capture_internal_exception.call_args[0][0][0] == EventCapturedError def test_with_locals_deprecation_enabled(sentry_init): From c5a7ae790bc0b332a8596614c537c96071fd7bcf Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 10:54:21 +0100 Subject: [PATCH 20/25] Fixed test for old python --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index e02175ab92..da13cbcb72 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -378,7 +378,7 @@ def e(exc): e(MyDivisionError()) e(ValueError()) - mock_capture_internal_exception.assert_called_once() + assert mock_capture_internal_exception.call_count == 1 assert mock_capture_internal_exception.call_args[0][0][0] == EventCapturedError From bbb93e6c90fbf2801922d49f3579463c518ab217 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 10:57:15 +0100 Subject: [PATCH 21/25] Formatting --- tests/test_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index da13cbcb72..fa55c1111a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -358,7 +358,6 @@ def test_simple_transport(sentry_init): def test_ignore_errors(sentry_init, capture_events): - with mock.patch( "sentry_sdk.scope.Scope._capture_internal_exception" ) as mock_capture_internal_exception: From 12e632d2b356b4c30b0cc1f0ca9b7bc216b8022d Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 12:54:59 +0100 Subject: [PATCH 22/25] Passing the client as separate argument and updating api docs. --- docs/apidocs.rst | 3 ++ sentry_sdk/client.py | 10 ++--- sentry_sdk/hub.py | 52 +++++++++++++++----------- sentry_sdk/scope.py | 87 +++++++++++++++++++++++++++++--------------- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/docs/apidocs.rst b/docs/apidocs.rst index dc4117e559..855778484d 100644 --- a/docs/apidocs.rst +++ b/docs/apidocs.rst @@ -11,6 +11,9 @@ API Docs .. autoclass:: sentry_sdk.Client :members: +.. autoclass:: sentry_sdk.client._Client + :members: + .. autoclass:: sentry_sdk.Transport :members: diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 7cb90e3317..ce2fed258b 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -156,6 +156,8 @@ class _Client(object): forwarding them to sentry through the configured transport. It takes the client options as keyword arguments and optionally the DSN as first argument. + + Alias of :py:class:`Client`. """ def __init__(self, *args, **kwargs): @@ -560,12 +562,8 @@ def capture_event( :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. - :param scope: An optional scope to use for determining whether this event should be captured. - - :param top_scope: An optional top scope that should also be merged into the scope to be applied to the event. - - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help. """ diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 753849cd1a..cf748bb8ea 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -307,19 +307,25 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): """ Captures an event. - Alias of :py:meth:`sentry_sdk.Client.capture_event`. + Alias of :py:meth:`sentry_sdk.Scope.capture_event`. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. """ client, top_scope = self._stack[-1] if client is None: return None - scope_kwargs["client"] = client - last_event_id = top_scope.capture_event( - event, hint, scope=scope, **scope_kwargs + event, hint, client=client, scope=scope, **scope_kwargs ) is_transaction = event.get("type") == "transaction" @@ -333,16 +339,18 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): """ Captures a message. - Alias of :py:meth:`sentry_sdk.Client.capture_message`. + Alias of :py:meth:`sentry_sdk.Scope.capture_message`. - :param message: The string to send as the message. + :param message: The string to send as the message to Sentry. :param level: If no level is provided, the default level is `info`. - :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ @@ -350,10 +358,8 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs): if client is None: return None - scope_kwargs["client"] = client - last_event_id = top_scope.capture_message( - message, level=level, scope=scope, **scope_kwargs + message, level=level, client=client, scope=scope, **scope_kwargs ) if last_event_id is not None: @@ -365,12 +371,16 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] """Captures an exception. - Alias of :py:meth:`sentry_sdk.Client.capture_exception`. + Alias of :py:meth:`sentry_sdk.Scope.capture_exception`. + + :param error: An exception to capture. If `None`, `sys.exc_info()` will be used. - :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ @@ -378,9 +388,9 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): if client is None: return None - scope_kwargs["client"] = client - - last_event_id = top_scope.capture_exception(error, scope=scope, **scope_kwargs) + last_event_id = top_scope.capture_exception( + error, client=client, scope=scope, **scope_kwargs + ) if last_event_id is not None: self._last_event_id = last_event_id diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index f5c8286067..b25e6a98e6 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -52,6 +52,7 @@ Union, ) + import sentry_sdk from sentry_sdk.profiler import Profile from sentry_sdk.tracing import Span @@ -588,76 +589,102 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): while len(self._breadcrumbs) > max_breadcrumbs: self._breadcrumbs.popleft() - def capture_event(self, event, hint=None, scope=None, **scope_kwargs): - # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwargs): + # type: (Event, Optional[Hint], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str] """ Captures an event. - Alias of :py:meth:`sentry_sdk.Client.capture_event`. + Merges given scope data and calls :py:meth:`sentry_sdk.Client.capture_event`. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param client: The client to use for sending the event to Sentry. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ - client = scope_kwargs.pop("client") + if client is None: + return None if scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return client.capture_event(event, hint, scope=scope) + return client.capture_event(event, hint, client=client, scope=scope) - def capture_message(self, message, level=None, scope=None, **scope_kwargs): - # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] + def capture_message( + self, message, level=None, client=None, scope=None, **scope_kwargs + ): + # type: (str, Optional[str], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str] """ Captures a message. - Alias of :py:meth:`sentry_sdk.Client.capture_message`. - :param message: The string to send as the message. :param level: If no level is provided, the default level is `info`. - :param scope: An optional :py:class:`sentry_sdk.Scope` to use. + :param client: The client to use for sending the event to Sentry. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ + if client is None: + return None + if level is None: level = "info" - # the client is in scope_kwargs + event = { + "message": message, + "level": level, + } - return self.capture_event( - {"message": message, "level": level}, scope=scope, **scope_kwargs - ) + return self.capture_event(event, client=client, scope=scope, **scope_kwargs) - def capture_exception(self, error=None, scope=None, **scope_kwargs): - # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + def capture_exception(self, error=None, client=None, scope=None, **scope_kwargs): + # type: (Optional[Union[BaseException, ExcInfo]], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str] """Captures an exception. - Alias of :py:meth:`sentry_sdk.Client.capture_exception`. + :param error: An exception to capture. If `None`, `sys.exc_info()` will be used. + + :param client: The client to use for sending the event to Sentry. - :param error: An exception to catch. If `None`, `sys.exc_info()` will be used. + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. - :param scope_kwargs: For supported `**scope_kwargs` see - :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`). """ + if client is None: + return None + if error is not None: exc_info = exc_info_from_error(error) else: exc_info = sys.exc_info() - event, hint = event_from_exception( - exc_info, client_options=scope_kwargs["client"].options - ) + event, hint = event_from_exception(exc_info, client_options=client.options) try: - # the client is in scope_kwargs - - return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) + return self.capture_event( + event, hint=hint, client=client, scope=scope, **scope_kwargs + ) except Exception: self._capture_internal_exception(sys.exc_info()) From eb56a127d04332fbb159d2b2555a7fed5f1a237b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 12:55:33 +0100 Subject: [PATCH 23/25] ApiDocs --- sentry_sdk/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index ce2fed258b..70ffdbe2aa 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -157,7 +157,7 @@ class _Client(object): the client options as keyword arguments and optionally the DSN as first argument. - Alias of :py:class:`Client`. + Alias of :py:class:`Client`. (Was created for better intelisense support) """ def __init__(self, *args, **kwargs): From 074a9eff83343fc3fa0b969fdf0f026f5d5a2c1b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 14 Dec 2023 13:05:47 +0100 Subject: [PATCH 24/25] Fix --- sentry_sdk/scope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index b25e6a98e6..8682e7face 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -617,7 +617,7 @@ def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwarg if scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return client.capture_event(event, hint, client=client, scope=scope) + return client.capture_event(event=event, hint=hint, scope=scope) def capture_message( self, message, level=None, client=None, scope=None, **scope_kwargs From 6733114e347115f3f40494d74b1ec2bdda69fea6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 08:37:18 +0100 Subject: [PATCH 25/25] Remove condition from _merge_scope (also not present in current implementation) --- sentry_sdk/scope.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 8682e7face..c715847d38 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -614,8 +614,7 @@ def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwarg if client is None: return None - if scope_kwargs is not None: - scope = _merge_scopes(self, scope, scope_kwargs) + scope = _merge_scopes(self, scope, scope_kwargs) return client.capture_event(event=event, hint=hint, scope=scope)