Search code examples
pythonsetuptoolsdocopt

Python cli module import not found


I am trying to run a packaged cli, which does dynamic imports. When I run the code through the main cli script it works as expected. However after I package the code up using setup.py sdistand then install the dist tar with pip. The cli itself gives an import error ImportError: No module named. All of the modules are in the same folder as the cli.py file.

This is how I have created my main called cli.py

def main():
    args = docopt(__doc__, version="1.0")
    argv = [args['<command>']] + args['<args>']
    module = importlib.import_module(args['<command>'])
    print(docopt(module.__doc__, argv=argv))

if __name__ == '__main__':
    main()

And my setup.py looks like this

from setuptools import setup
setup(
    name='testing-cli',
    version='0.0.1',
    packages=['testing']
    entry_points = {
        'console_scripts': ['testing-cli = testing.cli:main'],
    }
)

Any ideas as to why when packaged I get an import error when it is packaged but when running like ./cli.py <arg> it imports fine?


Solution

  • The script will import modules from its directory, not from your current directory. That means that if you run /some/directory/cli.py module, it will look for module in /some/directory/module.py.

    I presume that you're running cli.py from the directory with the other modules, but when it gets installed, testing-cli goes to some /usr/local/bin which doesn't contain the other modules. They are instead stored in a testing package on sys.path. So you actually need to do this:

    importlib.import_module("testing." + args['<command>'])
    

    or possibly through a relative import:

    importlib.import_module("." + args['<command>'], package="testing")
    

    This will mean that your script will stop working when you run it from the build directory, unless you installed the package. To fix that, use python setup.py develop, which will mock-install the package onto sys.path. (Or more straightforward, export PYTHONPATH=. That will put your current directory on sys.path and make the testing subdir visible as a package)