New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for pip
-installed console_scripts
execution through subprocess.run
#6362
Comments
Ughh, this request again! At a distance it looks like a really neat feature until you start trying to figure out how it's supposed to be implemented and start unpicking the misassumptions around console scripts themselves and how PyInstaller works. Bear with me... Let's start with collecting the executables:
Those shim executables which setuptools uses to launch console scripts have absolute paths hardcoded and are therefore not relocatable. Take for example my Linux shim for pytest: #!/home/brenainn/.pyenv/versions/3.9.6/bin/python3.9
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(console_main()) The shebang at the top hardcodes a) that this is to be ran with Python and b) exactly where Python is installed. If I run this file on my machine it will launch the entrypoint from source using the Python I have installed and ignoring anything that PyInstaller built. If I ran this file on any other machine or if I uninstalled that particular version of Python I would get a file not found error. For the Windows EXE shims, it's the same story except that they're compiled so they're even harder to do any kind of use relative paths conversion on. But I'm leading you down a bit of a rabbit hole here by talking about that absolute path to Python because, as PyInstaller does not ship the Now to invoke them:
This is not safe in regular Python. There is no guarantee that the current Python environment's # hello_world/hello_world/run_hello_world.py
from hello_world import hello_world
hello_world() Then invoke them using: subprocess.run([sys.executable, "-m", "hello_word.run_hello_world"]) This method is completely portable in that it doesn't matter how your Python environment is set up W.R.T I'd also like to point out that adding a PyInstaller bundle's root directory to So what should you be doing instead?
if getattr(sys, 'frozen', False):
subprocess.run([os.path.join(sys._MEIPASS, "entry_point_foo")])
else:
subprocess.run([sys.executable, "-m", "hello_word.entry_point_foo"])
|
At the end of the day, even without all other issues and considerations listed above, the feature as proposed would require us to automatically freeze any entry-point script we encounter in collected packages, on the off chance that one (or some) of them will end up being used by the frozen application via subprocess... So I'm against going down that route. |
Thank you for this solution here. I have a few scripts that run in subprocesses and correctly use: I guess it isn't that hard, but the fact that I did correctly use: |
Do you actually need them to be subprocesses? Why not just import the scripts (wrapping their contents into function bodies if need be)?
I would have no objection to an example being added to make doing this clearer if I had ever seen a genuine case where the subprocesses shouldn't have been removed in favour of just |
Is your feature request related to a problem? Please describe.
PyInstaller does not currently support calling
console_scripts
throughsubprocess.run()
.Describe the solution you'd like
The solution would be to include all
console_scripts
in the application bundle and add their location to$PATH
.Describe alternatives you've considered
I have managed to work around the issue by using
--add-binary $(which $CONSOLESCRIPT):.
and executing the binary in code withsubprocess.run([os.path.join(os.path.dirname(__file__), $CONSOLESCRIPT])
, but it is hacky and error-prone.For example,
uwsgi
does not work properly with such setup, but that might be because it uses some kind of custom-built python executable for running, so maybe that use case is more complex to implement. But basicpip
-installedconsole_scripts
should work the same as they work in a python environment.Additional context
Here's a minimal reproducible example (Ubuntu 20.04):
Let's define a package
hello_world
with the following file structure:Such that the contents of files are:
Let's now define an application
main.py
in which we use thehello_world
script:Assuming that we are in a
venv
, and we have the above definedhello_world
package installed, runningpyinstaller main.py
creates an application bundle in adist/main
directory.Deactivating
venv
and runningdist/main/main
now raises the following error:If we were to properly bundle all
console_scripts
with the application, the previous example would print:and exit without error.
The text was updated successfully, but these errors were encountered: