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

1

12

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
Violone answered 20/2, 2022 at 6:36 Comment(1)
The problem isn't that you are "using entry_points / console_scripts". The problem is that you are not doing so; or rather, you are trying to run the program in a way that ignores that aspect of the setup.Tapes
T
11
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.

Tapes answered 20/2, 2022 at 7:17 Comment(5)
Thanks, I didn't know this. Turns out I was using the system Python that comes with macOS and the PATH is set up such that pip-installed packages aren't in it. I vaguely remembered python3 -m ... being the recommended new way to execute packages from somewhere and just tried that. I just installed my own Python from Brew, then installed my package through that pip3. Running the package as a command works out of the box with this installation!Violone
"the system Python that comes with macOS and the PATH is set up such that pip-installed packages aren't in it" Yes; I assume this is a security feature for OSes that come with Python pre-deployed, so that you don't disrupt a Python installation needed for critical system tasks. "python3 -m ... being the recommended new way to execute packages from somewhere and just tried that." To execute (actually executable) packages (or more commonly, modules), yes. But that's not actually what you were trying to do.Tapes
"Running the package as a command works out of the box with this installation!" As I said, you don't need to install your code to use it with python -m. You do need to understand how the packaging system works, no matter what you are trying to do. You should also read up on virtual environments, while you're at it.Tapes
Can you illustrate with an example module that should be called with python -m? Why is Rackfocus not an executable package? What would you call it instead?Violone
Like I said, rackfocus is a package. It just isn't an executable one. To be executable you would have to take the steps described in the last link. python -m is useful for any module that you'd call directly, like python whatever.py - the differences are as explained at the other link.Tapes

© 2022 - 2024 — McMap. All rights reserved.