Skip to content

Commit

Permalink
feat(scopes): Explicit scopes (#633)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Mar 11, 2020
1 parent 4112000 commit e680a75
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 7 deletions.
12 changes: 9 additions & 3 deletions sentry_sdk/api.py
Expand Up @@ -67,34 +67,40 @@ def scopemethod(f):
def capture_event(
event, # type: Event
hint=None, # type: Optional[Hint]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_event(event, hint)
return hub.capture_event(event, hint, scope=scope, **scope_args)
return None


@hubmethod
def capture_message(
message, # type: str
level=None, # type: Optional[str]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_message(message, level)
return hub.capture_message(message, level, scope=scope, **scope_args)
return None


@hubmethod
def capture_exception(
error=None, # type: Optional[BaseException]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_exception(error)
return hub.capture_exception(error, scope=scope, **scope_args)
return None


Expand Down
37 changes: 33 additions & 4 deletions sentry_sdk/hub.py
Expand Up @@ -23,6 +23,7 @@
from typing import Any
from typing import Optional
from typing import Tuple
from typing import Dict
from typing import List
from typing import Callable
from typing import Generator
Expand All @@ -47,6 +48,24 @@ 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 @@ -285,11 +304,14 @@ def capture_event(
self,
event, # type: Event
hint=None, # type: Optional[Hint]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
"""Captures an event. Alias of :py:meth:`sentry_sdk.Client.capture_event`.
"""
client, scope = self._stack[-1]
client, top_scope = self._stack[-1]
scope = _update_scope(top_scope, scope, scope_args)
if client is not None:
rv = client.capture_event(event, hint, scope)
if rv is not None:
Expand All @@ -301,6 +323,8 @@ def capture_message(
self,
message, # type: str
level=None, # type: Optional[str]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
"""Captures a message. The message is just a string. If no level
Expand All @@ -312,10 +336,15 @@ def capture_message(
return None
if level is None:
level = "info"
return self.capture_event({"message": message, "level": level})
return self.capture_event(
{"message": message, "level": level}, scope=scope, **scope_args
)

def capture_exception(
self, error=None # type: Optional[Union[BaseException, ExcInfo]]
self,
error=None, # type: Optional[Union[BaseException, ExcInfo]]
scope=None, # type: Optional[Any]
**scope_args # type: Dict[str, Any]
):
# type: (...) -> Optional[str]
"""Captures an exception.
Expand All @@ -334,7 +363,7 @@ def capture_exception(

event, hint = event_from_exception(exc_info, client_options=client.options)
try:
return self.capture_event(event, hint=hint)
return self.capture_event(event, hint=hint, scope=scope, **scope_args)
except Exception:
self._capture_internal_exception(sys.exc_info())

Expand Down
44 changes: 44 additions & 0 deletions sentry_sdk/scope.py
Expand Up @@ -323,6 +323,50 @@ def _drop(event, cause, ty):

return event

def update_from_scope(self, scope):
# type: (Scope) -> None
if scope._level is not None:
self._level = scope._level
if scope._fingerprint is not None:
self._fingerprint = scope._fingerprint
if scope._transaction is not None:
self._transaction = scope._transaction
if scope._user is not None:
self._user = scope._user
if scope._tags:
self._tags.update(scope._tags)
if scope._contexts:
self._contexts.update(scope._contexts)
if scope._extras:
self._extras.update(scope._extras)
if scope._breadcrumbs:
self._breadcrumbs.extend(scope._breadcrumbs)
if scope._span:
self._span = scope._span

def update_from_kwargs(
self,
user=None, # type: Optional[Any]
level=None, # type: Optional[str]
extras=None, # type: Optional[Dict[str, Any]]
contexts=None, # type: Optional[Dict[str, Any]]
tags=None, # type: Optional[Dict[str, str]]
fingerprint=None, # type: Optional[List[str]]
):
# type: (...) -> None
if level is not None:
self._level = level
if user is not None:
self._user = user
if extras is not None:
self._extras.update(extras)
if contexts is not None:
self._contexts.update(contexts)
if tags is not None:
self._tags.update(tags)
if fingerprint is not None:
self._fingerprint = fingerprint

def __copy__(self):
# type: () -> Scope
rv = object.__new__(self.__class__) # type: Scope
Expand Down
47 changes: 47 additions & 0 deletions tests/test_scope.py
@@ -1,4 +1,5 @@
import copy
from sentry_sdk import capture_exception
from sentry_sdk.scope import Scope


Expand All @@ -15,3 +16,49 @@ def test_copying():
assert "bam" not in s2._tags

assert s1._fingerprint is s2._fingerprint


def test_merging(sentry_init, capture_events):
sentry_init()

s = Scope()
s.set_user({"id": 42})

events = capture_events()

capture_exception(NameError(), scope=s)

(event,) = events
assert event["user"] == {"id": 42}


def test_common_args():
s = Scope()
s.update_from_kwargs(
user={"id": 23},
level="warning",
extras={"k": "v"},
contexts={"os": {"name": "Blafasel"}},
tags={"x": "y"},
fingerprint=["foo"],
)

s2 = Scope()
s2.set_extra("foo", "bar")
s2.set_tag("a", "b")
s2.set_context("device", {"a": "b"})
s2.update_from_scope(s)

assert s._user == {"id": 23}
assert s._level == "warning"
assert s._extras == {"k": "v"}
assert s._contexts == {"os": {"name": "Blafasel"}}
assert s._tags == {"x": "y"}
assert s._fingerprint == ["foo"]

assert s._user == s2._user
assert s._level == s2._level
assert s._fingerprint == s2._fingerprint
assert s2._extras == {"k": "v", "foo": "bar"}
assert s2._tags == {"a": "b", "x": "y"}
assert s2._contexts == {"os": {"name": "Blafasel"}, "device": {"a": "b"}}

0 comments on commit e680a75

Please sign in to comment.