Search code examples
pythonsetuptoolspypidistutils

Installing "scripts" in setup.py as part of a Python package, on user's path and recognized as Python scripts


I'm trying to have a Python script available on a user's path when they install my package from PyPI using pip:

pip install MyPackage

MyPackage is on PyPI and installs successfully--apparently--in a conda virtual environment. The setup.py file (excerpted) looks like this:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

config = {
    'name': 'MyPackage',
    'version': '0.2.1.dev',
    'install_requires': [...],
    'packages': [
        'MyPackage', 'MyPackage.utils'
    ],
    'py_modules': [
        'MyCLI',
    ],
    'scripts': [
        'MyPackage/MyCLI.py',
    ],
...

On GNU/Linux, when I type MyCLI and hit Tab, it successfully auto-completes to MyCLI.py. When I ask which MyCLI.py it shows me the fully qualified path to Python script in the virtual environment folder:

$ which MyCLI.py
/home/arthur/Applications/miniconda3/envs/MyPackage/bin/MyCLI.py

MyCLI.py uses fire to wrap a Python class, expose its methods at the command line, present docstrings as help documentation, and parse arguments. It looks like:

'''
My Command Line Interface
'''

class CLIRuntime(object):
    def run(self):
        do_something()


if __name__ == '__main__':
    import fire
    fire.Fire(CLIRuntime)

If I run this script with the Python interpreter, it executes correctly.

python $(which MyCLI.py)

The problem is, when I try to run it without specifying the Python interpreter, it seems to think it is a bash script or binary file and wrecks my terminal session:

$ MyCLI.py
/home/arthur/Applications/miniconda3/envs/MyPackage/bin/MyCLI.py: line 8:
My Command Line Interface
: No such file or directory
from: can't read /var/mail/__future__

How can I change setup.py so that this script is available on a user's path but also known as/ runs as a Python script?

I want to note that if I install this package from source using pip in editable mode (pip install -e .), MyCLI.py is on my path and runs correctly as a Python script. It just doesn't appear to work when installing from PyPI.


Solution

  • To tell your shell what program should be used to execute a script file, you need to add a "hash-bang" declaration as the first line in the script. For Python executing inside of a virtualenv, either

    #!python
    

    or

    #!/usr/bin/env python
    

    will do the trick. If you're using Python 3, use python3 instead.