Python Forum

Full Version: Setuptools CLI program ModuleNotFoundError after splitting up module into package
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi community!
I wrote a simple command line tool that basically copies Spotify tracks from one playlist to another (minus the duplicates).
For the application itself I used click and spotipy.
I used setuptools in order to be able to install the program and call it from the command line everywhere.
At first my program was in two files and the project structure looked roughly like that:

spotify-playlist-merger
│ configuration.py
│ setup.py
│ spotifypm.py

So the main code is in spotifypm.py while configuration.py is a Data Class used by spotifypm.py (It also should be named after CamelCase, right?).
If I then executed "pip3 install ." in the directory the program would install without any issues. After that I could access my program for example by entering "spotifypm --help" in the terminal.
The setup.py looks like this:
from setuptools import setup

setup(
    name='spotifypm',
    version='0.1.0',
    py_modules=['spotifypm', 'configuration'],
    install_requires=[
        'Click',
        'loguru',
        'spotipy',
        'PyYAML'
    ],
    entry_points={
        'console_scripts': [
            'spotifypm = spotifypm:cli',
        ],
    },
)
Because I thought that having all the logic in one big file was a little confusing, I decided to refactor my code. The structure ended up looking like this:

spotify-playlist-merger
│ setup.py
└─spotify_playlist_merger
.....│ __init__.py
.....│ Configuration.py
.....│ helpers.py
.....│ spotifypm.py


Executing the code via my IDE directly worked without issues but if I tried to install the program again via the setup.py and then executing "spotifypm --help" in the terminal returns the following error:

Traceback (most recent call last):
  File "C:\Users\Clemens\AppData\Local\Programs\Python\Python310\Scripts\spotifypm-script.py", line 33, in <module>
    sys.exit(load_entry_point('spotifypm==0.1.0', 'console_scripts', 'spotifypm')())
  File "C:\Users\Clemens\AppData\Local\Programs\Python\Python310\Scripts\spotifypm-script.py", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "C:\Users\Clemens\AppData\Local\Programs\Python\Python310\lib\importlib\metadata\__init__.py", line 162, in load
    module = import_module(match.group('module'))
  File "C:\Users\Clemens\AppData\Local\Programs\Python\Python310\lib\importlib\__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "C:\Users\Clemens\AppData\Local\Programs\Python\Python310\lib\site-packages\spotify_playlist_merger\spotifypm.py", line 3, in <module>
    import Configuration
ModuleNotFoundError: No module named 'Configuration'
My setup.py looks like this:

import setuptools
from setuptools import setup

setup(
    name='spotifypm',
    version='0.1.0',
    packages=setuptools.find_packages(),
    include_package_data=True,
    install_requires=[
        'Click',
        'loguru',
        'spotipy',
        'PyYAML'
    ],
    entry_points={
        'console_scripts': [
            'spotifypm = spotify_playlist_merger.spotifypm:cli',
        ],
    },
)
Can anyone help me out here? I tried to replicate what they did in the documentation of click: https://click.palletsprojects.com/en/8.0.x/setuptools/
For a closer look, the project is on gitlab as well: https://gitlab.com/clemwo/spotify-playli...ree/master
I solved the issue by creating a subpackage called "scripts" where I placed the spotifypm.py file and left the other files in the package above that. My setup.py now looks like this:

import setuptools
from setuptools import setup

setup(
    name='spotifypm',
    version='0.1.0',
    packages=setuptools.find_packages(),
    include_package_data=True,
    install_requires=[
        'Click',
        'loguru',
        'spotipy',
        'PyYAML'
    ],
    entry_points={
        'console_scripts': [
            'spotifypm = spotify_playlist_merger.scripts.spotifypm:cli',
        ],
    },
)
The structure is as follows:

spotify-playlist-merger
│ setup.py
└─spotify_playlist_merger
.....│ __init__.py
.....│ Configuration.py
.....│ helpers.py
.....└─scripts
..........│ __init__.py
..........│ spotifypm.py

I'd still be interested why the entry point apparently has to reside in an extra package? Why didnt it work before?