-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathconftest.py
More file actions
66 lines (52 loc) · 2.88 KB
/
Copy pathconftest.py
File metadata and controls
66 lines (52 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import gc
import warnings
import pytest
# Test-session boot — plugin imports and importing every collected test module —
# allocates almost exclusively permanent objects, so automatic cyclic GC during that
# phase only adds pauses (seconds on a full-tree CI shard collection). Run the boot
# with GC off, then freeze the survivors into the permanent generation so the
# collector never rescans them during the test phase. Tests themselves run with GC
# enabled as usual. (django.setup() runs in pytest-django's load_initial_conftests,
# before conftest files load, so it stays outside the window.)
gc.disable()
def _end_gc_boot_window() -> None:
if gc.isenabled():
return
# Deliberately no gc.collect() before the freeze: sweeping the boot garbage
# (including items deselected by pytest-split sharding — measured at ~7k cyclic
# objects for a 5-way shard of posthog/api/test) costs ~0.5s per invocation but
# reclaims only ~1MB, so the garbage gets frozen along with the survivors.
gc.freeze()
gc.enable()
# gc.get_referrers() cannot see referrers in the frozen permanent generation,
# which turns hypothesis's register_random() liveness check into a false positive
# for Randoms registered after the freeze (e.g. trio's module-level instance,
# registered when hypothesis is first imported). Refcounts are unaffected, so the
# ReferenceError path for real misuse still works; only the warning is spurious.
warnings.filterwarnings("ignore", message=r"It looks like `register_random` was passed")
def pytest_collection_finish() -> None:
_end_gc_boot_window()
@pytest.hookimpl(tryfirst=True)
def pytest_runtestloop() -> None:
# Safety net for processes that never run a local collection (e.g. the
# pytest-xdist controller): end the window before the test loop starts.
_end_gc_boot_window()
def pytest_unconfigure() -> None:
# Frozen objects skip the final cyclic collections of interpreter shutdown, so their
# finalizers run in the late teardown phase where extension modules may already be
# gone — observed as exit code 139 (SIGSEGV) on the Temporal CI shards. Restore the
# default heap state so shutdown behaves exactly as without the boot window.
gc.unfreeze()
@pytest.fixture(autouse=True)
def _activate_personhog_fake(request):
"""Force all person/group reads through the personhog fake for every test.
The fake is seeded explicitly by the test helpers in posthog.test.persons
(create_person, create_group, etc.) — no signals are used. Tests in
personhog_client/ manage their own client and are excluded.
"""
if "personhog_client" in str(request.node.path):
yield
return
from posthog.test.personhog_fake import activate_personhog_fake # noqa: PLC0415, I001 — lazy import avoids connecting signals before Django is ready
with activate_personhog_fake():
yield