Skip to content

Commit 54a8111

Browse files
committed
Merge remote-tracking branch 'origin/master' into wip/sessiontracking
2 parents 51b72f7 + 8d669f6 commit 54a8111

32 files changed

Lines changed: 350 additions & 102 deletions

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.9.0
2+
3+
* The SDK now captures `SystemExit` and other `BaseException`s when coming from
4+
within a WSGI app (Flask, Django, ...)
5+
* Pyramid: No longer report an exception if there exists an exception view for
6+
it.
7+
18
## 0.8.1
29

310
* Fix infinite recursion bug in Celery integration.

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
include LICENSE
2+
include sentry_sdk/py.typed

examples/basic.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import sentry_sdk
2+
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
3+
from sentry_sdk.integrations.atexit import AtexitIntegration
4+
from sentry_sdk.integrations.dedupe import DedupeIntegration
5+
from sentry_sdk.integrations.stdlib import StdlibIntegration
6+
7+
8+
sentry_sdk.init(
9+
dsn="https://<key>@sentry.io/<project>",
10+
default_integrations=False,
11+
integrations=[
12+
ExcepthookIntegration(),
13+
AtexitIntegration(),
14+
DedupeIntegration(),
15+
StdlibIntegration(),
16+
],
17+
environment="Production",
18+
release="1.0.0",
19+
send_default_pii=False,
20+
max_breadcrumbs=5,
21+
)
22+
23+
with sentry_sdk.push_scope() as scope:
24+
scope.user = {"email": "john.doe@example.com"}
25+
scope.set_tag("page_locale", "de-at")
26+
scope.set_extra("request", {"id": "d5cf8a0fd85c494b9c6453c4fba8ab17"})
27+
scope.level = "warning"
28+
sentry_sdk.capture_message("Something went wrong!")
29+
30+
sentry_sdk.add_breadcrumb(category="auth", message="Authenticated user", level="info")
31+
32+
try:
33+
1 / 0
34+
except Exception as e:
35+
sentry_sdk.capture_exception(e)

mypy.ini

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,45 @@
11
[mypy]
22
allow_redefinition = True
3+
check_untyped_defs = True
4+
; disallow_any_decorated = True
5+
; disallow_any_explicit = True
6+
; disallow_any_expr = True
37
disallow_any_generics = True
8+
; disallow_any_unimported = True
9+
disallow_incomplete_defs = True
10+
; disallow_subclassing_any = True
11+
; disallow_untyped_calls = True
12+
disallow_untyped_decorators = True
13+
; disallow_untyped_defs = True
14+
no_implicit_optional = True
15+
strict_equality = True
16+
strict_optional = True
417
warn_redundant_casts = True
18+
; warn_return_any = True
19+
; warn_unused_configs = True
20+
; warn_unused_ignores = True
521

622
[mypy-sentry_sdk.integrations.*]
723
disallow_any_generics = False
824

925
[mypy-sentry_sdk.utils]
1026
disallow_any_generics = False
27+
28+
[mypy-django.*]
29+
ignore_missing_imports = True
30+
[mypy-pyramid.*]
31+
ignore_missing_imports = True
32+
[mypy-psycopg2.*]
33+
ignore_missing_imports = True
34+
[mypy-pytest.*]
35+
ignore_missing_imports = True
36+
[mypy-aiohttp.*]
37+
ignore_missing_imports = True
38+
[mypy-sanic.*]
39+
ignore_missing_imports = True
40+
[mypy-tornado.*]
41+
ignore_missing_imports = True
42+
[mypy-fakeredis.*]
43+
ignore_missing_imports = True
44+
[mypy-rq.*]
45+
ignore_missing_imports = True

sentry_sdk/api.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,42 @@
1010
from typing import Optional
1111
from typing import overload
1212
from typing import Callable
13-
from typing import Dict
13+
from typing import TypeVar
1414
from contextlib import ContextManager
15+
16+
from sentry_sdk.utils import Event, Hint, Breadcrumb, BreadcrumbHint
17+
18+
F = TypeVar("F", bound=Callable[..., Any])
1519
else:
1620

1721
def overload(x):
1822
return x
1923

2024

21-
__all__ = []
22-
23-
24-
def public(f):
25-
__all__.append(f.__name__)
26-
return f
25+
__all__ = [
26+
"capture_event",
27+
"capture_message",
28+
"capture_exception",
29+
"add_breadcrumb",
30+
"configure_scope",
31+
"push_scope",
32+
"flush",
33+
"last_event_id",
34+
]
2735

2836

2937
def hubmethod(f):
38+
# type: (F) -> F
3039
f.__doc__ = "%s\n\n%s" % (
3140
"Alias for `Hub.%s`" % f.__name__,
3241
inspect.getdoc(getattr(Hub, f.__name__)),
3342
)
34-
return public(f)
43+
return f
3544

3645

3746
@hubmethod
3847
def capture_event(event, hint=None):
39-
# type: (Dict[str, Any], Dict[str, Any]) -> Optional[str]
48+
# type: (Event, Optional[Hint]) -> Optional[str]
4049
hub = Hub.current
4150
if hub is not None:
4251
return hub.capture_event(event, hint)
@@ -45,7 +54,7 @@ def capture_event(event, hint=None):
4554

4655
@hubmethod
4756
def capture_message(message, level=None):
48-
# type: (str, Optional[Any]) -> Optional[str]
57+
# type: (str, Optional[str]) -> Optional[str]
4958
hub = Hub.current
5059
if hub is not None:
5160
return hub.capture_message(message, level)
@@ -63,7 +72,7 @@ def capture_exception(error=None):
6372

6473
@hubmethod
6574
def add_breadcrumb(crumb=None, hint=None, **kwargs):
66-
# type: (Dict[str, Any], Dict[str, Any], **Any) -> None
75+
# type: (Optional[Breadcrumb], Optional[BreadcrumbHint], **Any) -> None
6776
hub = Hub.current
6877
if hub is not None:
6978
return hub.add_breadcrumb(crumb, hint, **kwargs)

sentry_sdk/client.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def get_options(*args, **kwargs):
4747
for key, value in iteritems(options):
4848
if key not in rv:
4949
raise TypeError("Unknown option %r" % (key,))
50-
rv[key] = value # type: ignore
50+
rv[key] = value
5151

5252
if rv["dsn"] is None:
5353
rv["dsn"] = os.environ.get("SENTRY_DSN")
@@ -108,9 +108,10 @@ def _prepare_event(
108108
hint = dict(hint or ()) # type: Hint
109109

110110
if scope is not None:
111-
event = scope.apply_to_event(event, hint)
112-
if event is None:
113-
return
111+
event_ = scope.apply_to_event(event, hint)
112+
if event_ is None:
113+
return None
114+
event = event_
114115

115116
if (
116117
self.options["attach_stacktrace"]
@@ -178,7 +179,7 @@ def _is_ignored_error(self, event, hint):
178179
if errcls == full_name or errcls == type_name:
179180
return True
180181
else:
181-
if issubclass(exc_info[0], errcls):
182+
if issubclass(exc_info[0], errcls): # type: ignore
182183
return True
183184

184185
return False
@@ -187,7 +188,7 @@ def _should_capture(
187188
self,
188189
event, # type: Event
189190
hint, # type: Hint
190-
scope=None, # type: Scope
191+
scope=None, # type: Optional[Scope]
191192
):
192193
# type: (...) -> bool
193194
if scope is not None and not scope._should_capture:
@@ -205,10 +206,10 @@ def _should_capture(
205206
return True
206207

207208
def capture_event(self, event, hint=None, scope=None):
208-
# type: (Dict[str, Any], Any, Scope) -> Optional[str]
209+
# type: (Dict[str, Any], Optional[Any], Optional[Scope]) -> Optional[str]
209210
"""Captures an event.
210211
211-
This takes the ready made event and an optoinal hint and scope. The
212+
This takes the ready made event and an optional hint and scope. The
212213
hint is internally used to further customize the representation of the
213214
error. When provided it's a dictionary of optional information such
214215
as exception info.
@@ -225,7 +226,7 @@ def capture_event(self, event, hint=None, scope=None):
225226
event["event_id"] = rv = uuid.uuid4().hex
226227
if not self._should_capture(event, hint, scope):
227228
return None
228-
event = self._prepare_event(event, hint, scope) # type: ignore
229+
event = self._prepare_event(event, hint, scope)
229230
if event is None:
230231
return None
231232
self.transport.capture_event(event)

sentry_sdk/consts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"send_default_pii": bool,
3636
"http_proxy": Optional[str],
3737
"https_proxy": Optional[str],
38-
"ignore_errors": List[type],
38+
"ignore_errors": List[Union[type, str]],
3939
"request_bodies": str,
4040
"before_send": Optional[EventProcessor],
4141
"before_breadcrumb": Optional[BreadcrumbProcessor],
@@ -49,7 +49,7 @@
4949
)
5050

5151

52-
VERSION = "0.8.1"
52+
VERSION = "0.9.0"
5353
DEFAULT_SERVER_NAME = socket.gethostname() if hasattr(socket, "gethostname") else None
5454
DEFAULT_OPTIONS = {
5555
"dsn": None,

sentry_sdk/debug.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ def filter(self, record):
2020

2121

2222
def init_debug_support():
23+
# type: () -> None
2324
if not logger.handlers:
2425
configure_logger()
2526
configure_debug_hub()
2627

2728

2829
def configure_logger():
30+
# type: () -> None
2931
_handler = logging.StreamHandler(sys.stderr)
3032
_handler.setFormatter(logging.Formatter(" [sentry] %(levelname)s: %(message)s"))
3133
logger.addHandler(_handler)
@@ -34,6 +36,7 @@ def configure_logger():
3436

3537

3638
def configure_debug_hub():
39+
# type: () -> None
3740
def _get_debug_hub():
3841
return Hub.current
3942

sentry_sdk/hub.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
if False:
2323
from contextlib import ContextManager
24+
from sys import _OptExcInfo
2425

2526
from typing import Union
2627
from typing import Any
@@ -29,6 +30,7 @@
2930
from typing import List
3031
from typing import Callable
3132
from typing import Generator
33+
from typing import Type
3234
from typing import overload
3335

3436
from sentry_sdk.integrations import Integration
@@ -39,8 +41,8 @@ def overload(x):
3941
return x
4042

4143

42-
_local = ContextVar("sentry_current_hub")
43-
_initial_client = None
44+
_local = ContextVar("sentry_current_hub") # type: ignore
45+
_initial_client = None # type: Optional[weakref.ReferenceType[Client]]
4446

4547

4648
def _should_send_default_pii():
@@ -53,9 +55,11 @@ def _should_send_default_pii():
5355

5456
class _InitGuard(object):
5557
def __init__(self, client):
58+
# type: (Client) -> None
5659
self._client = client
5760

5861
def __enter__(self):
62+
# type: () -> _InitGuard
5963
return self
6064

6165
def __exit__(self, exc_type, exc_value, tb):
@@ -106,6 +110,7 @@ def __exit__(self, exc_type, exc_value, tb):
106110

107111
class _ScopeManager(object):
108112
def __init__(self, hub):
113+
# type: (Hub) -> None
109114
self._hub = hub
110115
self._original_len = len(hub._stack)
111116
self._layer = hub._stack[-1]
@@ -160,8 +165,13 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
160165

161166
_stack = None # type: List[Tuple[Optional[Client], Scope]]
162167

168+
# Mypy doesn't pick up on the metaclass.
169+
if False:
170+
current = None # type: Hub
171+
main = None # type: Hub
172+
163173
def __init__(self, client_or_hub=None, scope=None):
164-
# type: (Union[Hub, Client], Optional[Any]) -> None
174+
# type: (Optional[Union[Hub, Client]], Optional[Any]) -> None
165175
if isinstance(client_or_hub, Hub):
166176
hub = client_or_hub
167177
client, other_scope = hub._stack[-1]
@@ -200,7 +210,7 @@ def run(self, callback):
200210
return callback()
201211

202212
def get_integration(self, name_or_class):
203-
# type: (Union[str, Integration]) -> Any
213+
# type: (Union[str, Type[Integration]]) -> Any
204214
"""Returns the integration for this hub by name or class. If there
205215
is no client bound or the client does not have that integration
206216
then `None` is returned.
@@ -221,14 +231,15 @@ def get_integration(self, name_or_class):
221231
if rv is not None:
222232
return rv
223233

224-
initial_client = _initial_client
225-
if initial_client is not None:
226-
initial_client = initial_client()
234+
if _initial_client is not None:
235+
initial_client = _initial_client()
236+
else:
237+
initial_client = None
227238

228239
if (
229240
initial_client is not None
230241
and initial_client is not client
231-
and initial_client.integrations.get(name_or_class) is not None
242+
and initial_client.integrations.get(integration_name) is not None
232243
):
233244
warning = (
234245
"Integration %r attempted to run but it was only "
@@ -257,7 +268,7 @@ def bind_client(self, new):
257268
self._stack[-1] = (new, top[1])
258269

259270
def capture_event(self, event, hint=None):
260-
# type: (Event, Hint) -> Optional[str]
271+
# type: (Event, Optional[Hint]) -> Optional[str]
261272
"""Captures an event. The return value is the ID of the event.
262273
263274
The event is a dictionary following the Sentry v7/v8 protocol
@@ -309,9 +320,10 @@ def capture_exception(self, error=None):
309320
return None
310321

311322
def _capture_internal_exception(self, exc_info):
323+
# type: (_OptExcInfo) -> Any
312324
"""Capture an exception that is likely caused by a bug in the SDK
313325
itself."""
314-
logger.error("Internal error in sentry_sdk", exc_info=exc_info)
326+
logger.error("Internal error in sentry_sdk", exc_info=exc_info) # type: ignore
315327

316328
def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
317329
# type: (Optional[Breadcrumb], Optional[BreadcrumbHint], **Any) -> None

0 commit comments

Comments
 (0)