Skip to content

Commit 500e070

Browse files
authored
feat: Added basic API docs (#57)
1 parent 88b2d3a commit 500e070

File tree

14 files changed

+142
-70
lines changed

14 files changed

+142
-70
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ tox-test:
2424
lint:
2525
@tox -e linters
2626
.PHONY: lint
27+
28+
apidocs:
29+
@pip install pdoc pygments
30+
@pdoc --overwrite --html --html-dir build/apidocs sentry_sdk
31+
.PHONY: apidocs

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ After initialization, you can capture exceptions like this:
3535

3636
You can create a scope to attach data to all events happening inside of it:
3737

38-
with sentry_sdk.get_current_hub().push_scope():
38+
with sentry_sdk.Hub.current.push_scope():
3939
with sentry_sdk.configure_scope() as scope:
4040
scope.transaction = "my_view_name"
4141
scope.set_tag("key", "value")

sentry_sdk/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
1+
"""
2+
The Sentry SDK is the new-style SDK for [sentry.io](https://sentry.io/). It implements
3+
the unified API that all modern SDKs follow for Python 2.7 and 3.5 or later.
4+
5+
The user documentation can be found on [docs.sentry.io](https://docs.sentry.io/).
6+
7+
## Quickstart
8+
9+
The only thing to get going is to call `sentry_sdk.init()`. When not passed any
10+
arguments the default options are used and the DSN is picked up from the `SENTRY_DSN`
11+
environment variable. Otherwise the DSN can be passed with the `dsn` keyword
12+
or first argument.
13+
14+
import sentry_sdk
15+
sentry_sdk.init()
16+
17+
This initializes the default integrations which will automatically pick up any
18+
uncaught exceptions. Additionally you can report arbitrary other exceptions:
19+
20+
try:
21+
my_failing_function()
22+
except Exception as e:
23+
sentry_sdk.capture_exception(e)
24+
"""
125
from .api import * # noqa
226
from .api import __all__ # noqa
327

28+
# modules we consider public
29+
__all__.append("integrations")
30+
431
# Initialize the debug support after everything is loaded
532
from .debug import init_debug_support
633

sentry_sdk/api.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
import inspect
12
from contextlib import contextmanager
23

34
from .hub import Hub
45
from .scope import Scope
56
from .utils import EventHint
7+
from .transport import Transport, HttpTransport
68
from .client import Client, get_options
79
from .integrations import setup_integrations
810

911

10-
__all__ = ["Hub", "Scope", "Client", "EventHint"]
12+
__all__ = ["Hub", "Scope", "Client", "EventHint", "Transport", "HttpTransport"]
1113

1214

1315
def public(f):
1416
__all__.append(f.__name__)
1517
return f
1618

1719

20+
def hubmethod(f):
21+
f.__doc__ = "%s\n\n%s" % (
22+
"Alias for `Hub.%s`" % f.__name__,
23+
inspect.getdoc(getattr(Hub, f.__name__)),
24+
)
25+
return public(f)
26+
27+
1828
class _InitGuard(object):
1929
def __init__(self, client):
2030
self._client = client
@@ -32,7 +42,9 @@ def _init_on_hub(hub, args, kwargs):
3242
options = get_options(*args, **kwargs)
3343
client = Client(options)
3444
hub.bind_client(client)
35-
setup_integrations(options)
45+
setup_integrations(
46+
options["integrations"] or [], with_defaults=options["default_integrations"]
47+
)
3648
return _InitGuard(client)
3749

3850

@@ -52,41 +64,36 @@ def _init_on_current(*args, **kwargs):
5264
return _init_on_hub(Hub.current, args, kwargs)
5365

5466

55-
@public
67+
@hubmethod
5668
def capture_event(event, hint=None):
57-
"""Alias for `Hub.current.capture_event`"""
5869
hub = Hub.current
5970
if hub is not None:
6071
return hub.capture_event(event, hint)
6172

6273

63-
@public
74+
@hubmethod
6475
def capture_message(message, level=None):
65-
"""Alias for `Hub.current.capture_message`"""
6676
hub = Hub.current
6777
if hub is not None:
6878
return hub.capture_message(message, level)
6979

7080

71-
@public
81+
@hubmethod
7282
def capture_exception(error=None):
73-
"""Alias for `Hub.current.capture_exception`"""
7483
hub = Hub.current
7584
if hub is not None:
7685
return hub.capture_exception(error)
7786

7887

79-
@public
88+
@hubmethod
8089
def add_breadcrumb(*args, **kwargs):
81-
"""Alias for `Hub.current.add_breadcrumb`"""
8290
hub = Hub.current
8391
if hub is not None:
8492
return hub.add_breadcrumb(*args, **kwargs)
8593

8694

87-
@public
95+
@hubmethod
8896
def configure_scope(callback=None):
89-
"""Alias for `Hub.current.configure_scope`"""
9097
hub = Hub.current
9198
if hub is not None:
9299
return hub.configure_scope(callback)
@@ -99,15 +106,8 @@ def inner():
99106
return inner()
100107

101108

102-
@public
103-
def get_current_hub():
104-
"""Alias for `Hub.current`"""
105-
return Hub.current
106-
107-
108-
@public
109+
@hubmethod
109110
def last_event_id():
110-
"""Alias for `Hub.last_event_id`"""
111111
hub = Hub.current
112112
if hub is not None:
113113
return hub.last_event_id()

sentry_sdk/hub.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def _internal_exceptions():
1818
except Exception:
1919
hub = Hub.current
2020
if hub:
21-
hub.capture_internal_exception(sys.exc_info())
21+
hub._capture_internal_exception(sys.exc_info())
2222

2323

2424
def _get_client_options():
@@ -124,7 +124,13 @@ def bind_client(self, new):
124124
self._stack[-1] = (new, top[1])
125125

126126
def capture_event(self, event, hint=None):
127-
"""Captures an event."""
127+
"""Captures an event. The return value is the ID of the event.
128+
129+
The event is a dictionary following the Sentry v7/v8 protocol
130+
specification. Optionally an `EventHint` object can be passed that
131+
is used by processors to extract additional information from it.
132+
Typically the event hint object would contain exception information.
133+
"""
128134
client, scope = self._stack[-1]
129135
if client is not None:
130136
rv = client.capture_event(event, hint, scope)
@@ -133,15 +139,22 @@ def capture_event(self, event, hint=None):
133139
return rv
134140

135141
def capture_message(self, message, level=None):
136-
"""Captures a message."""
142+
"""Captures a message. The message is just a string. If no level
143+
is provided the default level is `info`.
144+
"""
137145
if self.client is None:
138146
return
139147
if level is None:
140148
level = "info"
141149
return self.capture_event({"message": message, "level": level})
142150

143151
def capture_exception(self, error=None):
144-
"""Captures an exception."""
152+
"""Captures an exception.
153+
154+
The argument passed can be `None` in which case the last exception
155+
will be reported, otherwise an exception object or an `exc_info`
156+
tuple.
157+
"""
145158
client = self.client
146159
if client is None:
147160
return
@@ -156,15 +169,19 @@ def capture_exception(self, error=None):
156169
try:
157170
return self.capture_event(event, hint=hint)
158171
except Exception:
159-
self.capture_internal_exception(sys.exc_info())
172+
self._capture_internal_exception(sys.exc_info())
160173

161-
def capture_internal_exception(self, exc_info):
174+
def _capture_internal_exception(self, exc_info):
162175
"""Capture an exception that is likely caused by a bug in the SDK
163176
itself."""
164177
logger.debug("Internal error in sentry_sdk", exc_info=exc_info)
165178

166179
def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
167-
"""Adds a breadcrumb."""
180+
"""Adds a breadcrumb. The breadcrumbs are a dictionary with the
181+
data as the sentry v7/v8 protocol expects. `hint` is an optional
182+
value that can be used by `before_breadcrumb` to customize the
183+
breadcrumbs that are emitted.
184+
"""
168185
client, scope = self._stack[-1]
169186
if client is None:
170187
logger.info("Dropped breadcrumb because no client bound")

sentry_sdk/integrations/__init__.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""This package"""
12
from threading import Lock
23

34
from ..utils import logger
@@ -7,35 +8,47 @@
78
_installer_lock = Lock()
89

910

10-
def _get_default_integrations():
11+
def get_default_integrations():
12+
"""Returns an iterator of default integration instances."""
1113
from .logging import LoggingIntegration
1214
from .excepthook import ExcepthookIntegration
1315
from .dedupe import DedupeIntegration
1416
from .atexit import AtexitIntegration
1517

16-
yield LoggingIntegration
17-
yield ExcepthookIntegration
18-
yield DedupeIntegration
19-
yield AtexitIntegration
18+
yield LoggingIntegration()
19+
yield ExcepthookIntegration()
20+
yield DedupeIntegration()
21+
yield AtexitIntegration()
2022

2123

22-
def setup_integrations(options):
23-
integrations = list(options.get("integrations", None) or ())
24-
default_integrations = options.get("default_integrations") or False
25-
26-
if default_integrations:
27-
for cls in _get_default_integrations():
28-
if not any(isinstance(x, cls) for x in integrations):
29-
integrations.append(cls())
24+
def setup_integrations(integrations, with_defaults=True):
25+
"""Given a list of integration instances this installs them all. When
26+
`with_defaults` is set to `True` then all default integrations are added
27+
unless they were already provided before.
28+
"""
29+
integrations = list(integrations)
30+
if with_defaults:
31+
for instance in get_default_integrations():
32+
if not any(isinstance(x, type(instance)) for x in integrations):
33+
integrations.append(instance)
3034

3135
for integration in integrations:
3236
integration()
3337

3438

3539
class Integration(object):
40+
"""Baseclass for all integrations."""
41+
3642
identifier = None
43+
"""A unique identifying string for the integration. Integrations must
44+
set this as a class attribute.
45+
"""
3746

3847
def install(self):
48+
"""An integration must implement all its code here. When the
49+
`setup_integrations` function runs it will invoke this unless the
50+
integration was already activated elsewhere.
51+
"""
3952
raise NotImplementedError()
4053

4154
def __call__(self):

sentry_sdk/integrations/_wsgi.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
22
import sys
33

4-
import sentry_sdk
4+
from sentry_sdk import capture_exception
55
from sentry_sdk.hub import (
6+
Hub,
67
_internal_exceptions,
78
_should_send_default_pii,
89
_get_client_options,
@@ -150,7 +151,7 @@ def get_client_ip(environ):
150151

151152

152153
def run_wsgi_app(app, environ, start_response):
153-
hub = sentry_sdk.get_current_hub()
154+
hub = Hub.current
154155
hub.push_scope()
155156
with _internal_exceptions():
156157
with hub.configure_scope() as scope:
@@ -160,7 +161,7 @@ def run_wsgi_app(app, environ, start_response):
160161
rv = app(environ, start_response)
161162
except Exception:
162163
einfo = sys.exc_info()
163-
sentry_sdk.capture_exception(einfo)
164+
capture_exception(einfo)
164165
hub.pop_scope_unsafe()
165166
reraise(*einfo)
166167

@@ -180,7 +181,7 @@ def __iter__(self):
180181
self._response = iter(self._response)
181182
except Exception:
182183
einfo = sys.exc_info()
183-
sentry_sdk.capture_exception(einfo)
184+
capture_exception(einfo)
184185
reraise(*einfo)
185186
return self
186187

@@ -191,7 +192,7 @@ def __next__(self):
191192
raise
192193
except Exception:
193194
einfo = sys.exc_info()
194-
sentry_sdk.capture_exception(einfo)
195+
capture_exception(einfo)
195196
reraise(*einfo)
196197

197198
def close(self):
@@ -201,7 +202,7 @@ def close(self):
201202
self._response.close()
202203
except Exception:
203204
einfo = sys.exc_info()
204-
sentry_sdk.capture_exception(einfo)
205+
capture_exception(einfo)
205206
reraise(*einfo)
206207

207208

sentry_sdk/integrations/atexit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111

1212
def default_shutdown_callback(pending, timeout):
13+
"""This is the default shutdown callback that is set on the options.
14+
It prints out a message to stderr that informs the user that some events
15+
are still pending and the process is waiting for them to flush out.
16+
"""
17+
1318
def echo(msg):
1419
sys.stderr.write(msg + "\n")
1520

sentry_sdk/integrations/celery.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from celery.signals import task_failure, task_prerun, task_postrun
44
from celery.exceptions import SoftTimeLimitExceeded
55

6-
from sentry_sdk import get_current_hub
6+
from sentry_sdk import Hub
77
from sentry_sdk.hub import _internal_exceptions
88

99
from . import Integration
@@ -24,7 +24,7 @@ def _process_failure_signal(self, sender, task_id, einfo, **kw):
2424
if hasattr(sender, "throws") and isinstance(einfo.exception, sender.throws):
2525
return
2626

27-
hub = get_current_hub()
27+
hub = Hub.current
2828
if isinstance(einfo.exception, SoftTimeLimitExceeded):
2929
with hub.push_scope():
3030
with hub.configure_scope() as scope:
@@ -40,10 +40,10 @@ def _process_failure_signal(self, sender, task_id, einfo, **kw):
4040

4141
def _handle_task_prerun(self, sender, task, **kw):
4242
with _internal_exceptions():
43-
hub = get_current_hub()
43+
hub = Hub.current
4444
hub.push_scope()
4545
with hub.configure_scope() as scope:
4646
scope.transaction = task.name
4747

4848
def _handle_task_postrun(self, sender, task_id, task, **kw):
49-
get_current_hub().pop_scope_unsafe()
49+
Hub.current.pop_scope_unsafe()

0 commit comments

Comments
 (0)