Skip to content

Commit

Permalink
Handle setup.py possibly using sys.exit
Browse files Browse the repository at this point in the history
Since `setup.py` is an arbitrary Python script legacy
scripts may be complex and may use `sys.exit`.
To adhere to PEP 517 handle `SystemExit` exception
by ignoring it with exit code `0` and converting it to
`CalledProcessError(exit_code, 'setup.py')` in case of
non-zero exit code.

fixes #3973
  • Loading branch information
arcivanov committed Aug 5, 2023
1 parent 77c14a8 commit e3131c9
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
10 changes: 9 additions & 1 deletion setuptools/build_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,15 @@ def run_setup(self, setup_script='setup.py'):
with _open_setup_script(__file__) as f:
code = f.read().replace(r'\r\n', r'\n')

exec(code, locals())
try:
exec(code, locals())
except SystemExit as e:
if e.code:
# We pretend under PEP 517 that the implementation
# called a subprocess, which failed.
from subprocess import CalledProcessError
raise CalledProcessError(e.code, "setup.py")
# We ignore exit code indicating success

def get_requires_for_build_wheel(self, config_settings=None):
return self._get_build_requires(config_settings, requirements=['wheel'])
Expand Down
56 changes: 56 additions & 0 deletions setuptools/tests/test_build_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import re
from zipfile import ZipFile
from pathlib import Path
from subprocess import CalledProcessError

import pytest
from jaraco import path
Expand Down Expand Up @@ -217,6 +218,37 @@ def run():
},
]

sys_exit_defns = [
{ # simple setup.py script
'setup.py': DALS(
"""
__import__('setuptools').setup(
name='foo',
version='0.0.0',
py_modules=['hello'],
setup_requires=['six'],
)
import sys
sys.exit(0)
"""
),
'hello.py': DALS(
"""
def run():
print('hello')
"""
),
},
{ # simple setup.py script
'setup.py': DALS(
"""
import sys
sys.exit(1)
"""
),
},
]


class TestBuildMetaBackend:
backend_name = 'setuptools.build_meta'
Expand Down Expand Up @@ -260,6 +292,30 @@ def test_build_wheel(self, build_backend):
modules = [f for f in python_scripts if not f.endswith('setup.py')]
assert len(modules) == 1

@pytest.fixture(params=sys_exit_defns)
def sys_exit_build_backend(self, tmpdir, request):
path.build(request.param, prefix=str(tmpdir))
with tmpdir.as_cwd():
yield self.get_build_backend()

def test_get_requires_for_build_wheel_with_sys_exit(self, sys_exit_build_backend):
try:
actual = sys_exit_build_backend.get_requires_for_build_wheel()
expected = ['six', 'wheel']
assert sorted(actual) == sorted(expected)
except CalledProcessError as e:
assert e.returncode == 1
assert e.cmd == "setup.py"

def test_get_requires_for_build_sdist_with_sys_exit(self, sys_exit_build_backend):
try:
actual = sys_exit_build_backend.get_requires_for_build_sdist()
expected = ['six']
assert sorted(actual) == sorted(expected)
except CalledProcessError as e:
assert e.returncode == 1
assert e.cmd == "setup.py"

@pytest.mark.parametrize('build_type', ('wheel', 'sdist'))
def test_build_with_existing_file_present(self, build_type, tmpdir_cwd):
# Building a sdist/wheel should still succeed if there's
Expand Down

0 comments on commit e3131c9

Please sign in to comment.