Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(2) Move capture_* from Hub to Client #2555

Merged
merged 32 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f2a4a3a
Moved get_integration from Hub to Client
antonpirker Nov 29, 2023
4869365
Moved add_breadcrumb from Hub to Scope
antonpirker Nov 29, 2023
e5f9e9d
Moved session functions from Hub to Scope
antonpirker Nov 29, 2023
a338099
Fixed import
antonpirker Nov 29, 2023
d900a1b
Fixed Python 2.7 syntax
antonpirker Nov 29, 2023
0afb3ab
Merge branch 'master' into antonpirker/refactor-hub
antonpirker Nov 30, 2023
fe77d04
Give the client to the scope function. Want to establish a pattern.
antonpirker Dec 1, 2023
4ba7dce
Merge branch 'antonpirker/refactor-hub' of github.com:getsentry/sentr…
antonpirker Dec 1, 2023
b15613a
Moved capture_* functions from Hub to Client
antonpirker Dec 1, 2023
8331bd0
Renamed scope_args to scope_kwargs
antonpirker Dec 1, 2023
ce1759f
oops
antonpirker Dec 1, 2023
9a08b6e
preserve old behavior when called from client directly (not from hub)
antonpirker Dec 1, 2023
f9b8d5a
update test
antonpirker Dec 1, 2023
d233ae5
Merge branch 'master' into antonpirker/refactor-hub-capture
antonpirker Dec 4, 2023
2932adc
Merge branch 'master' into antonpirker/refactor-hub
antonpirker Dec 4, 2023
3a7af73
Merge branch 'antonpirker/refactor-hub' into antonpirker/refactor-hub…
antonpirker Dec 4, 2023
345c419
Merge branch 'master' into antonpirker/refactor-hub-capture
sentrivana Dec 6, 2023
bb3250a
_update_scope belongs into the scope not the client
antonpirker Dec 11, 2023
3a22dd6
Made top_scope separate parameter
antonpirker Dec 11, 2023
c78b1f5
Hub calls capture_* on scope that calls it on client. Later with new …
antonpirker Dec 13, 2023
0901e72
Merge branch 'feat/new-scopes' into antonpirker/refactor-hub-capture
antonpirker Dec 13, 2023
269fd56
Moved everything to scope and only have capture_event in client
antonpirker Dec 13, 2023
0354065
small fix
antonpirker Dec 13, 2023
3d5825e
Updated test
antonpirker Dec 13, 2023
4e78d1f
Fixed test
antonpirker Dec 13, 2023
ba72216
Fixed test
antonpirker Dec 14, 2023
c5a7ae7
Fixed test for old python
antonpirker Dec 14, 2023
bbb93e6
Formatting
antonpirker Dec 14, 2023
12e632d
Passing the client as separate argument and updating api docs.
antonpirker Dec 14, 2023
eb56a12
ApiDocs
antonpirker Dec 14, 2023
074a9ef
Fix
antonpirker Dec 14, 2023
6733114
Remove condition from _merge_scope (also not present in current imple…
antonpirker Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,31 @@ def capture_event(
event, # type: Event
hint=None, # type: Optional[Hint]
scope=None, # type: Optional[Any]
**scope_args # type: Any
**scope_kwargs # 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_kwargs)


@hubmethod
def capture_message(
message, # type: str
level=None, # type: Optional[str]
scope=None, # type: Optional[Any]
**scope_args # type: Any
**scope_kwargs # 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_kwargs)


@hubmethod
def capture_exception(
error=None, # type: Optional[Union[BaseException, ExcInfo]]
scope=None, # type: Optional[Any]
**scope_args # type: Any
**scope_kwargs # type: Any
):
# type: (...) -> Optional[str]
return Hub.current.capture_exception(error, scope=scope, **scope_args)
return Hub.current.capture_exception(error, scope=scope, **scope_kwargs)


@hubmethod
Expand Down
8 changes: 6 additions & 2 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,12 @@ 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`.
antonpirker marked this conversation as resolved.
Show resolved Hide resolved

: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.
"""
Expand Down
96 changes: 46 additions & 50 deletions sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
)

from sentry_sdk.utils import (
exc_info_from_error,
event_from_exception,
logger,
ContextVar,
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -322,76 +302,90 @@ 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_kwargs):
# 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_kwargs: For supported `**scope_kwargs` see
: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

def capture_message(self, message, level=None, scope=None, **scope_args):
scope_kwargs["client"] = client

last_event_id = top_scope.capture_event(
event, hint, scope=scope, **scope_kwargs
)

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_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_args: For supported `**scope_args` 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`).
"""
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_kwargs["client"] = client

last_event_id = top_scope.capture_message(
message, level=level, scope=scope, **scope_kwargs
)

def capture_exception(self, error=None, scope=None, **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_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_args: For supported `**scope_args` 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`).
"""
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_kwargs["client"] = client

return None
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

return last_event_id

def _capture_internal_exception(
self, exc_info # type: Any
Expand All @@ -401,6 +395,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)
Expand Down
120 changes: 118 additions & 2 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections import deque
from itertools import chain
import os
import sys
import uuid

from sentry_sdk.attachments import Attachment
Expand All @@ -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
Expand All @@ -37,12 +43,13 @@
from sentry_sdk._types import (
Breadcrumb,
BreadcrumbHint,
ErrorProcessor,
Event,
EventProcessor,
ErrorProcessor,
ExcInfo,
Hint,
Type,
Union,
)

from sentry_sdk.profiler import Profile
Expand Down Expand Up @@ -81,6 +88,28 @@ def wrapper(self, *args, **kwargs):
return wrapper # type: ignore


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")

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.
Expand Down Expand Up @@ -559,6 +588,93 @@ 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")

if scope_kwargs is not None:
antonpirker marked this conversation as resolved.
Show resolved Hide resolved
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]
"""
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`).
"""
if level is None:
level = "info"

# the client is in scope_kwargs

return self.capture_event(
{"message": message, "level": level}, scope=scope, **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`).
"""
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
)

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
"""Starts a new session."""
Expand Down
2 changes: 1 addition & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.scope._capture_internal_exception = raise_it
antonpirker marked this conversation as resolved.
Show resolved Hide resolved

def e(exc):
try:
Expand Down
Loading