8

I'm trying to distribute a shell script along with a Python package. Ideally, the shell script is installed when I run pip install my_package. I read from this SO that, my expected behavior is exactly what the scripts keyword of setuptools.setup provides. E.g. the script my_script will be installed with the following setup.py script:

setup(
    ...
    scripts=['my_script'],
    ... 
) 

However, I cannot use the above method for two reasons:

  1. the official doc did not mention this behavior. I don't know if I can continue to do this way.
  2. my whole project is built on pyproject.toml, without setup.py. Although pyproject.toml has provided a [project.scripts] table, as explained in the setuptools official doc, the scripts can only be python functions instead of shell scripts.

For completeness, in my case, the shell script reads git status and sets environment variables, which will be read from within my python project. The shell script and my python project are bonded so tightly that I would rather not split them into two projects.

I have also tried to use a python function to execute the shell script, e.g.

[project.scripts]
my_script = 'my_project:my_func'
def my_func():
    subprocess.run(...)

The problem with this solution is that every time I run my_script, my_project is loaded and the loading process is really slow.

sinoroc
  • 18,409
  • 2
  • 39
  • 70

2 Answers2

4

Maybe a link in the comments leads to this information already. Anyway, I think it is worth posting that scripts = [...] in setup.py can be written in pyproject.toml as:

[tool.setuptools]
script-files = ["scripts/myscript1", "scripts/myscript2"]

However, this feature is deprecated. I hope the authors of the packaging tools will recognize the problem with shell scripts and deal with it.

Link: setuptools docs

VPfB
  • 14,927
  • 6
  • 41
  • 75
  • I dislike that it's deprecated but this answer seems to fit best with the pyproject.toml. I haven't seen another way to add in runnable non python scripts using setuptools (or poetry). I'm going to use this method and hopefully by the time the deprecation hits there will be some standard way. – greedybuddha Jan 18 '23 at 18:35
1

I'm not exactly sure it will work for you case, but I solved this by creating a "shim" setup.py file (it has an added benefit of being able to install your project in edit mode).

It usually just calls setup(), but it was possible to pass the scripts argument:

"""Shim setup file to allow for editable install."""

from setuptools import setup

if __name__ == "__main__":
    setup(scripts=["myscript"])

Everything else was loaded from pyproject.toml.

Mauricio
  • 87
  • 2
  • 2
  • 2
    As far as I know, nowadays, editable installation should be possible even without a `setup.py` script. For the rest, I agree with this answer. -- I think `scripts` is somewhat deprecated, which might be the reason why it is not ported to `pyproject.toml` notation. – sinoroc Nov 08 '22 at 09:29
  • Interesting. I didn't know about editable install without `setup.py`. I'll check that out later. As for `scripts`, it definitely feel that way. – Mauricio Nov 09 '22 at 03:01
  • 2
    See [PEP 660](https://peps.python.org/pep-0660/). As far as I can tell it has been added to [pip 21.3 (2021-10-11)](https://pip.pypa.io/en/stable/news/#v21-3) and to [setuptools v64.0.0 (11 Aug 2022)](https://setuptools.pypa.io/en/stable/history.html#v64-0-0). – sinoroc Nov 09 '22 at 09:09
  • 1
    Big thanks to you all!! The solution works in my case, but I'm a little worried about `scripts` being formally deprecated in the near future. Guess the `setup.py` solution is the only way for now... Thanks for the help! – Luting Wang Nov 15 '22 at 06:57