Search code examples
pythonpipsetup.pytwine

How to package my python project to be runnable from command line?


I've coded my python project and have succeeded in publishing it to test pypi. However, now I can't figure out how to correctly configure it as a console script. Upon running my_project on the command line, I get the following stack trace:

Traceback (most recent call last):
  File "/home/thatcoolcoder/.local/bin/my_project", line 5, in <module>
    from my_project.__main__ import main
ModuleNotFoundError: No module named 'my_project'

Clearly, it's created a script to run but the script is then failing to import my actual code.

Folder structure:

pyproject.toml
setup.cfg
my_project
├── __init__.py (empty)
├── __main__.py

Relevant sections of setup.cfg:

[metadata]
name = my-project
version = 1.0.5

...

[options]
package_dir =
    = my_project
packages = find:

...

[options.packages.find]
where = my_project

[options.entry_points]
console_scripts =
    my_project = my_project.__main__:main

pyproject.toml (probably not relevant)

[build-system]
requires = [
    "setuptools>=42",
    "wheel"
]

__main__.py:

from my_project import foo

def main():
    foo.bar()

if __name__ == '__main__':
    main()

To build and upload, I'm running the following: (python is python 3.10)

python -m build
python -m twine upload --repository testpypi dist/*

Then to install and run:

pip install -i https://test.pypi.org/pypi/ --extra-index-url https://pypi.org/simple my-project --upgrade

my_project

How can I make this console script work?

Also, this current method of setting console_scripts only allows it to be run as my_project; is it possible to also make it work by python -m my_project? Or perhaps this will work once my main issue is fixed.


Solution

  • I finally got back to this problem today and it appears that I was using an incorrect source layout, which caused the pip module installation to not work. I switched to a directory structure like this one:

    ├── src
    │   └── mypackage
    │       ├── __init__.py
    │       └── mod1.py
    ├── setup.py
    └── setup.cfg
    

    and modified the relevant parts of my setup.cfg:

    [options]
    package_dir=
        =src
    packages=find:
    
    [options.packages.find]
    where=src
    

    Then I can run it like python -m mypackage. This also made the console scripts work. It works on Linux but I presume it also works on other systems.