From c6c51fdcb5d3d10f6cda7b7392d01586ff653c4c Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 11:24:54 +0100 Subject: [PATCH 01/16] Added new global APIs --- docs/api.rst | 12 ++++++++ docs/apidocs.rst | 3 ++ sentry_sdk/api.py | 69 ++++++++++++++++++++++++++++++++++++++++++++ sentry_sdk/client.py | 10 +++++-- sentry_sdk/scope.py | 9 ++++++ 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index f504bbb642..d12f2caaab 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -40,6 +40,18 @@ Distributed Tracing .. autofunction:: sentry_sdk.api.get_baggage .. autofunction:: sentry_sdk.api.get_traceparent +New Scopes/Client APIs +====================== + +.. autofunction:: sentry_sdk.api.get_client +.. autofunction:: sentry_sdk.api.sentry_is_initialized +.. autofunction:: sentry_sdk.api.get_current_scope +.. autofunction:: sentry_sdk.api.get_isolation_scope +.. autofunction:: sentry_sdk.api.get_global_scope + +.. autofunction:: sentry_sdk.api.set_current_scope +.. autofunction:: sentry_sdk.api.set_isolation_scope + Managing Scope (advanced) ========================= diff --git a/docs/apidocs.rst b/docs/apidocs.rst index 855778484d..52f87544d0 100644 --- a/docs/apidocs.rst +++ b/docs/apidocs.rst @@ -14,6 +14,9 @@ API Docs .. autoclass:: sentry_sdk.client._Client :members: +.. autoclass:: sentry_sdk.client.NoopClient + :members: + .. autoclass:: sentry_sdk.Transport :members: diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index ffa525ca66..939e4588f6 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -1,6 +1,8 @@ import inspect +from sentry_sdk import scope from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.client import Client, NoopClient from sentry_sdk.hub import Hub from sentry_sdk.scope import Scope from sentry_sdk.tracing import NoOpSpan, Transaction @@ -77,6 +79,73 @@ def scopemethod(f): return f +def clientemethod(f): + # type: (F) -> F + f.__doc__ = "%s\n\n%s" % ( + "Alias for :py:meth:`sentry_sdk._Client.%s`" % f.__name__, + inspect.getdoc(getattr(Client, f.__name__)), + ) + return f + + +def sentry_is_initialized(): + # type: () -> bool + """ + Returns whether Sentry has been initialized or not. + If an client is available Sentry is initialized. + """ + client = Client.get_client() + if client.__class__ == NoopClient: + return False + else: + return True + + +@clientemethod +def get_client(): + # type: () -> Union[Client, NoopClient] + return Client.get_client() + + +@scopemethod +def get_current_scope(): + # type: () -> Scope + return Scope.get_current_scope() + + +@scopemethod +def get_isolation_scope(): + # type: () -> Scope + return Scope.get_isolation_scope() + + +@scopemethod +def get_global_scope(): + # type: () -> Scope + return Scope.get_global_scope() + + +def set_current_scope(new_current_scope): + # type: (Scope) -> None + """ + Sets the given scope as the new current scope overwritting the existing current scope. + + :param new_current_scope: The scope to set as the new current scope. + """ + + scope.sentry_current_scope.set(new_current_scope) + + +def set_isolation_scope(new_isolation_scope): + # type: (Scope) -> None + """ + Sets the given scope as the new isolation scope overwritting the existing isolation scope. + + :param new_isolation_scope: The scope to set as the new isolation scope. + """ + scope.sentry_isolation_scope.set(new_isolation_scope) + + @hubmethod def capture_event( event, # type: Event diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index bab41f7be5..acd8ad0c16 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -152,6 +152,9 @@ def _get_options(*args, **kwargs): class NoopClient: + """ + A client that does not send any events to Sentry. This is used as a fallback when the Sentry SDK is not yet initialized. + """ def __repr__(self): # type: () -> str return "<{} id={}>".format(self.__class__.__name__, id(self)) @@ -241,7 +244,7 @@ class _Client(NoopClient): the client options as keyword arguments and optionally the DSN as first argument. - Alias of :py:class:`Client`. (Was created for better intelisense support) + Alias of :py:class:`sentry_sdk.Client`. (Was created for better intelisense support) """ def __init__(self, *args, **kwargs): @@ -261,7 +264,10 @@ def __setstate__(self, state): @classmethod def get_client(cls): - # type: () -> Union[_Client, NoopClient] + # type: () -> Union[Client, NoopClient] + """ + Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. + """ client = Scope.get_current_scope().client if client is not None: return client diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index acc3b33520..aa958727c6 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -210,6 +210,9 @@ def __init__(self, ty=None, client=None): @classmethod def get_current_scope(cls): # type: () -> Scope + """ + Returns the current scope. + """ scope = sentry_current_scope.get() if scope is None: scope = Scope(ty="current") @@ -220,6 +223,9 @@ def get_current_scope(cls): @classmethod def get_isolation_scope(cls): # type: () -> Scope + """ + Returns the isolation scope. + """ scope = sentry_isolation_scope.get() if scope is None: scope = Scope(ty="isolation") @@ -229,6 +235,9 @@ def get_isolation_scope(cls): @classmethod def get_global_scope(cls): + """ + Returns the global scope. + """ # type: () -> Scope global SENTRY_GLOBAL_SCOPE if SENTRY_GLOBAL_SCOPE is None: From cb50afbd37e6864a6c8ddc3585d6ded252d7670f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 11:25:27 +0100 Subject: [PATCH 02/16] whitespace --- docs/api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.rst b/docs/api.rst index d12f2caaab..084266eb0e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -40,6 +40,7 @@ Distributed Tracing .. autofunction:: sentry_sdk.api.get_baggage .. autofunction:: sentry_sdk.api.get_traceparent + New Scopes/Client APIs ====================== From 1ea1a7df4ee60e887820695f1765fdf9eb1898f9 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 11:33:02 +0100 Subject: [PATCH 03/16] Some apidocs --- sentry_sdk/scope.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index aa958727c6..82beadedf7 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -248,17 +248,24 @@ def get_global_scope(cls): @property def is_forked(self): # type: () -> bool + """ + Weither this scope is a fork of another scope. + """ return self.original_scope is not None def fork(self): # type: () -> Scope + """ + Returns a fork of this scope. + """ self.original_scope = self return copy(self) def isolate(self): # type: () -> None """ - Create a new isolation scope for this scope. + Creates a new isolation scope for this scope. + The new isolation scope will be a fork of the current isolation scope. """ isolation_scope = Scope.get_isolation_scope() forked_isolation_scope = isolation_scope.fork() @@ -266,6 +273,12 @@ def isolate(self): def set_client(self, client=None): # type: (Optional[sentry_sdk.Client]) -> None + """ + Sets the client for this scope. + + :param client: The client to use in this scope. + If `None` the client of the scope will be deleted. + """ self.client = client def _load_trace_data_from_env(self): From 76da76537492ada7783d38e78f7edb0ba8e31107 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 11:42:28 +0100 Subject: [PATCH 04/16] Added 'new in version' to apidocs --- sentry_sdk/api.py | 6 ++++++ sentry_sdk/client.py | 4 ++++ sentry_sdk/scope.py | 14 ++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 939e4588f6..95a1ab260c 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -93,6 +93,8 @@ def sentry_is_initialized(): """ Returns whether Sentry has been initialized or not. If an client is available Sentry is initialized. + + .. versionadded:: 1.XX.0 """ client = Client.get_client() if client.__class__ == NoopClient: @@ -131,6 +133,8 @@ def set_current_scope(new_current_scope): Sets the given scope as the new current scope overwritting the existing current scope. :param new_current_scope: The scope to set as the new current scope. + + .. versionadded:: 1.XX.0 """ scope.sentry_current_scope.set(new_current_scope) @@ -142,6 +146,8 @@ def set_isolation_scope(new_isolation_scope): Sets the given scope as the new isolation scope overwritting the existing isolation scope. :param new_isolation_scope: The scope to set as the new isolation scope. + + .. versionadded:: 1.XX.0 """ scope.sentry_isolation_scope.set(new_isolation_scope) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index acd8ad0c16..02c8d86d9e 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -154,6 +154,8 @@ def _get_options(*args, **kwargs): class NoopClient: """ A client that does not send any events to Sentry. This is used as a fallback when the Sentry SDK is not yet initialized. + + .. versionadded:: 1.XX.0 """ def __repr__(self): # type: () -> str @@ -267,6 +269,8 @@ def get_client(cls): # type: () -> Union[Client, NoopClient] """ Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. + + .. versionadded:: 1.XX.0 """ client = Scope.get_current_scope().client if client is not None: diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 82beadedf7..e0e7aa42d9 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -212,6 +212,8 @@ def get_current_scope(cls): # type: () -> Scope """ Returns the current scope. + + .. versionadded:: 1.XX.0 """ scope = sentry_current_scope.get() if scope is None: @@ -225,6 +227,8 @@ def get_isolation_scope(cls): # type: () -> Scope """ Returns the isolation scope. + + .. versionadded:: 1.XX.0 """ scope = sentry_isolation_scope.get() if scope is None: @@ -237,6 +241,8 @@ def get_isolation_scope(cls): def get_global_scope(cls): """ Returns the global scope. + + .. versionadded:: 1.XX.0 """ # type: () -> Scope global SENTRY_GLOBAL_SCOPE @@ -250,6 +256,8 @@ def is_forked(self): # type: () -> bool """ Weither this scope is a fork of another scope. + + .. versionadded:: 1.XX.0 """ return self.original_scope is not None @@ -257,6 +265,8 @@ def fork(self): # type: () -> Scope """ Returns a fork of this scope. + + .. versionadded:: 1.XX.0 """ self.original_scope = self return copy(self) @@ -266,6 +276,8 @@ def isolate(self): """ Creates a new isolation scope for this scope. The new isolation scope will be a fork of the current isolation scope. + + .. versionadded:: 1.XX.0 """ isolation_scope = Scope.get_isolation_scope() forked_isolation_scope = isolation_scope.fork() @@ -278,6 +290,8 @@ def set_client(self, client=None): :param client: The client to use in this scope. If `None` the client of the scope will be deleted. + + .. versionadded:: 1.XX.0 """ self.client = client From af59f78559a9b20421e550e718b451689c4cdbae Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 12:00:48 +0100 Subject: [PATCH 05/16] formatting --- sentry_sdk/api.py | 2 +- sentry_sdk/client.py | 1 + sentry_sdk/scope.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 95a1ab260c..91d1692aca 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -91,7 +91,7 @@ def clientemethod(f): def sentry_is_initialized(): # type: () -> bool """ - Returns whether Sentry has been initialized or not. + Returns whether Sentry has been initialized or not. If an client is available Sentry is initialized. .. versionadded:: 1.XX.0 diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 02c8d86d9e..4fabdb64da 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -157,6 +157,7 @@ class NoopClient: .. versionadded:: 1.XX.0 """ + def __repr__(self): # type: () -> str return "<{} id={}>".format(self.__class__.__name__, id(self)) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index def27b5d20..7525e6c9d5 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -274,7 +274,7 @@ def fork(self): def isolate(self): # type: () -> None """ - Creates a new isolation scope for this scope. + Creates a new isolation scope for this scope. The new isolation scope will be a fork of the current isolation scope. .. versionadded:: 1.XX.0 @@ -288,7 +288,7 @@ def set_client(self, client=None): """ Sets the client for this scope. - :param client: The client to use in this scope. + :param client: The client to use in this scope. If `None` the client of the scope will be deleted. .. versionadded:: 1.XX.0 From 96a22ef9112d30ef3243ae2d7a85a669fe2078d1 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 12:05:15 +0100 Subject: [PATCH 06/16] formatting --- 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 7525e6c9d5..506a0d3b25 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -239,12 +239,12 @@ def get_isolation_scope(cls): @classmethod def get_global_scope(cls): + # type: () -> Scope """ Returns the global scope. .. versionadded:: 1.XX.0 """ - # type: () -> Scope global SENTRY_GLOBAL_SCOPE if SENTRY_GLOBAL_SCOPE is None: SENTRY_GLOBAL_SCOPE = Scope(ty="global") From 5f2ec2924b1169a619933dfaa00818247b00df77 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 15:15:19 +0100 Subject: [PATCH 07/16] First try to switch Scope with new scope mechanisms under the hood --- sentry_sdk/client.py | 7 ++- sentry_sdk/hub.py | 110 +++++++++++++++---------------------------- sentry_sdk/scope.py | 84 ++++++++++++--------------------- 3 files changed, 76 insertions(+), 125 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 4fabdb64da..a60e511980 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -158,6 +158,11 @@ class NoopClient: .. versionadded:: 1.XX.0 """ + options = _get_options() # type: Dict[str, Any] + metrics_aggregator = None # type: Optional[Any] + monitor = None # type: Optional[Any] + transport = None # type: Optional[Any] + def __repr__(self): # type: () -> str return "<{} id={}>".format(self.__class__.__name__, id(self)) @@ -267,7 +272,7 @@ def __setstate__(self, state): @classmethod def get_client(cls): - # type: () -> Union[Client, NoopClient] + # type: () -> NoopClient """ Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 45afb56cc9..ae95ac0d10 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -6,7 +6,7 @@ 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 +from sentry_sdk.client import Client, NoopClient from sentry_sdk.tracing import ( NoOpSpan, Span, @@ -266,21 +266,19 @@ def get_integration( If the return value is not `None` the hub is guaranteed to have a client attached. """ - client = self.client - if client is not None: - return client.get_integration(name_or_class) + Client.get_client().get_integration(name_or_class) @property def client(self): - # type: () -> Optional[Client] + # type: () -> Optional[NoopClient] """Returns the current client on the hub.""" - return self._stack[-1][0] + return Client.get_client() @property def scope(self): # type: () -> Scope """Returns the current scope on the hub.""" - return self._stack[-1][1] + return Scope.get_current_scope() def last_event_id(self): # type: () -> Optional[str] @@ -292,8 +290,7 @@ def bind_client( ): # type: (...) -> None """Binds a new client to the hub.""" - top = self._stack[-1] - self._stack[-1] = (new, top[1]) + Scope.get_global_scope().set_client(new) def capture_event(self, event, hint=None, scope=None, **scope_kwargs): # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] @@ -313,12 +310,8 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): 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 - - last_event_id = top_scope.capture_event( - event, hint, client=client, scope=scope, **scope_kwargs + last_event_id = Scope.get_current_scope().capture_event( + event, hint, scope=scope, **scope_kwargs ) is_transaction = event.get("type") == "transaction" @@ -347,12 +340,8 @@ 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, top_scope = self._stack[-1] - if client is None: - return None - - last_event_id = top_scope.capture_message( - message, level=level, client=client, scope=scope, **scope_kwargs + last_event_id = Scope.get_current_scope().capture_message( + message, level=level, scope=scope, **scope_kwargs ) if last_event_id is not None: @@ -377,12 +366,8 @@ 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, top_scope = self._stack[-1] - if client is None: - return None - - last_event_id = top_scope.capture_exception( - error, client=client, scope=scope, **scope_kwargs + last_event_id = Scope.get_current_scope().capture_exception( + error, scope=scope, **scope_kwargs ) if last_event_id is not None: @@ -419,9 +404,10 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs): logger.info("Dropped breadcrumb because no client bound") return + # TODO: fix client kwargs["client"] = client - scope.add_breadcrumb(crumb, hint, **kwargs) + Scope.get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): # type: (Optional[Span], str, Any) -> Span @@ -440,12 +426,12 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - client, scope = self._stack[-1] - + # TODO: fix hub kwargs["hub"] = self - kwargs["client"] = client - return scope.start_span(span=span, instrumenter=instrumenter, **kwargs) + return Scope.get_isolation_scope().start_span( + span=span, instrumenter=instrumenter, **kwargs + ) def start_transaction( self, transaction=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs @@ -475,12 +461,10 @@ def start_transaction( For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. """ - client, scope = self._stack[-1] - + # TODO: fix hub kwargs["hub"] = self - kwargs["client"] = client - return scope.start_transaction( + return Scope.get_isolation_scope().start_transaction( transaction=transaction, instrumenter=instrumenter, **kwargs ) @@ -489,9 +473,7 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None): """ Sets the propagation context from environment or headers and returns a transaction. """ - scope = self._stack[-1][1] - - return scope.continue_trace( + return Scope.get_isolation_scope().continue_trace( environ_or_headers=environ_or_headers, op=op, name=name, source=source ) @@ -580,25 +562,19 @@ def configure_scope( # noqa :returns: If no callback is provided, returns a context manager that returns the scope. """ - - client, scope = self._stack[-1] + scope = Scope.get_isolation_scope() if continue_trace: - scope.generate_propagation_context() + Scope.get_isolation_scope().generate_propagation_context() if callback is not None: - if client is not None: - callback(scope) - - return None + # TODO: used to return None when client is None. Check if this changes behavior. + callback(scope) @contextmanager def inner(): # type: () -> Generator[Scope, None, None] - if client is not None: - yield scope - else: - yield Scope() + yield scope return inner() @@ -607,17 +583,14 @@ def start_session( ): # type: (...) -> None """Starts a new session.""" - client, scope = self._stack[-1] - scope.start_session( - client=client, + Scope.get_isolation_scope().start_session( session_mode=session_mode, ) def end_session(self): # type: (...) -> None """Ends the current session if there is one.""" - client, scope = self._stack[-1] - scope.end_session(client=client) + Scope.get_isolation_scope().end_session() def stop_auto_session_tracking(self): # type: (...) -> None @@ -626,8 +599,7 @@ 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`. """ - client, scope = self._stack[-1] - scope.stop_auto_session_tracking(client=client) + Scope.get_isolation_scope().stop_auto_session_tracking() def resume_auto_session_tracking(self): # type: (...) -> None @@ -635,8 +607,7 @@ def resume_auto_session_tracking(self): disabled earlier. This requires that generally automatic session tracking is enabled. """ - scope = self._stack[-1][1] - scope.resume_auto_session_tracking() + Scope.get_isolation_scope().resume_auto_session_tracking() def flush( self, @@ -647,25 +618,21 @@ def flush( """ Alias for :py:meth:`sentry_sdk.Client.flush` """ - client, scope = self._stack[-1] - if client is not None: - return client.flush(timeout=timeout, callback=callback) + return Client.get_client().flush(timeout=timeout, callback=callback) def get_traceparent(self): # type: () -> Optional[str] """ Returns the traceparent either from the active span or from the scope. """ - client, scope = self._stack[-1] - return scope.get_traceparent(client=client) + return Scope.get_current_scope().get_traceparent() def get_baggage(self): # type: () -> Optional[str] """ Returns Baggage either from the active span or from the scope. """ - client, scope = self._stack[-1] - baggage = scope.get_baggage(client=client) + baggage = Scope.get_current_scope().get_baggage() if baggage is not None: return baggage.serialize() @@ -679,9 +646,9 @@ def iter_trace_propagation_headers(self, span=None): from the span representing the request, if available, or the current span on the scope if not. """ - client, scope = self._stack[-1] - - return scope.iter_trace_propagation_headers(span=span, client=client) + return Scope.get_current_scope().iter_trace_propagation_headers( + span=span, + ) def trace_propagation_meta(self, span=None): # type: (Optional[Span]) -> str @@ -694,8 +661,9 @@ def trace_propagation_meta(self, span=None): "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future." ) - client, scope = self._stack[-1] - return scope.trace_propagation_meta(span=span, client=client) + return Scope.get_current_scope().trace_propagation_meta( + span=span, + ) GLOBAL_HUB = Hub() diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 506a0d3b25..e7dc750e0d 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -8,6 +8,7 @@ from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow +from sentry_sdk.client import Client from sentry_sdk.consts import FALSE_VALUES, INSTRUMENTER from sentry_sdk._functools import wraps from sentry_sdk.profiler import Profile @@ -416,7 +417,7 @@ def get_traceparent(self, *args, **kwargs): Returns the Sentry "sentry-trace" header (aka the traceparent) from the currently active span or the scopes Propagation Context. """ - client = kwargs.pop("client", None) + client = Client.get_client() # If we have an active span, return traceparent from there if ( @@ -437,7 +438,7 @@ def get_traceparent(self, *args, **kwargs): def get_baggage(self, *args, **kwargs): # type: (Any, Any) -> Optional[Baggage] - client = kwargs.pop("client", None) + client = Client.get_client() # If we have an active span, return baggage from there if ( @@ -487,18 +488,16 @@ def trace_propagation_meta(self, *args, **kwargs): "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future." ) - client = kwargs.pop("client", None) - meta = "" - sentry_trace = self.get_traceparent(client=client) + sentry_trace = self.get_traceparent() if sentry_trace is not None: meta += '' % ( SENTRY_TRACE_HEADER_NAME, sentry_trace, ) - baggage = self.get_baggage(client=client) + baggage = self.get_baggage() if baggage is not None: meta += '' % ( BAGGAGE_HEADER_NAME, @@ -529,16 +528,14 @@ def iter_trace_propagation_headers(self, *args, **kwargs): from the span representing the request, if available, or the current span on the scope if not. """ - span = kwargs.pop("span", None) - client = kwargs.pop("client", None) - - propagate_traces = client and client.options["propagate_traces"] - if not propagate_traces: + client = Client.get_client() + if not client.options.get("propagate_traces"): return + span = kwargs.pop("span", None) span = span or self.span - if client and has_tracing_enabled(client.options) and span is not None: + if has_tracing_enabled(client.options) and span is not None: for header in span.iter_headers(): yield header else: @@ -801,8 +798,8 @@ 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, client=None, scope=None, **scope_kwargs): - # type: (Event, Optional[Hint], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str] + def capture_event(self, event, hint=None, scope=None, **scope_kwargs): + # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] """ Captures an event. @@ -823,18 +820,13 @@ def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwarg :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 scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return client.capture_event(event=event, hint=hint, scope=scope) + return Client.get_client().capture_event(event=event, hint=hint, scope=scope) - 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] + def capture_message(self, message, level=None, scope=None, **scope_kwargs): + # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] """ Captures a message. @@ -853,9 +845,6 @@ def capture_message( :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" @@ -864,10 +853,10 @@ def capture_message( "level": level, } - return self.capture_event(event, client=client, scope=scope, **scope_kwargs) + return self.capture_event(event, scope=scope, **scope_kwargs) - 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] + 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 capture. If `None`, `sys.exc_info()` will be used. @@ -883,20 +872,17 @@ def capture_exception(self, error=None, client=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`). """ - 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) + event, hint = event_from_exception( + exc_info, client_options=Client.get_client().options + ) try: - return self.capture_event( - event, hint=hint, client=client, scope=scope, **scope_kwargs - ) + return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) except Exception: self._capture_internal_exception(sys.exc_info()) @@ -943,9 +929,9 @@ def start_transaction( For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. """ hub = kwargs.pop("hub", None) - client = kwargs.pop("client", None) + client = Client.get_client() - configuration_instrumenter = client and client.options["instrumenter"] + configuration_instrumenter = client.options.get("instrumenter") if instrumenter != configuration_instrumenter: return NoOpSpan() @@ -996,9 +982,9 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - client = kwargs.get("client", None) + client = Client.get_client() - configuration_instrumenter = client and client.options["instrumenter"] + configuration_instrumenter = client.options.get("instrumenter") if instrumenter != configuration_instrumenter: return NoOpSpan() @@ -1029,8 +1015,6 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): logger.warning(deprecation_msg) return span - kwargs.pop("client") - active_span = self.span if active_span is not None: new_child_span = active_span.start_child(**kwargs) @@ -1066,14 +1050,14 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None): 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.end_session() + client = Client.get_client() self._session = Session( - release=client.options["release"] if client else None, - environment=client.options["environment"] if client else None, + release=client.options.get("release"), + environment=client.options.get("environment"), user=self._user, session_mode=session_mode, ) @@ -1081,15 +1065,12 @@ def start_session(self, *args, **kwargs): 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) + Client.get_client().capture_session(session) def stop_auto_session_tracking(self, *args, **kwargs): # type: (*Any, **Any) -> None @@ -1098,10 +1079,7 @@ def stop_auto_session_tracking(self, *args, **kwargs): 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.end_session() self._force_auto_session_tracking = False def resume_auto_session_tracking(self): From 790b0bce33f867710d39561b6bf148d063835c75 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 15:22:57 +0100 Subject: [PATCH 08/16] Fixed add_breadcrumb (to always use isolation scope for now --- sentry_sdk/hub.py | 8 -------- sentry_sdk/scope.py | 8 +++++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index ae95ac0d10..4c7f7de166 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -399,14 +399,6 @@ 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. """ - client, scope = self._stack[-1] - if client is None: - logger.info("Dropped breadcrumb because no client bound") - return - - # TODO: fix client - kwargs["client"] = client - Scope.get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index e7dc750e0d..6352089a28 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -8,7 +8,7 @@ from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow -from sentry_sdk.client import Client +from sentry_sdk.client import Client, NoopClient from sentry_sdk.consts import FALSE_VALUES, INSTRUMENTER from sentry_sdk._functools import wraps from sentry_sdk.profiler import Profile @@ -766,8 +766,10 @@ 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. """ - client = kwargs.pop("client", None) - if client is None: + client = Client.get_client() + + if client.__class__ == NoopClient: + logger.info("Dropped breadcrumb because no client bound") return before_breadcrumb = client.options.get("before_breadcrumb") From bf94514ba7d93eca25e0abe4ec141a7159e998dd Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 15:35:16 +0100 Subject: [PATCH 09/16] Sorted imports --- sentry_sdk/client.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index a60e511980..18a28d65b7 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -5,37 +5,37 @@ import socket from sentry_sdk._compat import datetime_utcnow, string_types, text_type, iteritems -from sentry_sdk.utils import ( - capture_internal_exceptions, - current_stacktrace, - disable_capture_event, - format_timestamp, - get_sdk_name, - get_type_name, - get_default_release, - handle_in_app, - logger, -) -from sentry_sdk.serializer import serialize -from sentry_sdk.tracing import trace, has_tracing_enabled -from sentry_sdk.transport import make_transport from sentry_sdk.consts import ( + ClientConstructor, DEFAULT_MAX_VALUE_LENGTH, DEFAULT_OPTIONS, INSTRUMENTER, VERSION, - ClientConstructor, ) -from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, setup_integrations -from sentry_sdk.utils import ContextVar -from sentry_sdk.sessions import SessionFlusher from sentry_sdk.envelope import Envelope +from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, setup_integrations +from sentry_sdk.monitor import Monitor from sentry_sdk.profiler import has_profiling_enabled, setup_profiler +from sentry_sdk.scope import Scope from sentry_sdk.scrubber import EventScrubber -from sentry_sdk.monitor import Monitor +from sentry_sdk.sessions import SessionFlusher from sentry_sdk.spotlight import setup_spotlight - +from sentry_sdk.tracing import trace, has_tracing_enabled +from sentry_sdk.transport import make_transport +from sentry_sdk.serializer import serialize from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk.utils import ( + capture_internal_exceptions, + ContextVar, + current_stacktrace, + disable_capture_event, + format_timestamp, + get_default_release, + get_sdk_name, + get_type_name, + handle_in_app, + logger, +) if TYPE_CHECKING: from typing import Any @@ -47,7 +47,6 @@ 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 From 1386be7ffbf6692d7c333704b2440f085236450a Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 16:03:22 +0100 Subject: [PATCH 10/16] Fixed import --- sentry_sdk/client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 18a28d65b7..df0a9157f1 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -16,7 +16,6 @@ from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, setup_integrations from sentry_sdk.monitor import Monitor from sentry_sdk.profiler import has_profiling_enabled, setup_profiler -from sentry_sdk.scope import Scope from sentry_sdk.scrubber import EventScrubber from sentry_sdk.sessions import SessionFlusher from sentry_sdk.spotlight import setup_spotlight @@ -49,6 +48,7 @@ from sentry_sdk.integrations import Integration from sentry_sdk._types import Event, Hint from sentry_sdk.session import Session + from sentry_sdk.scope import Scope _client_init_debug = ContextVar("client_init_debug") @@ -277,6 +277,8 @@ def get_client(cls): .. versionadded:: 1.XX.0 """ + from sentry_sdk.scope import Scope + client = Scope.get_current_scope().client if client is not None: return client From 6cb1fadc06ad02de048d750bb58add06a25c85c3 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 18 Dec 2023 16:43:24 +0100 Subject: [PATCH 11/16] Try to break recurson --- sentry_sdk/debug.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/debug.py b/sentry_sdk/debug.py index fe8ae50cea..f25e22be6b 100644 --- a/sentry_sdk/debug.py +++ b/sentry_sdk/debug.py @@ -2,6 +2,7 @@ import logging from sentry_sdk import utils +from sentry_sdk.client import Client from sentry_sdk.hub import Hub from sentry_sdk.utils import logger from sentry_sdk.client import _client_init_debug @@ -13,10 +14,8 @@ def filter(self, record): # type: (LogRecord) -> bool if _client_init_debug.get(False): return True - hub = Hub.current - if hub is not None and hub.client is not None: - return hub.client.options["debug"] - return False + + return Client.get_client().options["debug"] def init_debug_support(): From 23e8b424e52c8631a7732b627a6aa6dd1647b803 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 07:58:18 +0100 Subject: [PATCH 12/16] Moved api to prevent ciruclar imports --- sentry_sdk/api.py | 30 +++++++++++++++++++++++------- sentry_sdk/client.py | 34 +++++++++------------------------- sentry_sdk/debug.py | 6 +++--- sentry_sdk/hub.py | 7 ++++--- sentry_sdk/scope.py | 28 ++++++++++++++-------------- 5 files changed, 53 insertions(+), 52 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 91d1692aca..c42276bbe6 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -88,6 +88,28 @@ def clientemethod(f): return f +def get_client(): + # type: () -> Union[Client, NoopClient] + """ + Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. + + .. versionadded:: 1.XX.0 + """ + client = Scope.get_current_scope().client + if client is not None: + return client + + client = Scope.get_isolation_scope().client + if client is not None: + return client + + client = Scope.get_global_scope().client + if client is not None: + return client + + return NoopClient() + + def sentry_is_initialized(): # type: () -> bool """ @@ -96,19 +118,13 @@ def sentry_is_initialized(): .. versionadded:: 1.XX.0 """ - client = Client.get_client() + client = get_client() if client.__class__ == NoopClient: return False else: return True -@clientemethod -def get_client(): - # type: () -> Union[Client, NoopClient] - return Client.get_client() - - @scopemethod def get_current_scope(): # type: () -> Scope diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index df0a9157f1..d931e11685 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -157,7 +157,7 @@ class NoopClient: .. versionadded:: 1.XX.0 """ - options = _get_options() # type: Dict[str, Any] + options = _get_options() # type: Dict[str, Any] metrics_aggregator = None # type: Optional[Any] monitor = None # type: Optional[Any] transport = None # type: Optional[Any] @@ -171,6 +171,10 @@ def should_send_default_pii(self): # type: () -> bool return False + def is_active(self): + # type: () -> bool + return False + def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None return None @@ -269,30 +273,6 @@ def __setstate__(self, state): self.options = state["options"] self._init_impl() - @classmethod - def get_client(cls): - # type: () -> NoopClient - """ - Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. - - .. versionadded:: 1.XX.0 - """ - from sentry_sdk.scope import Scope - - client = Scope.get_current_scope().client - if client is not None: - return client - - client = Scope.get_isolation_scope().client - if client is not None: - return client - - client = Scope.get_global_scope().client - if client is not None: - return client - - return NoopClient() - def _setup_instrumentation(self, functions_to_trace): # type: (Sequence[Dict[str, str]]) -> None """ @@ -415,6 +395,10 @@ def _capture_envelope(envelope): self._setup_instrumentation(self.options.get("functions_to_trace", [])) + def is_active(self): + # type: () -> bool + return True + @property def dsn(self): # type: () -> Optional[str] diff --git a/sentry_sdk/debug.py b/sentry_sdk/debug.py index f25e22be6b..257fa6e75c 100644 --- a/sentry_sdk/debug.py +++ b/sentry_sdk/debug.py @@ -2,7 +2,7 @@ import logging from sentry_sdk import utils -from sentry_sdk.client import Client +from sentry_sdk.api import get_client from sentry_sdk.hub import Hub from sentry_sdk.utils import logger from sentry_sdk.client import _client_init_debug @@ -14,8 +14,8 @@ def filter(self, record): # type: (LogRecord) -> bool if _client_init_debug.get(False): return True - - return Client.get_client().options["debug"] + + return get_client().options["debug"] def init_debug_support(): diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 4c7f7de166..8980e428b8 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -3,6 +3,7 @@ from contextlib import contextmanager +from sentry_sdk.api import get_client from sentry_sdk._compat import with_metaclass from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.scope import Scope @@ -266,13 +267,13 @@ def get_integration( If the return value is not `None` the hub is guaranteed to have a client attached. """ - Client.get_client().get_integration(name_or_class) + get_client().get_integration(name_or_class) @property def client(self): # type: () -> Optional[NoopClient] """Returns the current client on the hub.""" - return Client.get_client() + return get_client() @property def scope(self): @@ -610,7 +611,7 @@ def flush( """ Alias for :py:meth:`sentry_sdk.Client.flush` """ - return Client.get_client().flush(timeout=timeout, callback=callback) + return get_client().flush(timeout=timeout, callback=callback) def get_traceparent(self): # type: () -> Optional[str] diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 6352089a28..48812eaa21 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -6,9 +6,9 @@ import sys import uuid +from sentry_sdk.api import get_client from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow -from sentry_sdk.client import Client, NoopClient from sentry_sdk.consts import FALSE_VALUES, INSTRUMENTER from sentry_sdk._functools import wraps from sentry_sdk.profiler import Profile @@ -417,7 +417,7 @@ def get_traceparent(self, *args, **kwargs): Returns the Sentry "sentry-trace" header (aka the traceparent) from the currently active span or the scopes Propagation Context. """ - client = Client.get_client() + client = get_client() # If we have an active span, return traceparent from there if ( @@ -438,7 +438,7 @@ def get_traceparent(self, *args, **kwargs): def get_baggage(self, *args, **kwargs): # type: (Any, Any) -> Optional[Baggage] - client = Client.get_client() + client = get_client() # If we have an active span, return baggage from there if ( @@ -528,7 +528,7 @@ def iter_trace_propagation_headers(self, *args, **kwargs): from the span representing the request, if available, or the current span on the scope if not. """ - client = Client.get_client() + client = get_client() if not client.options.get("propagate_traces"): return @@ -766,14 +766,14 @@ 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. """ - client = Client.get_client() + client = get_client() - if client.__class__ == NoopClient: + if not client.is_active(): logger.info("Dropped breadcrumb because no client bound") return - before_breadcrumb = client.options.get("before_breadcrumb") - max_breadcrumbs = client.options.get("max_breadcrumbs") + before_breadcrumb = client.options["before_breadcrumb"] + max_breadcrumbs = client.options["max_breadcrumbs"] crumb = dict(crumb or ()) # type: Breadcrumb crumb.update(kwargs) @@ -825,7 +825,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): if scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return Client.get_client().capture_event(event=event, hint=hint, scope=scope) + return get_client().capture_event(event=event, hint=hint, scope=scope) def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] @@ -880,7 +880,7 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): exc_info = sys.exc_info() event, hint = event_from_exception( - exc_info, client_options=Client.get_client().options + exc_info, client_options=get_client().options ) try: @@ -931,7 +931,7 @@ def start_transaction( For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. """ hub = kwargs.pop("hub", None) - client = Client.get_client() + client = get_client() configuration_instrumenter = client.options.get("instrumenter") @@ -984,7 +984,7 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - client = Client.get_client() + client = get_client() configuration_instrumenter = client.options.get("instrumenter") @@ -1056,7 +1056,7 @@ def start_session(self, *args, **kwargs): self.end_session() - client = Client.get_client() + client = get_client() self._session = Session( release=client.options.get("release"), environment=client.options.get("environment"), @@ -1072,7 +1072,7 @@ def end_session(self, *args, **kwargs): if session is not None: session.close() - Client.get_client().capture_session(session) + get_client().capture_session(session) def stop_auto_session_tracking(self, *args, **kwargs): # type: (*Any, **Any) -> None From b9df63ff8e88cfbcef49a1b83c40589a33195896 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 10:05:25 +0100 Subject: [PATCH 13/16] Move get_client into scope to prevent circular imports --- sentry_sdk/api.py | 33 +++----------------------------- sentry_sdk/hub.py | 7 +++---- sentry_sdk/scope.py | 46 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index c42276bbe6..afa8e362cf 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -79,35 +79,12 @@ def scopemethod(f): return f -def clientemethod(f): - # type: (F) -> F - f.__doc__ = "%s\n\n%s" % ( - "Alias for :py:meth:`sentry_sdk._Client.%s`" % f.__name__, - inspect.getdoc(getattr(Client, f.__name__)), - ) - return f - - def get_client(): # type: () -> Union[Client, NoopClient] """ - Returns the current :py:class:`sentry_sdk.Client`. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. - - .. versionadded:: 1.XX.0 + Returns the current client. If no client is bound, returns a noop client. """ - client = Scope.get_current_scope().client - if client is not None: - return client - - client = Scope.get_isolation_scope().client - if client is not None: - return client - - client = Scope.get_global_scope().client - if client is not None: - return client - - return NoopClient() + return Scope.get_active_client() def sentry_is_initialized(): @@ -118,11 +95,7 @@ def sentry_is_initialized(): .. versionadded:: 1.XX.0 """ - client = get_client() - if client.__class__ == NoopClient: - return False - else: - return True + return Scope.get_active_client().is_active() @scopemethod diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 8980e428b8..c9e404831c 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -3,7 +3,6 @@ from contextlib import contextmanager -from sentry_sdk.api import get_client from sentry_sdk._compat import with_metaclass from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.scope import Scope @@ -267,13 +266,13 @@ def get_integration( If the return value is not `None` the hub is guaranteed to have a client attached. """ - get_client().get_integration(name_or_class) + Scope.get_active_client().get_integration(name_or_class) @property def client(self): # type: () -> Optional[NoopClient] """Returns the current client on the hub.""" - return get_client() + return Scope.get_active_client() @property def scope(self): @@ -611,7 +610,7 @@ def flush( """ Alias for :py:meth:`sentry_sdk.Client.flush` """ - return get_client().flush(timeout=timeout, callback=callback) + return Scope.get_active_client().flush(timeout=timeout, callback=callback) def get_traceparent(self): # type: () -> Optional[str] diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 48812eaa21..9239cfe9bd 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -6,9 +6,9 @@ import sys import uuid -from sentry_sdk.api import get_client from sentry_sdk.attachments import Attachment from sentry_sdk._compat import datetime_utcnow +from sentry_sdk.client import NoopClient from sentry_sdk.consts import FALSE_VALUES, INSTRUMENTER from sentry_sdk._functools import wraps from sentry_sdk.profiler import Profile @@ -252,6 +252,30 @@ def get_global_scope(cls): return SENTRY_GLOBAL_SCOPE + @classmethod + def get_active_client(cls): + # type: () -> Union[sentry_sdk.Client, NoopClient] + """ + Returns the current :py:class:`sentry_sdk.Client`. + This checks the current scope, the isolation scope and the global scope for a client. + If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. + + .. versionadded:: 1.XX.0 + """ + client = Scope.get_current_scope().client + if client is not None: + return client + + client = Scope.get_isolation_scope().client + if client is not None: + return client + + client = Scope.get_global_scope().client + if client is not None: + return client + + return NoopClient() + @property def is_forked(self): # type: () -> bool @@ -417,7 +441,7 @@ def get_traceparent(self, *args, **kwargs): Returns the Sentry "sentry-trace" header (aka the traceparent) from the currently active span or the scopes Propagation Context. """ - client = get_client() + client = Scope.get_active_client() # If we have an active span, return traceparent from there if ( @@ -438,7 +462,7 @@ def get_traceparent(self, *args, **kwargs): def get_baggage(self, *args, **kwargs): # type: (Any, Any) -> Optional[Baggage] - client = get_client() + client = Scope.get_active_client() # If we have an active span, return baggage from there if ( @@ -528,7 +552,7 @@ def iter_trace_propagation_headers(self, *args, **kwargs): from the span representing the request, if available, or the current span on the scope if not. """ - client = get_client() + client = Scope.get_active_client() if not client.options.get("propagate_traces"): return @@ -766,7 +790,7 @@ 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. """ - client = get_client() + client = Scope.get_active_client() if not client.is_active(): logger.info("Dropped breadcrumb because no client bound") @@ -825,7 +849,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): if scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return get_client().capture_event(event=event, hint=hint, scope=scope) + return Scope.get_active_client().capture_event(event=event, hint=hint, scope=scope) def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] @@ -880,7 +904,7 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): exc_info = sys.exc_info() event, hint = event_from_exception( - exc_info, client_options=get_client().options + exc_info, client_options=Scope.get_active_client().options ) try: @@ -931,7 +955,7 @@ def start_transaction( For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. """ hub = kwargs.pop("hub", None) - client = get_client() + client = Scope.get_active_client() configuration_instrumenter = client.options.get("instrumenter") @@ -984,7 +1008,7 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - client = get_client() + client = Scope.get_active_client() configuration_instrumenter = client.options.get("instrumenter") @@ -1056,7 +1080,7 @@ def start_session(self, *args, **kwargs): self.end_session() - client = get_client() + client = Scope.get_active_client() self._session = Session( release=client.options.get("release"), environment=client.options.get("environment"), @@ -1072,7 +1096,7 @@ def end_session(self, *args, **kwargs): if session is not None: session.close() - get_client().capture_session(session) + Scope.get_active_client().capture_session(session) def stop_auto_session_tracking(self, *args, **kwargs): # type: (*Any, **Any) -> None From 099a94a7cdf564e71942f0964cf4f0c66e68138b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 10:14:08 +0100 Subject: [PATCH 14/16] Named it get_client, its better --- sentry_sdk/hub.py | 4 ++-- sentry_sdk/scope.py | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index c9e404831c..15b39dd6fa 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -266,13 +266,13 @@ def get_integration( If the return value is not `None` the hub is guaranteed to have a client attached. """ - Scope.get_active_client().get_integration(name_or_class) + Scope.get_client().get_integration(name_or_class) @property def client(self): # type: () -> Optional[NoopClient] """Returns the current client on the hub.""" - return Scope.get_active_client() + return Scope.get_client() @property def scope(self): diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 9239cfe9bd..b736983e2d 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -253,10 +253,10 @@ def get_global_scope(cls): return SENTRY_GLOBAL_SCOPE @classmethod - def get_active_client(cls): + def get_client(cls): # type: () -> Union[sentry_sdk.Client, NoopClient] """ - Returns the current :py:class:`sentry_sdk.Client`. + Returns the currently used :py:class:`sentry_sdk.Client`. This checks the current scope, the isolation scope and the global scope for a client. If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned. @@ -441,7 +441,7 @@ def get_traceparent(self, *args, **kwargs): Returns the Sentry "sentry-trace" header (aka the traceparent) from the currently active span or the scopes Propagation Context. """ - client = Scope.get_active_client() + client = Scope.get_client() # If we have an active span, return traceparent from there if ( @@ -462,7 +462,7 @@ def get_traceparent(self, *args, **kwargs): def get_baggage(self, *args, **kwargs): # type: (Any, Any) -> Optional[Baggage] - client = Scope.get_active_client() + client = Scope.get_client() # If we have an active span, return baggage from there if ( @@ -552,7 +552,7 @@ def iter_trace_propagation_headers(self, *args, **kwargs): from the span representing the request, if available, or the current span on the scope if not. """ - client = Scope.get_active_client() + client = Scope.get_client() if not client.options.get("propagate_traces"): return @@ -790,7 +790,7 @@ 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. """ - client = Scope.get_active_client() + client = Scope.get_client() if not client.is_active(): logger.info("Dropped breadcrumb because no client bound") @@ -849,7 +849,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs): if scope_kwargs is not None: scope = _merge_scopes(self, scope, scope_kwargs) - return Scope.get_active_client().capture_event(event=event, hint=hint, scope=scope) + return Scope.get_client().capture_event(event=event, hint=hint, scope=scope) def capture_message(self, message, level=None, scope=None, **scope_kwargs): # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str] @@ -904,7 +904,7 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs): exc_info = sys.exc_info() event, hint = event_from_exception( - exc_info, client_options=Scope.get_active_client().options + exc_info, client_options=Scope.get_client().options ) try: @@ -955,7 +955,7 @@ def start_transaction( For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. """ hub = kwargs.pop("hub", None) - client = Scope.get_active_client() + client = Scope.get_client() configuration_instrumenter = client.options.get("instrumenter") @@ -1008,7 +1008,7 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs): For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. """ - client = Scope.get_active_client() + client = Scope.get_client() configuration_instrumenter = client.options.get("instrumenter") @@ -1080,7 +1080,7 @@ def start_session(self, *args, **kwargs): self.end_session() - client = Scope.get_active_client() + client = Scope.get_client() self._session = Session( release=client.options.get("release"), environment=client.options.get("environment"), @@ -1096,7 +1096,7 @@ def end_session(self, *args, **kwargs): if session is not None: session.close() - Scope.get_active_client().capture_session(session) + Scope.get_client().capture_session(session) def stop_auto_session_tracking(self, *args, **kwargs): # type: (*Any, **Any) -> None From 905f29c4cae51fd1276fd6b894a68090c4083fe4 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 10:15:37 +0100 Subject: [PATCH 15/16] Named it get_client, its better --- sentry_sdk/api.py | 4 ++-- sentry_sdk/hub.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index afa8e362cf..61d4464deb 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -84,7 +84,7 @@ def get_client(): """ Returns the current client. If no client is bound, returns a noop client. """ - return Scope.get_active_client() + return Scope.get_client() def sentry_is_initialized(): @@ -95,7 +95,7 @@ def sentry_is_initialized(): .. versionadded:: 1.XX.0 """ - return Scope.get_active_client().is_active() + return Scope.get_client().is_active() @scopemethod diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 15b39dd6fa..90a87c8890 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -610,7 +610,7 @@ def flush( """ Alias for :py:meth:`sentry_sdk.Client.flush` """ - return Scope.get_active_client().flush(timeout=timeout, callback=callback) + return Scope.get_client().flush(timeout=timeout, callback=callback) def get_traceparent(self): # type: () -> Optional[str] From 8c5d3bbb660588070565cab310dbe71072d39c31 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Tue, 19 Dec 2023 11:11:53 +0100 Subject: [PATCH 16/16] Cleanup --- sentry_sdk/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 531e125767..26cb87c3ef 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -2,7 +2,6 @@ from sentry_sdk import scope from sentry_sdk._types import TYPE_CHECKING -from sentry_sdk.client import Client, NoopClient from sentry_sdk.hub import Hub from sentry_sdk.scope import Scope from sentry_sdk.tracing import NoOpSpan, Transaction