Skip to content

Commit 6f8cada

Browse files
committed
BUG: improve resiliency in distutils.build_ext.finalize_options
1 parent 9cc2f5c commit 6f8cada

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

newsfragments/5083.bugfix.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed a bug where calling ``build_ext.finalize_options`` more than once would
2+
raise an exception.
3+
4+
Fixed a bug where calling ``build_ext.finalize_options`` after ``define`` or
5+
``undef`` attributes were already set would raise an exception.

setuptools/_distutils/command/build_ext.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,20 +266,20 @@ def finalize_options(self) -> None: # noqa: C901
266266
# specified by the 'define' option will be set to '1'. Multiple
267267
# symbols can be separated with commas.
268268

269-
if self.define:
269+
if isinstance(self.define, str):
270270
defines = self.define.split(',')
271271
self.define = [(symbol, '1') for symbol in defines]
272272

273273
# The option for macros to undefine is also a string from the
274274
# option parsing, but has to be a list. Multiple symbols can also
275275
# be separated with commas here.
276-
if self.undef:
276+
if isinstance(self.undef, str):
277277
self.undef = self.undef.split(',')
278278

279-
if self.swig_opts is None:
280-
self.swig_opts = []
281-
else:
279+
if isinstance(self.swig_opts, str):
282280
self.swig_opts = self.swig_opts.split(' ')
281+
elif self.swig_opts is None:
282+
self.swig_opts = []
283283

284284
# Finally add the user include and library directories if requested
285285
if self.user:

setuptools/tests/test_build_ext.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,33 @@ def C(file):
178178
assert example_stub.startswith(f"{build_lib}/mypkg/__pycache__/ext1")
179179
assert example_stub.endswith(".pyc")
180180

181+
def test_finalize_options_multiple_call(self):
182+
"""
183+
Regression test. Check that calling build_ext.finalize_options
184+
more than once doesn't raise an exception.
185+
"""
186+
extension = Extension('spam.eggs', ['eggs.c'])
187+
dist = Distribution(dict(ext_modules=[extension]))
188+
cmd = build_ext(dist)
189+
cmd.finalize_options()
190+
cmd.finalize_options()
191+
192+
def test_finalize_options_subclassing(self):
193+
"""
194+
Regression test. Check that calling build_ext.finalize_options after
195+
define or undef attributes were already set to their final type doesn't raise.
196+
"""
197+
extension = Extension('spam.eggs', ['eggs.c'])
198+
dist = Distribution(dict(ext_modules=[extension]))
199+
cmd = build_ext(dist)
200+
cmd.define = [("MY_MACRO", "1")]
201+
cmd.undef = ["EVIL_MACRO"]
202+
203+
cmd.finalize_options()
204+
205+
assert cmd.define == [("MY_MACRO", "1")]
206+
assert cmd.undef == ["EVIL_MACRO"]
207+
181208

182209
class TestBuildExtInplace:
183210
def get_build_ext_cmd(self, optional: bool, **opts) -> build_ext:

0 commit comments

Comments
 (0)