Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ venv
.hypothesis
semaphore
pip-wheel-metadata
.mypy_cache
2 changes: 2 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mypy]
allow_redefinition = True
19 changes: 16 additions & 3 deletions sentry_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,25 @@
except Exception as e:
sentry_sdk.capture_exception(e)
"""
from sentry_sdk.hub import Hub, init
from sentry_sdk.scope import Scope
from sentry_sdk.transport import Transport, HttpTransport
from sentry_sdk.client import Client

from sentry_sdk.api import * # noqa
from sentry_sdk.api import __all__ # noqa
from sentry_sdk.api import __all__ as api_all

from sentry_sdk.consts import VERSION # noqa

# modules we consider public
__all__.append("integrations")
__all__ = api_all + [ # noqa
"Hub",
"Scope",
"Client",
"Transport",
"HttpTransport",
"init",
"integrations",
]

# Initialize the debug support after everything is loaded
from sentry_sdk.debug import init_debug_support
Expand Down
15 changes: 12 additions & 3 deletions sentry_sdk/_compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import sys

if False:
from typing import Optional
from typing import Tuple
from typing import Any
from typing import Type


PY2 = sys.version_info[0] == 2

Expand Down Expand Up @@ -27,8 +33,8 @@ def implements_str(cls):
import queue # noqa

text_type = str
string_types = (text_type,)
number_types = (int, float)
string_types = (text_type,) # type: Tuple[type]
number_types = (int, float) # type: Tuple[type, type]
int_types = (int,) # noqa
iteritems = lambda x: x.items()

Expand All @@ -39,6 +45,8 @@ def implements_str(x):
return x

def reraise(tp, value, tb=None):
# type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> None
assert value is not None
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
Expand All @@ -53,8 +61,9 @@ def __new__(cls, name, this_bases, d):


def check_thread_support():
# type: () -> None
try:
from uwsgi import opt
from uwsgi import opt # type: ignore
except ImportError:
return

Expand Down
57 changes: 50 additions & 7 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import inspect
from contextlib import contextmanager

from sentry_sdk.hub import Hub, init
from sentry_sdk.hub import Hub
from sentry_sdk.scope import Scope
from sentry_sdk.transport import Transport, HttpTransport
from sentry_sdk.client import Client


__all__ = ["Hub", "Scope", "Client", "Transport", "HttpTransport", "init"]
if False:
from typing import Any
from typing import Optional
from typing import overload
from typing import Callable
from contextlib import AbstractContextManager
else:

def overload(x):
return x

_initial_client = None

__all__ = []


def public(f):
Expand All @@ -35,16 +42,20 @@ def capture_event(event, hint=None):

@hubmethod
def capture_message(message, level=None):
# type: (str, Optional[Any]) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_message(message, level)
return None
Comment thread
untitaker marked this conversation as resolved.


@hubmethod
def capture_exception(error=None):
# type: (ValueError) -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.capture_exception(error)
return None


@hubmethod
Expand All @@ -54,7 +65,19 @@ def add_breadcrumb(*args, **kwargs):
return hub.add_breadcrumb(*args, **kwargs)


@hubmethod
@overload # noqa
def configure_scope():
# type: () -> AbstractContextManager
pass


@overload # noqa
def configure_scope(callback):
# type: (Callable) -> None
pass


@hubmethod # noqa
def configure_scope(callback=None):
hub = Hub.current
if hub is not None:
Expand All @@ -66,9 +89,24 @@ def inner():
yield Scope()

return inner()
else:
# returned if user provided callback
return None


@hubmethod
@overload # noqa
def push_scope():
# type: () -> AbstractContextManager
pass


@overload # noqa
def push_scope(callback):
# type: (Callable) -> None
pass


@hubmethod # noqa
def push_scope(callback=None):
hub = Hub.current
if hub is not None:
Expand All @@ -80,6 +118,9 @@ def inner():
yield Scope()

return inner()
else:
# returned if user provided callback
return None


@hubmethod
Expand All @@ -91,6 +132,8 @@ def flush(timeout=None, callback=None):

@hubmethod
def last_event_id():
# type: () -> Optional[str]
hub = Hub.current
if hub is not None:
return hub.last_event_id()
return None
53 changes: 38 additions & 15 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,39 @@
from sentry_sdk.integrations import setup_integrations
from sentry_sdk.utils import ContextVar

if False:
from sentry_sdk.consts import ClientOptions
from sentry_sdk.scope import Scope
from typing import Any
from typing import Dict
from typing import Optional


_client_init_debug = ContextVar("client_init_debug")


def get_options(*args, **kwargs):
# type: (*str, **ClientOptions) -> ClientOptions
if args and (isinstance(args[0], string_types) or args[0] is None):
dsn = args[0]
dsn = args[0] # type: Optional[str]
args = args[1:]
else:
dsn = None

rv = dict(DEFAULT_OPTIONS)
options = dict(*args, **kwargs)
options = dict(*args, **kwargs) # type: ignore
if dsn is not None and options.get("dsn") is None:
options["dsn"] = dsn
options["dsn"] = dsn # type: ignore

for key, value in options.items():
if key not in rv:
raise TypeError("Unknown option %r" % (key,))
rv[key] = value
rv[key] = value # type: ignore

if rv["dsn"] is None:
rv["dsn"] = os.environ.get("SENTRY_DSN")

return rv
return rv # type: ignore


class Client(object):
Expand All @@ -54,6 +62,7 @@ class Client(object):
"""

def __init__(self, *args, **kwargs):
# type: (*str, **ClientOptions) -> None
old_debug = _client_init_debug.get(False)
try:
self.options = options = get_options(*args, **kwargs)
Expand All @@ -79,7 +88,13 @@ def dsn(self):
"""Returns the configured DSN as string."""
return self.options["dsn"]

def _prepare_event(self, event, hint, scope):
def _prepare_event(
self,
event, # type: Dict[str, Any]
hint, # type: Optional[Dict[str, Any]]
scope, # type: Optional[Scope]
):
# type: (...) -> Optional[Dict[str, Any]]
if event.get("timestamp") is None:
event["timestamp"] = datetime.utcnow()

Expand All @@ -104,8 +119,8 @@ def _prepare_event(self, event, hint, scope):
]

for key in "release", "environment", "server_name", "dist":
if event.get(key) is None and self.options[key] is not None:
event[key] = text_type(self.options[key]).strip()
if event.get(key) is None and self.options[key] is not None: # type: ignore
event[key] = text_type(self.options[key]).strip() # type: ignore
if event.get("sdk") is None:
sdk_info = dict(SDK_INFO)
sdk_info["integrations"] = sorted(self.integrations.keys())
Expand All @@ -132,11 +147,12 @@ def _prepare_event(self, event, hint, scope):
new_event = before_send(event, hint)
if new_event is None:
logger.info("before send dropped event (%s)", event)
event = new_event
event = new_event # type: ignore

return event

def _is_ignored_error(self, event, hint=None):
def _is_ignored_error(self, event, hint):
# type: (Dict[str, Any], Dict[str, Any]) -> bool
exc_info = hint.get("exc_info")
if exc_info is None:
return False
Expand All @@ -156,7 +172,13 @@ def _is_ignored_error(self, event, hint=None):

return False

def _should_capture(self, event, hint, scope=None):
def _should_capture(
self,
event, # type: Dict[str, Any]
hint, # type: Dict[str, Any]
scope=None, # type: Scope
):
# type: (...) -> bool
if scope is not None and not scope._should_capture:
return False

Expand All @@ -172,6 +194,7 @@ def _should_capture(self, event, hint, scope=None):
return True

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

This takes the ready made event and an optoinal hint and scope. The
Expand All @@ -183,17 +206,17 @@ def capture_event(self, event, hint=None, scope=None):
value of this function will be the ID of the captured event.
"""
if self.transport is None:
return
return None
if hint is None:
hint = {}
rv = event.get("event_id")
if rv is None:
event["event_id"] = rv = uuid.uuid4().hex
if not self._should_capture(event, hint, scope):
return
event = self._prepare_event(event, hint, scope)
return None
event = self._prepare_event(event, hint, scope) # type: ignore
if event is None:
return
return None
self.transport.capture_event(event)
return rv

Expand Down
40 changes: 40 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
import socket

if False:
from mypy_extensions import TypedDict
from typing import Optional
from typing import Callable
from typing import Union
from typing import List

from sentry_sdk.transport import Transport
from sentry_sdk.integrations import Integration

ClientOptions = TypedDict(
"ClientOptions",
{
"dsn": Optional[str],
"with_locals": bool,
"max_breadcrumbs": int,
"release": Optional[str],
"environment": Optional[str],
"server_name": Optional[str],
"shutdown_timeout": int,
"integrations": List[Integration],
"in_app_include": List[str],
"in_app_exclude": List[str],
"default_integrations": bool,
"dist": Optional[str],
"transport": Optional[Union[Transport, type, Callable]],
"sample_rate": int,
"send_default_pii": bool,
"http_proxy": Optional[str],
"https_proxy": Optional[str],
"ignore_errors": List[type],
"request_bodies": str,
"before_send": Optional[Callable],
"before_breadcrumb": Optional[Callable],
"debug": bool,
"attach_stacktrace": bool,
"ca_certs": Optional[str],
},
)


VERSION = "0.7.3"
DEFAULT_SERVER_NAME = socket.gethostname() if hasattr(socket, "gethostname") else None
Expand Down
2 changes: 2 additions & 0 deletions sentry_sdk/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from sentry_sdk.hub import Hub
from sentry_sdk.utils import logger
from sentry_sdk.client import _client_init_debug
from logging import LogRecord


class _HubBasedClientFilter(logging.Filter):
def filter(self, record):
# type: (LogRecord) -> bool
if _client_init_debug.get(False):
return True
hub = Hub.current
Expand Down
Loading