Skip to content

Commit e9f9d0e

Browse files
committed
Merge branch 'master' into sentry-sdk-2.0
2 parents fa5f50b + cf2d3c6 commit e9f9d0e

File tree

8 files changed

+96
-40
lines changed

8 files changed

+96
-40
lines changed

.craft.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ targets:
88
pypi:sentry-sdk:
99
- name: github
1010
- name: aws-lambda-layer
11-
includeNames: /^sentry-python-serverless-\d+(\.\d+)*\.zip$/
11+
# This regex that matches the version is taken from craft:
12+
# https://github.com/getsentry/craft/blob/8d77c38ddbe4be59f98f61b6e42952ca087d3acd/src/utils/version.ts#L11
13+
includeNames: /^sentry-python-serverless-\bv?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([\da-z-]+(?:\.[\da-z-]+)*))?(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?\b.zip$/
1214
layerName: SentryPythonServerlessSDK
1315
compatibleRuntimes:
1416
- name: python

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 1.40.6
4+
5+
### Various fixes & improvements
6+
7+
- Fix compatibility with `greenlet`/`gevent` (#2756) by @sentrivana
8+
- Fix query source relative filepath (#2717) by @gggritso
9+
- Support `clickhouse-driver==0.2.7` (#2752) by @sentrivana
10+
- Bump `checkouts/data-schemas` from `6121fd3` to `eb941c2` (#2747) by @dependabot
11+
312
## 1.40.5
413

514
### Various fixes & improvements

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
copyright = "2019-{}, Sentry Team and Contributors".format(datetime.now().year)
2929
author = "Sentry Team and Contributors"
3030

31-
release = "1.40.5"
31+
release = "1.40.6"
3232
version = ".".join(release.split(".")[:2]) # The short X.Y version.
3333

3434

sentry_sdk/client.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from datetime import datetime, timezone
66
from importlib import import_module
77

8-
from sentry_sdk._compat import check_uwsgi_thread_support
8+
from sentry_sdk._compat import PY37, check_uwsgi_thread_support
99
from sentry_sdk.utils import (
1010
capture_internal_exceptions,
1111
current_stacktrace,
@@ -15,6 +15,7 @@
1515
get_type_name,
1616
get_default_release,
1717
handle_in_app,
18+
is_gevent,
1819
logger,
1920
)
2021
from sentry_sdk.serializer import serialize
@@ -315,14 +316,22 @@ def _capture_envelope(envelope):
315316
self.metrics_aggregator = None # type: Optional[MetricsAggregator]
316317
experiments = self.options.get("_experiments", {})
317318
if experiments.get("enable_metrics", True):
318-
from sentry_sdk.metrics import MetricsAggregator
319-
320-
self.metrics_aggregator = MetricsAggregator(
321-
capture_func=_capture_envelope,
322-
enable_code_locations=bool(
323-
experiments.get("metric_code_locations", True)
324-
),
325-
)
319+
# Context vars are not working correctly on Python <=3.6
320+
# with gevent.
321+
metrics_supported = not is_gevent() or PY37
322+
if metrics_supported:
323+
from sentry_sdk.metrics import MetricsAggregator
324+
325+
self.metrics_aggregator = MetricsAggregator(
326+
capture_func=_capture_envelope,
327+
enable_code_locations=bool(
328+
experiments.get("metric_code_locations", True)
329+
),
330+
)
331+
else:
332+
logger.info(
333+
"Metrics not supported on Python 3.6 and lower with gevent."
334+
)
326335

327336
max_request_body_size = ("always", "never", "small", "medium")
328337
if self.options["max_request_body_size"] not in max_request_body_size:

sentry_sdk/consts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,4 @@ def _get_default_options():
326326
del _get_default_options
327327

328328

329-
VERSION = "1.40.5"
329+
VERSION = "1.40.6"

sentry_sdk/metrics.py

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
to_timestamp,
2020
serialize_frame,
2121
json_dumps,
22-
is_gevent,
2322
)
2423
from sentry_sdk.envelope import Envelope, Item
2524
from sentry_sdk.tracing import (
@@ -54,18 +53,7 @@
5453
from sentry_sdk._types import MetricValue
5554

5655

57-
try:
58-
from gevent.monkey import get_original # type: ignore
59-
from gevent.threadpool import ThreadPool # type: ignore
60-
except ImportError:
61-
import importlib
62-
63-
def get_original(module, name):
64-
# type: (str, str) -> Any
65-
return getattr(importlib.import_module(module), name)
66-
67-
68-
_in_metrics = ContextVar("in_metrics")
56+
_in_metrics = ContextVar("in_metrics", default=False)
6957
_sanitize_key = partial(re.compile(r"[^a-zA-Z0-9_/.-]+").sub, "_")
7058
_sanitize_value = partial(re.compile(r"[^\w\d_:/@\.{}\[\]$-]+", re.UNICODE).sub, "_")
7159
_set = set # set is shadowed below
@@ -96,7 +84,7 @@ def get_code_location(stacklevel):
9684
def recursion_protection():
9785
# type: () -> Generator[bool, None, None]
9886
"""Enters recursion protection and returns the old flag."""
99-
old_in_metrics = _in_metrics.get(False)
87+
old_in_metrics = _in_metrics.get()
10088
_in_metrics.set(True)
10189
try:
10290
yield old_in_metrics
@@ -429,9 +417,7 @@ def __init__(
429417
self._running = True
430418
self._lock = threading.Lock()
431419

432-
event_cls = get_original("threading", "Event")
433-
self._flush_event = event_cls() # type: threading.Event
434-
420+
self._flush_event = threading.Event() # type: threading.Event
435421
self._force_flush = False
436422

437423
# The aggregator shifts its flushing by up to an entire rollup window to
@@ -442,7 +428,7 @@ def __init__(
442428
# jittering.
443429
self._flush_shift = random.random() * self.ROLLUP_IN_SECONDS
444430

445-
self._flusher = None # type: Optional[Union[threading.Thread, ThreadPool]]
431+
self._flusher = None # type: Optional[threading.Thread]
446432
self._flusher_pid = None # type: Optional[int]
447433

448434
def _ensure_thread(self):
@@ -465,16 +451,11 @@ def _ensure_thread(self):
465451

466452
self._flusher_pid = pid
467453

468-
if not is_gevent():
469-
self._flusher = threading.Thread(target=self._flush_loop)
470-
self._flusher.daemon = True
471-
start_flusher = self._flusher.start
472-
else:
473-
self._flusher = ThreadPool(1)
474-
start_flusher = partial(self._flusher.spawn, func=self._flush_loop)
454+
self._flusher = threading.Thread(target=self._flush_loop)
455+
self._flusher.daemon = True
475456

476457
try:
477-
start_flusher()
458+
self._flusher.start()
478459
except RuntimeError:
479460
# Unfortunately at this point the interpreter is in a state that no
480461
# longer allows us to spawn a thread and we have to bail.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_file_text(file_name):
2121

2222
setup(
2323
name="sentry-sdk",
24-
version="1.40.5",
24+
version="1.40.6",
2525
author="Sentry Team and Contributors",
2626
author_email="[email protected]",
2727
url="https://github.com/getsentry/sentry-python",

tests/test_metrics.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99
from sentry_sdk.tracing import TRANSACTION_SOURCE_ROUTE
1010
from sentry_sdk.envelope import parse_json
1111

12+
try:
13+
import gevent
14+
except ImportError:
15+
gevent = None
16+
17+
18+
minimum_python_37_with_gevent = pytest.mark.skipif(
19+
gevent and sys.version_info < (3, 7),
20+
reason="Require Python 3.7 or higher with gevent",
21+
)
22+
1223

1324
def parse_metrics(bytes):
1425
rv = []
@@ -41,6 +52,7 @@ def parse_metrics(bytes):
4152
return rv
4253

4354

55+
@minimum_python_37_with_gevent
4456
@pytest.mark.forked
4557
def test_incr(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
4658
sentry_init(
@@ -93,6 +105,7 @@ def test_incr(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
93105
}
94106

95107

108+
@minimum_python_37_with_gevent
96109
@pytest.mark.forked
97110
def test_timing(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
98111
sentry_init(
@@ -153,6 +166,7 @@ def test_timing(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
153166
)
154167

155168

169+
@minimum_python_37_with_gevent
156170
@pytest.mark.forked
157171
def test_timing_decorator(
158172
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -248,6 +262,7 @@ def amazing_nano():
248262
assert line.strip() == "assert amazing() == 42"
249263

250264

265+
@minimum_python_37_with_gevent
251266
@pytest.mark.forked
252267
def test_timing_basic(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
253268
sentry_init(
@@ -302,6 +317,7 @@ def test_timing_basic(sentry_init, capture_envelopes, maybe_monkeypatched_thread
302317
}
303318

304319

320+
@minimum_python_37_with_gevent
305321
@pytest.mark.forked
306322
def test_distribution(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
307323
sentry_init(
@@ -364,6 +380,7 @@ def test_distribution(sentry_init, capture_envelopes, maybe_monkeypatched_thread
364380
)
365381

366382

383+
@minimum_python_37_with_gevent
367384
@pytest.mark.forked
368385
def test_set(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
369386
sentry_init(
@@ -417,6 +434,7 @@ def test_set(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
417434
}
418435

419436

437+
@minimum_python_37_with_gevent
420438
@pytest.mark.forked
421439
def test_gauge(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
422440
sentry_init(
@@ -450,6 +468,7 @@ def test_gauge(sentry_init, capture_envelopes, maybe_monkeypatched_threading):
450468
}
451469

452470

471+
@minimum_python_37_with_gevent
453472
@pytest.mark.forked
454473
def test_multiple(sentry_init, capture_envelopes):
455474
sentry_init(
@@ -504,6 +523,7 @@ def test_multiple(sentry_init, capture_envelopes):
504523
}
505524

506525

526+
@minimum_python_37_with_gevent
507527
@pytest.mark.forked
508528
def test_transaction_name(
509529
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -544,6 +564,7 @@ def test_transaction_name(
544564
}
545565

546566

567+
@minimum_python_37_with_gevent
547568
@pytest.mark.forked
548569
@pytest.mark.parametrize("sample_rate", [1.0, None])
549570
def test_metric_summaries(
@@ -654,6 +675,7 @@ def test_metric_summaries(
654675
}
655676

656677

678+
@minimum_python_37_with_gevent
657679
@pytest.mark.forked
658680
def test_metrics_summary_disabled(
659681
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -698,6 +720,7 @@ def test_metrics_summary_disabled(
698720
assert "_metrics_summary" not in t["spans"][0]
699721

700722

723+
@minimum_python_37_with_gevent
701724
@pytest.mark.forked
702725
@pytest.mark.skip(reason="Temporarily disable to release SDK 2.0a1.")
703726
def test_metrics_summary_filtered(
@@ -768,6 +791,7 @@ def should_summarize_metric(key, tags):
768791
} in t["d:foo@second"]
769792

770793

794+
@minimum_python_37_with_gevent
771795
@pytest.mark.forked
772796
def test_tag_normalization(
773797
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -811,6 +835,7 @@ def test_tag_normalization(
811835
}
812836

813837

838+
@minimum_python_37_with_gevent
814839
@pytest.mark.forked
815840
def test_before_emit_metric(
816841
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -854,6 +879,7 @@ def before_emit(key, tags):
854879
}
855880

856881

882+
@minimum_python_37_with_gevent
857883
@pytest.mark.forked
858884
def test_aggregator_flush(
859885
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -874,6 +900,7 @@ def test_aggregator_flush(
874900
assert Hub.current.client.metrics_aggregator.buckets == {}
875901

876902

903+
@minimum_python_37_with_gevent
877904
@pytest.mark.forked
878905
def test_tag_serialization(
879906
sentry_init, capture_envelopes, maybe_monkeypatched_threading
@@ -914,6 +941,7 @@ def test_tag_serialization(
914941
}
915942

916943

944+
@minimum_python_37_with_gevent
917945
@pytest.mark.forked
918946
def test_flush_recursion_protection(
919947
sentry_init, capture_envelopes, monkeypatch, maybe_monkeypatched_threading
@@ -946,11 +974,12 @@ def bad_capture_envelope(*args, **kwargs):
946974
assert m[0][1] == "counter@none"
947975

948976

977+
@minimum_python_37_with_gevent
949978
@pytest.mark.forked
950979
def test_flush_recursion_protection_background_flush(
951980
sentry_init, capture_envelopes, monkeypatch, maybe_monkeypatched_threading
952981
):
953-
monkeypatch.setattr(metrics.MetricsAggregator, "FLUSHER_SLEEP_TIME", 0.1)
982+
monkeypatch.setattr(metrics.MetricsAggregator, "FLUSHER_SLEEP_TIME", 0.01)
954983
sentry_init(
955984
release="fun-release",
956985
environment="not-fun-env",
@@ -977,3 +1006,29 @@ def bad_capture_envelope(*args, **kwargs):
9771006
m = parse_metrics(envelope.items[0].payload.get_bytes())
9781007
assert len(m) == 1
9791008
assert m[0][1] == "counter@none"
1009+
1010+
1011+
@pytest.mark.skipif(
1012+
not gevent or sys.version_info >= (3, 7),
1013+
reason="Python 3.6 or lower and gevent required",
1014+
)
1015+
@pytest.mark.forked
1016+
def test_disable_metrics_for_old_python_with_gevent(
1017+
sentry_init, capture_envelopes, maybe_monkeypatched_threading
1018+
):
1019+
if maybe_monkeypatched_threading != "greenlet":
1020+
pytest.skip("Test specifically for gevent/greenlet")
1021+
1022+
sentry_init(
1023+
release="fun-release",
1024+
environment="not-fun-env",
1025+
_experiments={"enable_metrics": True},
1026+
)
1027+
envelopes = capture_envelopes()
1028+
1029+
metrics.incr("counter")
1030+
1031+
Hub.current.flush()
1032+
1033+
assert Hub.current.client.metrics_aggregator is None
1034+
assert not envelopes

0 commit comments

Comments
 (0)