ImportError: cannot import name 'main' from '__main__' on executing a command from my own package

Hi,

I’m having trouble with packaging. I upload my package with twine to testpypi and I download it with pip, no problem here. However, when I’m trying to use it, I get an ImportError : cannot import name 'main' from '__main__'.

first I though the problem was in my code without talking of packaging but I test it and it works fine. So I guess the problem is in my setup.py file or in my file tree maybe.

My file tree is looking like that :

filetree

Moreover, when I’m trying to import the ‘main’ function of __main__.py ‘manually’ in my code, it works so I really don’t know why this error occurs…

There is my setup.py file :

from setuptools import setup, find_packages
from io import open
from os import path
import pathlib

# The directory containing this file
HERE = pathlib.Path(__file__).parent

# The text of the README file
README = (HERE / "README.md").read_text()

# automatically captured required modules for install_requires in requirements.txt and as well as configure dependency links
with open(path.join(HERE, 'requirements.txt'), encoding='utf-8') as f:
    all_reqs = f.read().split('\n')

install_requires = [x.strip() for x in all_reqs if ('git+' not in x) and (
    not x.startswith('#')) and (not x.startswith('-'))]
dependency_links = [x.strip().replace('git+', '') for x in all_reqs if 'git+' not in x]

setup(
    name='pdal-parallelizer',
    description='A simple commandline app for parallelize your pdal treatments on point clouds',
    version='0.0.5',
    packages=find_packages(),  # list of all packages
    install_requires=install_requires,
    python_requires='>=2.7',  # any python greater than 2.7
    entry_points='''
        [console_scripts]
        pdal-parallelizer=__main__:main
    ''',
    author="Clément Alba (Métropole Européenne de Lille)",
    long_description=README,
    long_description_content_type="text/markdown",
    license='MIT',
    url='https://github.com/meldig/pdal-parallelizer',
    download_url='https://github.com/meldig/pdal-parallelizer',
    dependency_links=dependency_links,
    author_email='calba@lillemetropole.fr',
    classifiers=[
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.9",
    ]
)

And we can see in my __main.py__ there is a function called main :

@click.group()
@click.version_option('0.0.5')
def main():
    pass

if __name__ == "__main__":
    args = sys.argv
    if "--help" in args or len(args) == 1:
        print("pdal-parallelizer")
    main()

Did someone have an idea of what I’m doing wrong ?

Thank you in advance !:slight_smile:
Clément

The layout in your screenshot is confusing: you should separate the python files (pdal_parallelizer directory with its modules, including __init__ and __main__) and the other project files (readme, setup.py). Then, update your entry-point declaration to use the full name: pdal_parallelizer.__main__:main.

(BTW, your setup script does some work that’s not needed now: setuptools can read your readme for you, and you could have all your config in a toml or cfg file without setup.py — see Quickstart - setuptools 62.6.0.post20220623 documentation.
Reading requirements into install_requires is also not a good practice, but that’s a whole another topic.)

(Forum note: the line with ```python seems to mess up your code blocks; you need only ``` to close one)

Additionally, to clarify further, packages=find_packages() can’t find your package if you don’t have one, and all your code is just directly in the root, and with no __init__,py to be found. Therefore, your code either doesn’t get included at all when you build your sdists/wheel (and thus its not found when you try to import it or call its entrypoint on the command line).

Instead, move all your actual code into a src/your_package_name subdirectory, and add an empty file named __init__.py in the your_package_name subdirectory. Then, Setuptools will detect and include it automatically in recent versions (using the modern pyproject.toml configuration approach), or with just a bit of configuration in your setup.cfg

[options]
package_dir =
    = src

[options.packages.find]
where = src

This will also avoid the issue you saw where you can import it just fine when you happen to be in your project working directory, but it suddenly stops working if you leave it, and it won’t work for anyone else, because you’re actually using your local copy, not the one from the installed package.

Beyond that, I strongly recommend you follow the official Packaging tutorial, which starts out with guidance on how to structure your package to avoid these issues, as well as describes the simpler, modern and recommended configuration format using pyproject.toml, and for which Setuptools uses more sensible defaults for finding your package and its code that doesn’t require much configuration or fiddling.

3 Likes

Hi @CAM-Gerlach @merwok

I follow your advices and everything works fine ! Thanks a lot for helping me :slight_smile:

1 Like