Skip to content

Commit ae23101

Browse files
committed
fix: reset the cached compiler when build_ext is reinitialized
`httptools_build_ext.initialize_options()` returns early once `_initialized` is set, deliberately preserving the Cython and extension mutations applied on the first pass. Setuptools reinitializes the `build_ext` command between phases, so a chained invocation such as `setup.py build bdist_wheel` reaches the wheel phase holding the compiler instance the earlier phase already configured, and the build fails instead of selecting a compiler for the current phase. Clear only the transient `compiler` attribute in that early-return path. Delegating to the base initializer instead would discard exactly the cached command options and extension state the early return exists to protect, so the reset is kept as narrow as the problem. `tests/test_build.py` loads the real `setup.py`, finalizes the command, attaches a compiler, reinitializes, and asserts the run proceeds — it fails without this change. Verified: - Focused regression — 1 passed - `python setup.py build bdist_wheel` — succeeds - `make test` — 42 passed - `make typecheck` — 0 errors, 0 warnings - `git diff --check` — clean Fixes #126
1 parent cf10ce6 commit ae23101

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def initialize_options(self):
4040
# same command object, so make sure not to override previously
4141
# set options.
4242
if getattr(self, '_initialized', False):
43+
self.compiler = None
4344
return
4445

4546
super().initialize_options()

tests/test_build.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import runpy
2+
import unittest
3+
from pathlib import Path
4+
from unittest import mock
5+
6+
from setuptools import Distribution, Extension
7+
from setuptools._distutils.ccompiler import new_compiler
8+
9+
10+
class TestBuildExt(unittest.TestCase):
11+
def test_reinitialized_command_uses_fresh_compiler(self):
12+
setup_py = Path(__file__).parents[1] / "setup.py"
13+
with mock.patch("setuptools.setup") as setup:
14+
runpy.run_path(str(setup_py))
15+
16+
build_ext = setup.call_args.kwargs["cmdclass"]["build_ext"]
17+
distribution = Distribution({
18+
"cmdclass": {"build_ext": build_ext},
19+
"ext_modules": [Extension("test", ["test.c"])],
20+
})
21+
command = distribution.get_command_obj("build_ext")
22+
command.ensure_finalized()
23+
command.compiler = new_compiler()
24+
25+
distribution.reinitialize_command("build_ext")
26+
command.build_extensions = mock.Mock()
27+
command.run()
28+
29+
command.build_extensions.assert_called_once_with()

0 commit comments

Comments
 (0)