Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions examples/basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sentry_sdk
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
from sentry_sdk.integrations.atexit import AtexitIntegration
from sentry_sdk.integrations.dedupe import DedupeIntegration
from sentry_sdk.integrations.stdlib import StdlibIntegration


sentry_sdk.init(
dsn="https://<key>@sentry.io/<project>",
default_integrations=False,
integrations=[
ExcepthookIntegration(),
AtexitIntegration(),
DedupeIntegration(),
StdlibIntegration(),
],
environment="Production",
release="1.0.0",
send_default_pii=False,
max_breadcrumbs=5,
)

with sentry_sdk.push_scope() as scope:
scope.user = {"email": "john.doe@example.com"}
scope.set_tag("page_locale", "de-at")
scope.set_extra("request", {"id": "d5cf8a0fd85c494b9c6453c4fba8ab17"})
scope.level = "warning"
sentry_sdk.capture_message("Something went wrong!")

sentry_sdk.add_breadcrumb(category="auth", message="Authenticated user", level="info")

try:
1 / 0
except Exception as e:
sentry_sdk.capture_exception(e)
35 changes: 35 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
[mypy]
allow_redefinition = True
check_untyped_defs = True
; disallow_any_decorated = True
; disallow_any_explicit = True
; disallow_any_expr = True
disallow_any_generics = True
; disallow_any_unimported = True
disallow_incomplete_defs = True
; disallow_subclassing_any = True
; disallow_untyped_calls = True
disallow_untyped_decorators = True
; disallow_untyped_defs = True
no_implicit_optional = True
strict_equality = True
strict_optional = True
warn_redundant_casts = True
; warn_return_any = True
; warn_unused_configs = True
; warn_unused_ignores = True

[mypy-sentry_sdk.integrations.*]
disallow_any_generics = False

[mypy-sentry_sdk.utils]
disallow_any_generics = False

[mypy-django.*]
ignore_missing_imports = True
[mypy-pyramid.*]
ignore_missing_imports = True
[mypy-psycopg2.*]
ignore_missing_imports = True
[mypy-pytest.*]
ignore_missing_imports = True
[mypy-aiohttp.*]
ignore_missing_imports = True
[mypy-sanic.*]
ignore_missing_imports = True
[mypy-tornado.*]
ignore_missing_imports = True
[mypy-fakeredis.*]
ignore_missing_imports = True
[mypy-rq.*]
ignore_missing_imports = True
31 changes: 20 additions & 11 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,42 @@
from typing import Optional
from typing import overload
from typing import Callable
from typing import Dict
from typing import TypeVar
from contextlib import ContextManager

from sentry_sdk.utils import Event, Hint, Breadcrumb, BreadcrumbHint

F = TypeVar("F", bound=Callable[..., Any])
else:

def overload(x):
return x


__all__ = []


def public(f):
__all__.append(f.__name__)
return f
__all__ = [
"capture_event",
"capture_message",
"capture_exception",
"add_breadcrumb",
"configure_scope",
"push_scope",
"flush",
"last_event_id",
]


def hubmethod(f):
# type: (F) -> F
f.__doc__ = "%s\n\n%s" % (
"Alias for `Hub.%s`" % f.__name__,
inspect.getdoc(getattr(Hub, f.__name__)),
)
return public(f)
return f


@hubmethod
def capture_event(event, hint=None):
# type: (Dict[str, Any], Dict[str, Any]) -> Optional[str]
# type: (Event, Optional[Hint]) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_event(event, hint)
Expand All @@ -45,7 +54,7 @@ def capture_event(event, hint=None):

@hubmethod
def capture_message(message, level=None):
# type: (str, Optional[Any]) -> Optional[str]
# type: (str, Optional[str]) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_message(message, level)
Expand All @@ -63,7 +72,7 @@ def capture_exception(error=None):

@hubmethod
def add_breadcrumb(crumb=None, hint=None, **kwargs):
# type: (Dict[str, Any], Dict[str, Any], **Any) -> None
# type: (Optional[Breadcrumb], Optional[BreadcrumbHint], **Any) -> None
hub = Hub.current
if hub is not None:
return hub.add_breadcrumb(crumb, hint, **kwargs)
Expand Down
17 changes: 9 additions & 8 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_options(*args, **kwargs):
for key, value in iteritems(options):
if key not in rv:
raise TypeError("Unknown option %r" % (key,))
rv[key] = value # type: ignore
rv[key] = value

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

if scope is not None:
event = scope.apply_to_event(event, hint)
if event is None:
return
event_ = scope.apply_to_event(event, hint)
if event_ is None:
return None
event = event_

if (
self.options["attach_stacktrace"]
Expand Down Expand Up @@ -178,7 +179,7 @@ def _is_ignored_error(self, event, hint):
if errcls == full_name or errcls == type_name:
return True
else:
if issubclass(exc_info[0], errcls):
if issubclass(exc_info[0], errcls): # type: ignore
return True

return False
Expand All @@ -187,7 +188,7 @@ def _should_capture(
self,
event, # type: Event
hint, # type: Hint
scope=None, # type: Scope
scope=None, # type: Optional[Scope]
):
# type: (...) -> bool
if scope is not None and not scope._should_capture:
Expand All @@ -205,7 +206,7 @@ def _should_capture(
return True

def capture_event(self, event, hint=None, scope=None):
# type: (Dict[str, Any], Any, Scope) -> Optional[str]
# type: (Dict[str, Any], Optional[Any], Optional[Scope]) -> Optional[str]
"""Captures an event.

This takes the ready made event and an optional hint and scope. The
Expand All @@ -225,7 +226,7 @@ def capture_event(self, event, hint=None, scope=None):
event["event_id"] = rv = uuid.uuid4().hex
if not self._should_capture(event, hint, scope):
return None
event = self._prepare_event(event, hint, scope) # type: ignore
event = self._prepare_event(event, hint, scope)
if event is None:
return None
self.transport.capture_event(event)
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"send_default_pii": bool,
"http_proxy": Optional[str],
"https_proxy": Optional[str],
"ignore_errors": List[type],
"ignore_errors": List[Union[type, str]],
"request_bodies": str,
"before_send": Optional[EventProcessor],
"before_breadcrumb": Optional[BreadcrumbProcessor],
Expand Down
3 changes: 3 additions & 0 deletions sentry_sdk/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ def filter(self, record):


def init_debug_support():
# type: () -> None
if not logger.handlers:
configure_logger()
configure_debug_hub()


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


def configure_debug_hub():
# type: () -> None
def _get_debug_hub():
return Hub.current

Expand Down
32 changes: 22 additions & 10 deletions sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

if False:
from contextlib import ContextManager
from sys import _OptExcInfo

from typing import Union
from typing import Any
Expand All @@ -26,6 +27,7 @@
from typing import List
from typing import Callable
from typing import Generator
from typing import Type
from typing import overload

from sentry_sdk.integrations import Integration
Expand All @@ -36,8 +38,8 @@ def overload(x):
return x


_local = ContextVar("sentry_current_hub")
_initial_client = None
_local = ContextVar("sentry_current_hub") # type: ignore
_initial_client = None # type: Optional[weakref.ReferenceType[Client]]


def _should_send_default_pii():
Expand All @@ -50,9 +52,11 @@ def _should_send_default_pii():

class _InitGuard(object):
def __init__(self, client):
# type: (Client) -> None
self._client = client

def __enter__(self):
# type: () -> _InitGuard
return self

def __exit__(self, exc_type, exc_value, tb):
Expand Down Expand Up @@ -103,6 +107,7 @@ def __exit__(self, exc_type, exc_value, tb):

class _ScopeManager(object):
def __init__(self, hub):
# type: (Hub) -> None
self._hub = hub
self._original_len = len(hub._stack)
self._layer = hub._stack[-1]
Expand Down Expand Up @@ -157,8 +162,13 @@ class Hub(with_metaclass(HubMeta)): # type: ignore

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

# Mypy doesn't pick up on the metaclass.
if False:
current = None # type: Hub
main = None # type: Hub

def __init__(self, client_or_hub=None, scope=None):
# type: (Union[Hub, Client], Optional[Any]) -> None
# type: (Optional[Union[Hub, Client]], Optional[Any]) -> None
if isinstance(client_or_hub, Hub):
hub = client_or_hub
client, other_scope = hub._stack[-1]
Expand Down Expand Up @@ -197,7 +207,7 @@ def run(self, callback):
return callback()

def get_integration(self, name_or_class):
# type: (Union[str, Integration]) -> Any
# type: (Union[str, Type[Integration]]) -> Any
"""Returns the integration for this hub by name or class. If there
is no client bound or the client does not have that integration
then `None` is returned.
Expand All @@ -218,14 +228,15 @@ def get_integration(self, name_or_class):
if rv is not None:
return rv

initial_client = _initial_client
if initial_client is not None:
initial_client = initial_client()
if _initial_client is not None:
initial_client = _initial_client()
else:
initial_client = None

if (
initial_client is not None
and initial_client is not client
and initial_client.integrations.get(name_or_class) is not None
and initial_client.integrations.get(integration_name) is not None
):
warning = (
"Integration %r attempted to run but it was only "
Expand Down Expand Up @@ -254,7 +265,7 @@ def bind_client(self, new):
self._stack[-1] = (new, top[1])

def capture_event(self, event, hint=None):
# type: (Event, Hint) -> Optional[str]
# type: (Event, Optional[Hint]) -> Optional[str]
"""Captures an event. The return value is the ID of the event.

The event is a dictionary following the Sentry v7/v8 protocol
Expand Down Expand Up @@ -306,9 +317,10 @@ def capture_exception(self, error=None):
return None

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

def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
# type: (Optional[Breadcrumb], Optional[BreadcrumbHint], **Any) -> None
Expand Down
7 changes: 3 additions & 4 deletions sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ def iter_default_integrations():

if isinstance(iter_default_integrations.__doc__, str):
for import_string in import_strings:
iter_default_integrations.__doc__ += "\n- `{}`".format( # type: ignore
import_string
)
iter_default_integrations.__doc__ += "\n- `{}`".format(import_string)

return iter_default_integrations

Expand Down Expand Up @@ -72,7 +70,7 @@ def setup_integrations(integrations, with_defaults=True):
instance = integration_cls()
integrations[instance.identifier] = instance

for identifier, integration in iteritems(integrations):
for identifier, integration in iteritems(integrations): # type: ignore
with _installer_lock:
if identifier not in _installed_integrations:
logger.debug(
Expand Down Expand Up @@ -113,6 +111,7 @@ class Integration(object):

@staticmethod
def setup_once():
# type: () -> None
"""
Initialize the integration.

Expand Down
Loading