Skip to content

ref(api): Abstract base classes #2667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Feb 26, 2024
27 changes: 20 additions & 7 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ Looking to upgrade from Sentry SDK 1.x to 2.x? Here's a comprehensive list of wh
- The `reraise` function was moved from `sentry_sdk._compat` to `sentry_sdk.utils`.
- Moved the contents of `tracing_utils_py3.py` to `tracing_utils.py`. The `start_child_span_decorator` is now in `sentry_sdk.tracing_utils`.
- The actual implementation of `get_current_span` was moved to `sentry_sdk.tracing_utils`. `sentry_sdk.get_current_span` is still accessible as part of the top-level API.
- The classes listed in the table below are now abstract base classes. Therefore, they can no longer be instantiated. Subclasses can only be instantiated if they implement all of the abstract methods.
<details>
<summary><b>Show table</b></summary>

| Class | Abstract methods |
| ------------------------------------- | -------------------------------------- |
| `sentry_sdk.integrations.Integration` | `setup_once` |
| `sentry_sdk.metrics.Metric` | `add`, `serialize_value`, and `weight` |
| `sentry_sdk.profiler.Scheduler` | `setup` and `teardown` |
| `sentry_sdk.transport.Transport` | `capture_envelope` |

</details>

## Removed

Expand All @@ -33,16 +45,17 @@ Looking to upgrade from Sentry SDK 1.x to 2.x? Here's a comprehensive list of wh
- Removed `sentry_sdk.utils.Auth.store_api_url`.
- `sentry_sdk.utils.Auth.get_api_url`'s now accepts a `sentry_sdk.consts.EndpointType` enum instead of a string as its only parameter. We recommend omitting this argument when calling the function, since the parameter's default value is the only possible `sentry_sdk.consts.EndpointType` value. The parameter exists for future compatibility.
- Removed `tracing_utils_py2.py`. The `start_child_span_decorator` is now in `sentry_sdk.tracing_utils`.
- Removed the `sentry_sdk.profiler.Scheduler.stop_profiling` method. Any calls to this method can simply be removed, since this was a no-op method.

## Deprecated

- `profiler_mode` and `profiles_sample_rate` have been deprecated as `_experiments` options. Use them as top level options instead:
```python
sentry_sdk.init(
...,
profiler_mode="thread",
profiles_sample_rate=1.0,
)
```
```python
sentry_sdk.init(
...,
profiler_mode="thread",
profiles_sample_rate=1.0,
)
```
- Deprecated `sentry_sdk.transport.Transport.capture_event`. Please use `sentry_sdk.transport.Transport.capture_envelope`, instead.
- Passing a function to `sentry_sdk.init`'s `transport` keyword argument has been deprecated. If you wish to provide a custom transport, please pass a `sentry_sdk.transport.Transport` instance or a subclass.
6 changes: 4 additions & 2 deletions sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from abc import ABC, abstractmethod
from threading import Lock

from sentry_sdk._types import TYPE_CHECKING
Expand Down Expand Up @@ -177,7 +178,7 @@ class DidNotEnable(Exception): # noqa: N818
"""


class Integration:
class Integration(ABC):
"""Baseclass for all integrations.

To accept options for an integration, implement your own constructor that
Expand All @@ -191,6 +192,7 @@ class Integration:
"""String unique ID of integration type"""

@staticmethod
@abstractmethod
def setup_once():
# type: () -> None
"""
Expand All @@ -203,4 +205,4 @@ def setup_once():
Inside those hooks `Integration.current` can be used to access the
instance again.
"""
raise NotImplementedError()
pass
9 changes: 9 additions & 0 deletions sentry_sdk/integrations/_wsgi_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ def request_body_within_bounds(client, content_length):


class RequestExtractor:
"""
Base class for request extraction.
"""

# It does not make sense to make this class an ABC because it is not used
# for typing, only so that child classes can inherit common methods from
# it. Only some child classes implement all methods that raise
# NotImplementedError in this class.

def __init__(self, request):
# type: (Any) -> None
self.request = request
Expand Down
29 changes: 18 additions & 11 deletions sentry_sdk/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import threading
import time
import zlib
from abc import ABC, abstractmethod
from contextlib import contextmanager
from datetime import datetime, timezone
from functools import wraps, partial
Expand Down Expand Up @@ -119,23 +120,29 @@ def new_func(*args, **kwargs):
return new_func


class Metric:
class Metric(ABC):
__slots__ = ()

@abstractmethod
def __init__(self, first):
# type: (MetricValue) -> None
pass

@property
@abstractmethod
def weight(self):
# type: (...) -> int
raise NotImplementedError()
# type: () -> int
pass

def add(
self, value # type: MetricValue
):
# type: (...) -> None
raise NotImplementedError()
@abstractmethod
def add(self, value):
# type: (MetricValue) -> None
pass

@abstractmethod
def serialize_value(self):
# type: (...) -> Iterable[FlushedMetricValue]
raise NotImplementedError()
# type: () -> Iterable[FlushedMetricValue]
pass


class CounterMetric(Metric):
Expand Down Expand Up @@ -333,7 +340,7 @@ def _encode_locations(timestamp, code_locations):
"g": GaugeMetric,
"d": DistributionMetric,
"s": SetMetric,
}
} # type: dict[MetricType, type[Metric]]

# some of these are dumb
TIMING_FUNCTIONS = {
Expand Down
21 changes: 12 additions & 9 deletions sentry_sdk/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import threading
import time
import uuid
from abc import ABC, abstractmethod
from collections import deque

import sentry_sdk
Expand Down Expand Up @@ -587,7 +588,6 @@ def stop(self):
assert self.scheduler, "No scheduler specified"
logger.debug("[Profiling] Stopping profile")
self.active = False
self.scheduler.stop_profiling(self)
self.stop_ns = nanosecond_time()

def __enter__(self):
Expand Down Expand Up @@ -756,7 +756,7 @@ def valid(self):
return True


class Scheduler:
class Scheduler(ABC):
mode = "unknown" # type: ProfilerMode

def __init__(self, frequency):
Expand All @@ -778,27 +778,30 @@ def __exit__(self, ty, value, tb):
# type: (Optional[Any], Optional[Any], Optional[Any]) -> None
self.teardown()

@abstractmethod
def setup(self):
# type: () -> None
raise NotImplementedError
pass

@abstractmethod
def teardown(self):
# type: () -> None
raise NotImplementedError
pass

def ensure_running(self):
# type: () -> None
raise NotImplementedError
"""
Ensure the scheduler is running. By default, this method is a no-op.
The method should be overridden by any implementation for which it is
relevant.
"""
return None

def start_profiling(self, profile):
# type: (Profile) -> None
self.ensure_running()
self.new_profiles.append(profile)

def stop_profiling(self, profile):
# type: (Profile) -> None
pass

def make_sampler(self):
# type: () -> Callable[..., None]
cwd = os.getcwd()
Expand Down
6 changes: 4 additions & 2 deletions sentry_sdk/transport.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from abc import ABC, abstractmethod
import io
import warnings
import urllib3
Expand Down Expand Up @@ -33,7 +34,7 @@
DataCategory = Optional[str]


class Transport:
class Transport(ABC):
"""Baseclass for all transports.

A transport is used to send an event to sentry.
Expand Down Expand Up @@ -72,6 +73,7 @@ def capture_event(
envelope.add_event(event)
self.capture_envelope(envelope)

@abstractmethod
def capture_envelope(
self, envelope # type: Envelope
):
Expand All @@ -83,7 +85,7 @@ def capture_envelope(
submitted to Sentry. We use it to send all event data (including errors,
transactions, crons checkins, etc.) to Sentry.
"""
raise NotImplementedError()
pass

def flush(
self,
Expand Down