Search code examples
pythonpipsetuptoolspypicommand-line-tool

"No module named x.__main__; 'x' is a package and cannot be directly executed" when using entry_points / console_scripts


I have this CLI tool called Rackfocus. I've published to PyPI, and I'm reasonably sure it worked just fine before. When I try to run it with current versions of Python on Mac, I get the error:

No module named rackfocus.__main__; 'rackfocus' is a package
and cannot be directly executed

All I want is one package with one entry point that users can download and use using pip.

Based on tutorials, I have this in setup.py:

packages=['rackfocus']
entry_points = {
    'console_scripts': [
        'rackfocus=rackfocus.run:main'
    ]
}

And I have a rackfocus.run:main function, an init.py and everything. What's wrong?

You can reproduce this locally:

  1. Clone my repo.
  2. Create and activate a virtualenv (optional).
  3. pip3 install -e .
  4. python3 -m rackfocus

Solution

  • entry_points = {
        'console_scripts': [
            'rackfocus=rackfocus.run:main'
        ]
    }
    

    This tells the packaging system to create a wrapper executable named rackfocus. That executable will automatically handle all the necessary steps to get Python off the ground, find the run module in the rackfocus package, find its main function and call it.

    You run the executable like rackfocus (if you are using a virtual environment, it should be on the path already), not python -m rackfocus.

    Using python -m rackfocus is completely unrelated to that (it doesn't even have anything to do with packaging, and can easily be used with code that hasn't been installed yet). It doesn't use the wrapper; instead, it simply attempts to execute the rackfocus module. But in your case, rackfocus isn't a module; it's a package. The error message means exactly what it says.

    You would want python -m rackfocus.run to execute the run module - but of course, that still doesn't actually call main() (just like it wouldn't with python rackfocus/main.py - though the -m approach is more powerful; in particular, it allows your relative imports to work).

    The error message says rackfocus.__main__ because you can make a package runnable by giving it a __main__ module.