While working on a local project I found that if a SyntaxError is encountered in a file to be byte-compiled, it does not halt installation or cause setup.py to return a non-zero status code. Preferably a non-zero exit code would be returned if any errors are encountered during compilation.
Inspired by #1780, here is a minimal reproducing case here:
mkdir /tmp/bad_module
cd /tmp/bad_module
setup.pycat << EOF > setup.py
from setuptools import setup
setup(
name='bad_module',
version='0.1',
py_modules=['bad_module'],
install_requires=[]
)
EOF
bad_module.py with syntax error.echo "return 1" > bad_module.py
python -m virtualenv venv
source venv/bin/activate
Installing this package with pip install . will work just fine, but you can see that it detects the SyntaxError during the compilation step if you use the --verbose option (clipped a bunch of stuff here):
$ pip install --verbose .
Created temporary directory: /tmp/pip-ephem-wheel-cache-xdcp8ik3
Created temporary directory: /tmp/pip-req-tracker-3yim7_ej
Created requirements tracker '/tmp/pip-req-tracker-3yim7_ej'
Created temporary directory: /tmp/pip-install-bswg4x71
Processing /tmp/bad_module
...
removing build/bdist.linux-x86_64/wheel
done
Stored in directory: /tmp/pip-ephem-wheel-cache-xdcp8ik3/wheels/09/...
Removing source in /tmp/pip-req-build-n4h0pv0q
Successfully built bad-module
Installing collected packages: bad-module
*** Error compiling '/tmp/pip-install-bswg4x71/bad-module/bad_module.py'...
File "/tmp/pip-install-bswg4x71/bad-module/bad_module.py", line 1
return 1
^
SyntaxError: 'return' outside function
Successfully installed bad-module-0.1
Cleaning up...
Removed build tracker '/tmp/pip-req-tracker-3yim7_ej'
I think the thing here that's up for discussion is whether or not this should be a hard failure. Technically the build succeeds, and there might be some reason that someone wants to be able to include code that has a SyntaxError in it in the build files (not sure why).
I'm also not entirely sure how easy it is to turn off the "compile the code" step, and whether it's even desirable for the compilation step to happen automatically (particularly if the *.pyc files aren't supposed to go in the wheel anyway).
This feels like one of those changes that seems completely sensible but breaks someone's workflow somewhere. I would also not be surprised if making this a hard failure broke some existing source distribution installations out there that have one or two files with SyntaxErrors in them that may not even be used in some old ill-maintained projects out there.
The real question is whether this would help people enough by preventing them from deploying broken code that it's worth possibly breaking installs out there today that already work. It might be worth trying to estimate the size of the problem by making a proof of concept change in setuptools and trying to install the top 100-1000 packages on PyPI from source, then see how many times you are discovering real bugs (indicating that a hard failure would be useful) vs. how many times you are finding something you'll just need to work around (indicating that it would not be useful).
This feels like one of those changes that seems completely sensible but breaks someone's workflow somewhere.
If I may throw in my opinion, I'd prefer for the change: after byte-compile failure, gracefully exit with non-zero return code.
It seems to me it's becoming more culturally acceptable for open-source projects to softly enforce good software practices on users even when it means changes that break users' other processes.
For this particular situation of installing software, it struck me as strange and unexpected that median code-compilation steps could fail yet an installation would appear to succeed (from the point of view of the running shell script). And then I worried about several series of docker builds that I deal with day-to-day; "are those docker builds silently installing broken code!?". For the build+test processes I work with day-to-day, I prefer the approach of "everything works or everything stops (and someone immediately fixes it)".
So count me in as "pro graceful exit". Perhaps we could shorten this issue title to "grexit" and hold a vote on it?
my 2c: the current behaviour is desirable
Consider for example a package which supports python 2 + python 3 -- on python3 it provides additional apis for async functions.
The package properly splits the newer features into separate modules:
pkg/
__init__.py
core.py
core_async.py
in python 2, the core_async.py file would contain SyntaxErrors when byte-compiling, but that's fine -- the async apis aren't designed for use in python 2.