Search code examples
pythonsetuptools

"cannot import name '__main__" when trying to run a console command from the wheel I created


I've been struggling on and off for months trying to create a wheel for my project 'rpn' (https://github.com/ConceptJunkie/rpn). I've finally gotten to the point where everything seems right, except for the console commands I'm trying to create in setup.py.

My project has multiple modules, and my setup.py, which I will post below, creates a wheel that seems to work correctly. I haven't done extensive testing with virtualenv because I'd like to get the basics right. When I install the wheel with pip, I can run python and import my modules and run the functions just fine. However, I use the project (and a few auxilliary utilities) as a command-line app, so I want the wheel to create some commands for me.

I'm on Windows, so when I install the wheel, it creates a small EXE file for each of the commands I have defined, but when I try to run the commands, I get this output:

c:\app\python36\scripts>makeRPNUnits.exe
Traceback (most recent call last):
  File "c:\app\python36\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\app\python36\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\app\python36\Scripts\makeUnits.exe\__main__.py", line 5, in <module>
ImportError: cannot import name '__main__'

This is one of the little utilities I need to run to prepare the data files the app uses, but the other commands all do the same thing. If I go into Python I can run it just fine:

c:\app\python36\scripts>python
Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit 
(AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

>>> import makeUnits
>>> makeUnits.main( )
makeUnits 6.999.13 (7.0b13) - RPN command-line calculator unit conversion data generator 
copyright (c) 2018 (1988), Rick Gutleber ([email protected])

[and so on...]

I can also use the actual application through Python:

>>> rpn.handleOutput( rpn.rpn( [ '2', '2', '+' ] ) )
4

I have spent hours searching for help... the documentation for setup tools is very sparse and opaque, and I can only find tiny snippets of information elsewhere and it's often conflicting. There's never enough to understand the details, but just enough to make educated guesses.

My directory structure looks like this:

rpn/
├── rpndata
│   ├── various_text_files
└── setup.py
    __init__.py
    rpn.py
    makeUnits.py
    makeHelp.py
    MANIFEST.in
    rpn*.py

Each of the commands I wish to create is represented in a separate module that handles main. For example:

if __name__ == '__main__':
    main( )

and the rest of the files implement all the functionality used by the program and the various utilities. Again, most of this stuff works from within Python, but the commands in /python36/scripts fail with the error above.

The whole setup.py is rather lengthy, but the relevant part is this:

entry_points = {
    'console_scripts': [
        'rpn = rpn.rpn:__main__',
        'makeRPNHelp = rpn.makeHelp:__main__',
        'makeRPNUnits = rpn.makeUnits:__main__',
        'prepareRPNPrimeData = rpn.preparePrimeData:__main__',
        'testRPN = rpn.testRPN:__main__',
    ],
}

The experts may require more details, which I will be happy to provide, and of course, the entire project is available from GitHub. The project is fairly large, and somewhat convoluted... it's outgrown my ability to manage a Python project, because it's only a hobby for me, I do C++ at my day job.

Your help is appreciated.


Solution

  • For your project's particular case, you have defined a main function in every module, so for setuptool entrypoints to work you can simply reference them directly, as if how you imported them (import makeUnits), i.e.:

    entry_points = {
        'console_scripts': [
            'rpn = rpn:main',
            'makeRPNHelp = makeHelp:main',
            'makeRPNUnits = makeUnits:main',
            ...
        ]
    }
    

    Why you may have gotten confuse with needing __main__ is that this value is assigned to a module __name__ if that is the executable script, or naming a module as __main__.py is a way to allow a module to be executed directly using python -m example.module on the command line (pip for instance does this for python -m pip to work). However, for a standard entry point, just reference the module:attr directly and it should do what you expect.


    Also, please consider going through the documentation about packaging on the Python site, as it will address how to properly package a project (which setuptools documentation don't actually teach, as you noted). Also consider creating a directory called rpn and putting your rpn*.py files into there to better organize your program code into its own namespace (so to make the entry point as you originally defined it (e.g. makeHelp = rpn.makeHelp:main work; naturally this comes with a change in how the main function is imported (from rpn.makeHelp import main)), though I note that on PyPI (the official python package index) that there is already a rpn package.