Skip to content

Commit 579b9c3

Browse files
committed
Add mypyc infrastructure tests and tooling config
- Add 22 tests covering detection, activation, import hook, sentinel - Add mypyc tox environment - Add *.so to .gitignore - Add ruff exceptions for build_mypyc.py
1 parent 6e745ef commit 579b9c3

5 files changed

Lines changed: 282 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ __pycache__/
2727
*.lock
2828
*.log
2929
*.py[cod]
30+
*.so

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ ignore = [
149149
"*/__init__.py" = [
150150
"I001", # imports do not need to be sorted
151151
]
152+
"build_mypyc.py" = [
153+
"BLE001", # allow catching blind exception
154+
"EXE001", # shebang for documentation
155+
"I001", # imports in logical order
156+
"PTH", # simpler path handling for script
157+
"T201", # print is expected in CLI script
158+
]
152159
"src/graphql/execution/*" = [
153160
"BLE001", # allow catching blind exception
154161
]

tests/mypyc/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Tests for mypyc compilation infrastructure."""
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
"""Tests for mypyc compilation infrastructure."""
2+
3+
from __future__ import annotations
4+
5+
6+
def describe_mypyc_detection():
7+
"""Tests for mypyc detection utilities."""
8+
9+
def exports_detection_functions():
10+
"""The graphql module exports mypyc detection functions."""
11+
import graphql
12+
13+
assert hasattr(graphql, "is_mypyc_enabled")
14+
assert hasattr(graphql, "get_mypyc_modules")
15+
assert callable(graphql.is_mypyc_enabled)
16+
assert callable(graphql.get_mypyc_modules)
17+
18+
def is_mypyc_enabled_returns_bool():
19+
"""is_mypyc_enabled returns a boolean."""
20+
import graphql
21+
22+
result = graphql.is_mypyc_enabled()
23+
assert isinstance(result, bool)
24+
25+
def get_mypyc_modules_returns_frozenset():
26+
"""get_mypyc_modules returns a frozenset of strings."""
27+
import graphql
28+
29+
result = graphql.get_mypyc_modules()
30+
assert isinstance(result, frozenset)
31+
32+
33+
def describe_mypyc_module():
34+
"""Tests for the graphql_mypyc module."""
35+
36+
def can_import_graphql_mypyc():
37+
"""The graphql_mypyc module can be imported."""
38+
import graphql_mypyc
39+
40+
assert graphql_mypyc is not None
41+
42+
def exports_activate_deactivate():
43+
"""The graphql_mypyc module exports activate/deactivate functions."""
44+
import graphql_mypyc
45+
46+
assert hasattr(graphql_mypyc, "activate")
47+
assert hasattr(graphql_mypyc, "deactivate")
48+
assert hasattr(graphql_mypyc, "is_active")
49+
assert callable(graphql_mypyc.activate)
50+
assert callable(graphql_mypyc.deactivate)
51+
assert callable(graphql_mypyc.is_active)
52+
53+
def exports_compiled_modules_set():
54+
"""The graphql_mypyc module exports COMPILED_MODULES."""
55+
import graphql_mypyc
56+
57+
assert hasattr(graphql_mypyc, "COMPILED_MODULES")
58+
assert isinstance(graphql_mypyc.COMPILED_MODULES, frozenset)
59+
60+
def exports_version():
61+
"""The graphql_mypyc module exports __version__."""
62+
import graphql_mypyc
63+
64+
assert hasattr(graphql_mypyc, "__version__")
65+
assert isinstance(graphql_mypyc.__version__, str)
66+
67+
68+
def describe_mypyc_activation():
69+
"""Tests for mypyc activation/deactivation."""
70+
71+
def activation_is_idempotent():
72+
"""Calling activate multiple times is safe."""
73+
import graphql_mypyc
74+
75+
# Should already be active from auto-activation
76+
result1 = graphql_mypyc.activate()
77+
result2 = graphql_mypyc.activate()
78+
assert result1 is True
79+
assert result2 is True
80+
81+
def deactivation_works():
82+
"""Deactivation disables mypyc mode."""
83+
import graphql
84+
import graphql_mypyc
85+
86+
# Deactivate
87+
graphql_mypyc.deactivate()
88+
assert graphql_mypyc.is_active() is False
89+
assert graphql.is_mypyc_enabled() is False
90+
91+
# Reactivate for other tests
92+
graphql_mypyc.activate()
93+
assert graphql_mypyc.is_active() is True
94+
assert graphql.is_mypyc_enabled() is True
95+
96+
def deactivation_is_idempotent():
97+
"""Calling deactivate multiple times is safe."""
98+
import graphql_mypyc
99+
100+
# Deactivate twice
101+
graphql_mypyc.deactivate()
102+
result1 = graphql_mypyc.deactivate()
103+
result2 = graphql_mypyc.deactivate()
104+
assert result1 is True
105+
assert result2 is True
106+
107+
# Reactivate for other tests
108+
graphql_mypyc.activate()
109+
110+
111+
def describe_mypyc_auto_activation():
112+
"""Tests for automatic mypyc activation on import."""
113+
114+
def auto_activates_when_graphql_mypyc_available():
115+
"""Auto-activates mypyc when graphql_mypyc is installed."""
116+
# Since graphql_mypyc is in our src/, it's always available
117+
# and should be auto-activated when graphql is imported
118+
import graphql
119+
120+
# The module should report as enabled (hook is installed)
121+
# Note: This tests the hook infrastructure, not actual compilation
122+
assert graphql.is_mypyc_enabled() is True
123+
124+
def compiled_modules_reflects_current_state():
125+
"""get_mypyc_modules returns the currently compiled modules."""
126+
import graphql
127+
import graphql_mypyc
128+
129+
# The compiled modules set should match what's registered
130+
modules = graphql.get_mypyc_modules()
131+
assert modules == graphql_mypyc.COMPILED_MODULES
132+
133+
134+
def describe_import_hook():
135+
"""Tests for the import hook mechanism."""
136+
137+
def hook_module_exports():
138+
"""The _hook module exports expected functions."""
139+
from graphql_mypyc._hook import (
140+
COMPILED_MODULES,
141+
install_hook,
142+
is_hook_installed,
143+
uninstall_hook,
144+
)
145+
146+
assert isinstance(COMPILED_MODULES, frozenset)
147+
assert callable(install_hook)
148+
assert callable(uninstall_hook)
149+
assert callable(is_hook_installed)
150+
151+
def hook_is_installed_after_activation():
152+
"""The import hook is installed after activation."""
153+
import graphql_mypyc
154+
from graphql_mypyc._hook import is_hook_installed
155+
156+
graphql_mypyc.activate()
157+
assert is_hook_installed() is True
158+
159+
def hook_is_removed_after_deactivation():
160+
"""The import hook is removed after deactivation."""
161+
import graphql_mypyc
162+
from graphql_mypyc._hook import is_hook_installed
163+
164+
graphql_mypyc.deactivate()
165+
assert is_hook_installed() is False
166+
167+
# Reactivate for other tests
168+
graphql_mypyc.activate()
169+
170+
def compiled_modules_contains_sentinel():
171+
"""COMPILED_MODULES includes the sentinel module."""
172+
from graphql_mypyc._hook import COMPILED_MODULES
173+
174+
assert "graphql._sentinel" in COMPILED_MODULES
175+
176+
177+
def describe_sentinel_module():
178+
"""Tests for the sentinel module that validates the import hook."""
179+
180+
def graphql_sentinel_exists():
181+
"""The graphql._sentinel module can be imported."""
182+
from graphql import _sentinel
183+
184+
assert _sentinel is not None
185+
186+
def graphql_mypyc_sentinel_exists():
187+
"""The graphql_mypyc._sentinel module can be imported."""
188+
from graphql_mypyc import _sentinel
189+
190+
assert _sentinel is not None
191+
192+
def sentinel_has_expected_exports():
193+
"""The sentinel module exports expected functions."""
194+
from graphql import _sentinel
195+
196+
assert hasattr(_sentinel, "is_compiled")
197+
assert hasattr(_sentinel, "add_numbers")
198+
assert hasattr(_sentinel, "SENTINEL_VALUE")
199+
assert callable(_sentinel.is_compiled)
200+
assert callable(_sentinel.add_numbers)
201+
202+
def sentinel_add_numbers_works():
203+
"""The sentinel add_numbers function works correctly."""
204+
from graphql import _sentinel
205+
206+
assert _sentinel.add_numbers(2, 3) == 5
207+
assert _sentinel.add_numbers(-1, 1) == 0
208+
209+
def hook_redirects_to_mypyc_version():
210+
"""With hook active, graphql._sentinel gets graphql_mypyc._sentinel."""
211+
import sys
212+
213+
import graphql_mypyc
214+
215+
# Ensure hook is active
216+
graphql_mypyc.activate()
217+
218+
# Remove any cached import of graphql._sentinel
219+
if "graphql._sentinel" in sys.modules:
220+
del sys.modules["graphql._sentinel"]
221+
222+
# Import with hook active - should get redirected
223+
from graphql import _sentinel
224+
225+
# The SENTINEL_VALUE tells us which version we got
226+
# graphql_mypyc._sentinel has "mypyc-compiled"
227+
# graphql._sentinel (fallback) has "interpreted"
228+
assert _sentinel.SENTINEL_VALUE == "mypyc-compiled"
229+
230+
def without_hook_gets_interpreted_version():
231+
"""With hook inactive, graphql._sentinel is the interpreted version."""
232+
import sys
233+
234+
import graphql
235+
import graphql_mypyc
236+
237+
# Deactivate hook
238+
graphql_mypyc.deactivate()
239+
240+
# Remove any cached import from sys.modules
241+
if "graphql._sentinel" in sys.modules:
242+
del sys.modules["graphql._sentinel"]
243+
244+
# Also remove the cached attribute from the graphql module
245+
# (Python caches submodule imports as attributes on the parent)
246+
if hasattr(graphql, "_sentinel"):
247+
delattr(graphql, "_sentinel")
248+
249+
# Import without hook - should get original
250+
from graphql import _sentinel
251+
252+
assert _sentinel.SENTINEL_VALUE == "interpreted"
253+
254+
# Reactivate for other tests and clear cache
255+
graphql_mypyc.activate()
256+
if "graphql._sentinel" in sys.modules:
257+
del sys.modules["graphql._sentinel"]
258+
if hasattr(graphql, "_sentinel"):
259+
delattr(graphql, "_sentinel")

tox.ini

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py3{10,11,12,13,14}, pypy3{10,11}, ruff, mypy, docs
2+
envlist = py3{10,11,12,13,14}, pypy3{10,11}, ruff, mypy, mypyc, docs
33
isolated_build = true
44
requires =
55
tox>=4.34
@@ -33,6 +33,19 @@ skip_install = true
3333
commands =
3434
python -m mypy src tests
3535

36+
[testenv:mypyc]
37+
# Test the mypyc compilation infrastructure
38+
# This verifies the import hook, detection, and activation work correctly
39+
basepython = python3.14
40+
dependency_groups = test
41+
commands =
42+
# Run mypyc infrastructure tests
43+
python -m pytest tests/mypyc -v {posargs}
44+
# Verify mypyc detection is working
45+
python -c "import graphql; assert graphql.is_mypyc_enabled(), 'mypyc should be enabled'"
46+
python -c "import graphql; print(f'mypyc enabled: {{graphql.is_mypyc_enabled()}}')"
47+
python -c "import graphql; print(f'compiled modules: {{graphql.get_mypyc_modules()}}')"
48+
3649
[testenv:docs]
3750
basepython = python3.14
3851
dependency_groups = doc

0 commit comments

Comments
 (0)