2

I'd like to make a cli tool and found this as a reference: https://python-packaging.readthedocs.io/en/latest/command-line-scripts.html

So I created a directory with the following structure

$ tree modules_and_packages
modules_and_packages
├── bin
│   ├── cli_script
│   └── cli_script~
├── __init__.py
├── setup.py
├── setup.py~
├── some_module.py
└── some_module.py~

I can install the module through pip without any problems:

pip3 install ./modules_and_packages
Processing ./modules_and_packages
Installing collected packages: some-module
  Running setup.py install for some-module ... done
Successfully installed some-module-0.0.0

But when I run cli_script on the command line, I get the following error:

Traceback (most recent call last):
  File "/home/david/.local/bin/cli_script", line 3, in <module>
    import modules_and_packages
ModuleNotFoundError: No module named 'modules_and_packages'

Here's what's in the files:

setup.py

from setuptools import setup

setup(
    name='some_module',
    py_modules=['some_module'],
    scripts=['bin/cli_script']
)

some_module.py

def​ some_func():
    return​ 42

bin/cli_script

#!/usr/bin/env python3

import modules_and_packages
print('hello')

As you can see, I'm just trying to test the simplest case for making a command line tool using a module. What am I doing wrong here?

EDIT:

Error message I get when trying to import some_module after trying hoefling's suggestion

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/david/.local/lib/python3.6/site-packages/some_module.py", line 1
    ​def​ some_func():
        ^
SyntaxError: invalid character in identifier
hoefling
  • 39,011
  • 9
  • 90
  • 132
David J.
  • 1,313
  • 7
  • 37
  • 69

2 Answers2

2

You are not including modules_and_packages in the distribution. This is because your project structure is wrong - compare it with the one in the tutorial you referenced. It should look like the following:

myproject
├── modules_and_packages
│   ├── __init__.py
│   └── some_module.py
├── setup.py
└── bin
    └── cli_script

Now include the package modules_and_packages to the dist in the setup script:

# setup.py

setup(
    name='some_module',
    packages=['modules_and_packages'],
    scripts=['bin/cli_script']
)

It gets even easier if you use the setuptools.find_packages() function for package collection:

from setuptools import find_packages, setup

setup(
    name='some_module',
    packages=find_packages(),
    scripts=['bin/cli_script']
)
hoefling
  • 39,011
  • 9
  • 90
  • 132
  • I don't know if this is related, but I changed the structure as you suggested, and it seems that the correct module is being imported, but I still get the following error message: ``` edit: I'll add the error message to the original question ``` – David J. Apr 12 '20 at 12:16
  • That's because you have garbage characters hidden in the line, like zero-width spaces and stuff. Copy the line containing the `def some_func():` and print its repr from command line, e.g. `python -c "print(repr(' def some_func():'))"`. This will show the codes of garbage characters you have to delete or replace. – hoefling Apr 12 '20 at 13:59
  • Most probably you have copied the code from some webpage that appends additional formatting characters that make you code not runnable. – hoefling Apr 12 '20 at 13:59
  • I thought of that as well, so I typed it out by hand, but it still isn't working. At any rate, I made a separate question for that. Thanks for the help. https://stackoverflow.com/questions/61171637/syntaxerror-invalid-character-in-identifier-despite-there-being-almost-certa – David J. Apr 12 '20 at 18:21
1

The guide you are following is 5 years old and a little vague. You could setup your cli-tool like so:

~ tree modules_and_packages
├── bin
│   └── cli_script
├── setup.py
└── some_module
    ├── __init__.py
    └── some_module.py

setup.py

from setuptools import setup

setup(
    name='some_module',
    py_modules=['some_module'],
    scripts=['bin/cli_script']
)

some_module/__init__.py

from .some_module import *

some_module/some_module.py

def some_func():
    return 42

bin/cli_script

#!/usr/bin/env python3

import some_module

print('hello')
print(some_module.some_func())

Then to install, in the modules_and_packages directory, run:

~ python setup.py develop

Then running:

~ cli_script
hello
42

Alternatively, if you are familiar with cookiecutter, I would recommend using this template for a python package, as it has a built in cli interface.

Alex
  • 3,841
  • 2
  • 15
  • 32