Skip to content

Commit ddac03c

Browse files
committed
Merge branch 'master' into sentry-sdk-2.0
2 parents 467bde9 + e22abb6 commit ddac03c

26 files changed

+543
-252
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,19 @@ _Plus 2 more_
277277
- The parameter `propagate_hub` in `ThreadingIntegration()` was deprecated and renamed to `propagate_scope`.
278278

279279

280+
## 1.44.1
281+
282+
### Various fixes & improvements
283+
284+
- Make `monitor` async friendly (#2912) by @sentrivana
285+
286+
You can now decorate your async functions with the `monitor`
287+
decorator and they will correctly report their duration
288+
and completion status.
289+
290+
- Fixed `Event | None` runtime `TypeError` (#2928) by @szokeasaurusrex
291+
292+
280293
## 1.44.0
281294

282295
### Various fixes & improvements

CONTRIBUTING.md

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ This file outlines the process to contribute to the SDK itself. For contributing
88

99
Please search the [issue tracker](https://github.com/getsentry/sentry-python/issues) before creating a new issue (a problem or an improvement request). Please also ask in our [Sentry Community on Discord](https://discord.com/invite/Ww9hbqr) before submitting a new issue. There are a ton of great people in our Discord community ready to help you!
1010

11-
1211
## Submitting Changes
1312

1413
- Fork the `sentry-python` repo and prepare your changes.
@@ -64,7 +63,7 @@ This will make sure that your commits will have the correct coding style.
6463
```bash
6564
cd sentry-python
6665

67-
pip install -r linter-requirements.txt
66+
pip install -r devenv-requirements.txt
6867

6968
pip install pre-commit
7069

@@ -75,12 +74,8 @@ That's it. You should be ready to make changes, run tests, and make commits! If
7574

7675
## Running Tests
7776

78-
To run the tests, first setup your development environment according to the instructions above. Then, install the required packages for running tests with the following command:
79-
```bash
80-
pip install -r test-requirements.txt
81-
```
77+
You can run all tests with the following command:
8278

83-
Once the requirements are installed, you can run all tests with the following command:
8479
```bash
8580
pytest tests/
8681
```

codecov.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ coverage:
99
ignore:
1010
- "tests"
1111
- "sentry_sdk/_types.py"
12+
github_checks:
13+
annotations: false

devenv-requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-r linter-requirements.txt
2+
-r test-requirements.txt
3+
mockupdb # required by `pymongo` tests that are enabled by `pymongo` from linter requirements
4+
pytest<7.0.0 # https://github.com/pytest-dev/pytest/issues/9621; see tox.ini
5+
pytest-asyncio<=0.21.1 # https://github.com/pytest-dev/pytest-asyncio/issues/706

sentry_sdk/_types.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"session",
114114
"internal",
115115
"profile",
116-
"statsd",
116+
"metric_bucket",
117117
"monitor",
118118
]
119119
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
@@ -177,3 +177,37 @@
177177

178178
BucketKey = Tuple[MetricType, str, MeasurementUnit, MetricTagsInternal]
179179
MetricMetaKey = Tuple[MetricType, str, MeasurementUnit]
180+
181+
MonitorConfigScheduleType = Literal["crontab", "interval"]
182+
MonitorConfigScheduleUnit = Literal[
183+
"year",
184+
"month",
185+
"week",
186+
"day",
187+
"hour",
188+
"minute",
189+
"second", # not supported in Sentry and will result in a warning
190+
]
191+
192+
MonitorConfigSchedule = TypedDict(
193+
"MonitorConfigSchedule",
194+
{
195+
"type": MonitorConfigScheduleType,
196+
"value": Union[int, str],
197+
"unit": MonitorConfigScheduleUnit,
198+
},
199+
total=False,
200+
)
201+
202+
MonitorConfig = TypedDict(
203+
"MonitorConfig",
204+
{
205+
"schedule": MonitorConfigSchedule,
206+
"timezone": str,
207+
"checkin_margin": int,
208+
"max_runtime": int,
209+
"failure_issue_threshold": int,
210+
"recovery_threshold": int,
211+
},
212+
total=False,
213+
)

sentry_sdk/consts.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ class EndpointType(Enum):
3737
Event,
3838
EventProcessor,
3939
Hint,
40+
MeasurementUnit,
4041
ProfilerMode,
4142
TracesSampler,
4243
TransactionProcessor,
4344
MetricTags,
45+
MetricValue,
4446
)
4547

4648
# Experiments are feature flags to enable and disable certain unstable SDK
@@ -57,9 +59,9 @@ class EndpointType(Enum):
5759
"transport_zlib_compression_level": Optional[int],
5860
"transport_num_pools": Optional[int],
5961
"enable_metrics": Optional[bool],
60-
"metrics_summary_sample_rate": Optional[float],
61-
"should_summarize_metric": Optional[Callable[[str, MetricTags], bool]],
62-
"before_emit_metric": Optional[Callable[[str, MetricTags], bool]],
62+
"before_emit_metric": Optional[
63+
Callable[[str, MetricValue, MeasurementUnit, MetricTags], bool]
64+
],
6365
"metric_code_locations": Optional[bool],
6466
},
6567
total=False,

sentry_sdk/crons/api.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@
55

66

77
if TYPE_CHECKING:
8-
from typing import Any, Dict, Optional
9-
from sentry_sdk._types import Event
8+
from typing import Optional
9+
from sentry_sdk._types import Event, MonitorConfig
1010

1111

1212
def _create_check_in_event(
13-
monitor_slug=None,
14-
check_in_id=None,
15-
status=None,
16-
duration_s=None,
17-
monitor_config=None,
13+
monitor_slug=None, # type: Optional[str]
14+
check_in_id=None, # type: Optional[str]
15+
status=None, # type: Optional[str]
16+
duration_s=None, # type: Optional[float]
17+
monitor_config=None, # type: Optional[MonitorConfig]
1818
):
19-
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> Event
19+
# type: (...) -> Event
2020
options = sentry_sdk.get_client().options
2121
check_in_id = check_in_id or uuid.uuid4().hex # type: str
2222

@@ -37,13 +37,13 @@ def _create_check_in_event(
3737

3838

3939
def capture_checkin(
40-
monitor_slug=None,
41-
check_in_id=None,
42-
status=None,
43-
duration=None,
44-
monitor_config=None,
40+
monitor_slug=None, # type: Optional[str]
41+
check_in_id=None, # type: Optional[str]
42+
status=None, # type: Optional[str]
43+
duration=None, # type: Optional[float]
44+
monitor_config=None, # type: Optional[MonitorConfig]
4545
):
46-
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> str
46+
# type: (...) -> str
4747
check_in_event = _create_check_in_event(
4848
monitor_slug=monitor_slug,
4949
check_in_id=check_in_id,

sentry_sdk/crons/decorator.py

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77
from sentry_sdk.utils import now
88

99
if TYPE_CHECKING:
10+
from collections.abc import Awaitable, Callable
1011
from types import TracebackType
1112
from typing import (
1213
Any,
13-
Awaitable,
14-
Callable,
1514
Optional,
1615
ParamSpec,
1716
Type,
1817
TypeVar,
1918
Union,
19+
cast,
20+
overload,
2021
)
22+
from sentry_sdk._types import MonitorConfig
2123

2224
P = ParamSpec("P")
2325
R = TypeVar("R")
@@ -53,7 +55,7 @@ def test(arg):
5355
"""
5456

5557
def __init__(self, monitor_slug=None, monitor_config=None):
56-
# type: (Optional[str], Optional[dict[str, Any]]) -> None
58+
# type: (Optional[str], Optional[MonitorConfig]) -> None
5759
self.monitor_slug = monitor_slug
5860
self.monitor_config = monitor_config
5961

@@ -83,22 +85,50 @@ def __exit__(self, exc_type, exc_value, traceback):
8385
monitor_config=self.monitor_config,
8486
)
8587

86-
def __call__(self, fn):
87-
# type: (Callable[P, R]) -> Callable[P, Union[R, Awaitable[R]]]
88+
if TYPE_CHECKING:
89+
90+
@overload
91+
def __call__(self, fn):
92+
# type: (Callable[P, Awaitable[Any]]) -> Callable[P, Awaitable[Any]]
93+
# Unfortunately, mypy does not give us any reliable way to type check the
94+
# return value of an Awaitable (i.e. async function) for this overload,
95+
# since calling iscouroutinefunction narrows the type to Callable[P, Awaitable[Any]].
96+
...
97+
98+
@overload
99+
def __call__(self, fn):
100+
# type: (Callable[P, R]) -> Callable[P, R]
101+
...
102+
103+
def __call__(
104+
self,
105+
fn, # type: Union[Callable[P, R], Callable[P, Awaitable[Any]]]
106+
):
107+
# type: (...) -> Union[Callable[P, R], Callable[P, Awaitable[Any]]]
88108
if iscoroutinefunction(fn):
89-
90-
@wraps(fn)
91-
async def inner(*args: "P.args", **kwargs: "P.kwargs"):
92-
# type: (...) -> R
93-
with self:
94-
return await fn(*args, **kwargs)
109+
return self._async_wrapper(fn)
95110

96111
else:
112+
if TYPE_CHECKING:
113+
fn = cast("Callable[P, R]", fn)
114+
return self._sync_wrapper(fn)
115+
116+
def _async_wrapper(self, fn):
117+
# type: (Callable[P, Awaitable[Any]]) -> Callable[P, Awaitable[Any]]
118+
@wraps(fn)
119+
async def inner(*args: "P.args", **kwargs: "P.kwargs"):
120+
# type: (...) -> R
121+
with self:
122+
return await fn(*args, **kwargs)
123+
124+
return inner
97125

98-
@wraps(fn)
99-
def inner(*args: "P.args", **kwargs: "P.kwargs"):
100-
# type: (...) -> R
101-
with self:
102-
return fn(*args, **kwargs)
126+
def _sync_wrapper(self, fn):
127+
# type: (Callable[P, R]) -> Callable[P, R]
128+
@wraps(fn)
129+
def inner(*args: "P.args", **kwargs: "P.kwargs"):
130+
# type: (...) -> R
131+
with self:
132+
return fn(*args, **kwargs)
103133

104134
return inner

sentry_sdk/envelope.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ def data_category(self):
266266
elif ty == "profile":
267267
return "profile"
268268
elif ty == "statsd":
269-
return "statsd"
269+
return "metric_bucket"
270270
elif ty == "check_in":
271271
return "monitor"
272272
else:

sentry_sdk/integrations/celery/beat.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
)
1414

1515
if TYPE_CHECKING:
16-
from typing import Any
17-
from typing import Callable
18-
from typing import Dict
19-
from typing import Optional
20-
from typing import TypeVar
21-
from typing import Union
16+
from collections.abc import Callable
17+
from typing import Any, Optional, TypeVar, Union
18+
from sentry_sdk._types import (
19+
MonitorConfig,
20+
MonitorConfigScheduleType,
21+
MonitorConfigScheduleUnit,
22+
)
2223

2324
F = TypeVar("F", bound=Callable[..., Any])
2425

@@ -42,7 +43,7 @@
4243

4344

4445
def _get_headers(task):
45-
# type: (Task) -> Dict[str, Any]
46+
# type: (Task) -> dict[str, Any]
4647
headers = task.request.get("headers") or {}
4748

4849
# flatten nested headers
@@ -56,11 +57,11 @@ def _get_headers(task):
5657

5758

5859
def _get_monitor_config(celery_schedule, app, monitor_name):
59-
# type: (Any, Celery, str) -> Dict[str, Any]
60-
monitor_config = {} # type: Dict[str, Any]
61-
schedule_type = None # type: Optional[str]
60+
# type: (Any, Celery, str) -> MonitorConfig
61+
monitor_config = {} # type: MonitorConfig
62+
schedule_type = None # type: Optional[MonitorConfigScheduleType]
6263
schedule_value = None # type: Optional[Union[str, int]]
63-
schedule_unit = None # type: Optional[str]
64+
schedule_unit = None # type: Optional[MonitorConfigScheduleUnit]
6465

6566
if isinstance(celery_schedule, crontab):
6667
schedule_type = "crontab"
@@ -242,7 +243,7 @@ def _setup_celery_beat_signals():
242243

243244

244245
def crons_task_success(sender, **kwargs):
245-
# type: (Task, Dict[Any, Any]) -> None
246+
# type: (Task, dict[Any, Any]) -> None
246247
logger.debug("celery_task_success %s", sender)
247248
headers = _get_headers(sender)
248249

@@ -263,7 +264,7 @@ def crons_task_success(sender, **kwargs):
263264

264265

265266
def crons_task_failure(sender, **kwargs):
266-
# type: (Task, Dict[Any, Any]) -> None
267+
# type: (Task, dict[Any, Any]) -> None
267268
logger.debug("celery_task_failure %s", sender)
268269
headers = _get_headers(sender)
269270

@@ -284,7 +285,7 @@ def crons_task_failure(sender, **kwargs):
284285

285286

286287
def crons_task_retry(sender, **kwargs):
287-
# type: (Task, Dict[Any, Any]) -> None
288+
# type: (Task, dict[Any, Any]) -> None
288289
logger.debug("celery_task_retry %s", sender)
289290
headers = _get_headers(sender)
290291

0 commit comments

Comments
 (0)