setuptools: [BUG] sys.exit(0) in setup.py breaks PEP 517 hooks API

setuptools version

68.0.0

Python version

3.11

OS

Linux

Additional environment information

I believe this will affect any OS and any Python version.

Description

If a setup.py file calls sys.exit(0), building it through the PEP 517 hooks interface will fail. sys.exit() raises a SystemExit exception, which propagates up out of the hook function. Normally, unhandled exceptions indicate that something went wrong, and that’s OK - the frontend sees that the backend exited with an error, and displays its stdout/stderr for debugging. But in this case the exception is used even though nothing is wrong.

PEP 517 says:

If a hook raises an exception, or causes the process to terminate, then this indicates an error.

We could interpret that 100% literally: an exception was raised, therefore the backend has failed, but that’s clearly not the intention - sys.exit(0) indicates that execution of setup.py completed successfully. Edit: it’s like return for scripts. 🙂

This issue has been bounced around several packaging related projects already, most recently https://github.com/pypa/pyproject-hooks/issues/173 .

Expected behavior

I think setuptools hooks implementation should catch and ignore SystemExit with an err.code attribute of 0 or None (the non-error cases), on the assumption that this means running setup.py finished successfully.

I had a go at doing this in pyproject-hooks, the machinery that calls the hooks, but the hooks have to return something, so catching the error outside setuptools isn’t enough - we don’t know what it would have returned.

How to Reproduce

Save the following code as setup.py in a folder by itself and then run python -m build path/to/folder/:

from setuptools import setup
import sys

setup(
    name="pep517-test-setup-py-support",
    version="1.0",
)
sys.exit(0)

Output

$ python -m build tests/samples/setup-py/
* Creating virtualenv isolated environment...
* Installing packages in isolated environment... (setuptools, wheel)
* Getting build dependencies for sdist...
running egg_info
writing pep517_test_setup_py_support.egg-info/PKG-INFO
writing dependency_links to pep517_test_setup_py_support.egg-info/dependency_links.txt
writing top-level names to pep517_test_setup_py_support.egg-info/top_level.txt
reading manifest file 'pep517_test_setup_py_support.egg-info/SOURCES.txt'
writing manifest file 'pep517_test_setup_py_support.egg-info/SOURCES.txt'

Traceback (most recent call last):
  File "/home/takluyver/.local/lib/python3.11/site-packages/pyproject_hooks/_impl.py", line 19, in read_json
    with open(path, encoding='utf-8') as f:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpe0ghw0d1/output.json'

ERROR Backend operation failed: FileNotFoundError(2, 'No such file or directory')

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 17 (17 by maintainers)

Commits related to this issue

Most upvoted comments

OK, so I guess the consensus is to let SystemExit fall through if exit code is non-zero and only handle sys.main(0)?