pythonpython-3.xsetuptoolspython-wheel

Locally installed Python wheel throwing ModuleNotFoundError


Recently I've been trying to learn a bit more about how to build python modules and share code amongst my personal projects. This is my first attempt and I'm running into errors and I'm having a tough time finding solutions through normal searches. I'm hoping you guys can point me in the right direction and maybe provide some insight on what's causing the issue under the hood.

I've built a super simple "card" project. It has the following layout...

And here's the contents of a few of the files from the project.

cards/setup.py

import time
from setuptools import setup, find_packages

setup(
    name="pycard",
    version=f"0.1.{int(time.time())}",
    package_dir={"": "src"},
    packages=find_packages(where="src"),
    author="*****",
    python_requires=">=3.12",
)

cards/src/pycard/_init_.py

from src.pycard.Cards import Card, Rank, Suit, RankSuitContext, AcesHighContext, RankSuitCard, CardContext
from src.pycard.Decks import Stack, StackContext, FiftyTwoCardDeck

Everything seems to work fine within my project. Unit tests pass, etc. To build, I run...

python setup.py sdist bdist_wheel

This creates the build, dist, and egg directories with a dist/pycard-version.whl file.

Now I create a separate project, create a new Conda environment, and run...

pip install path/to/pycard-*version*.whl

This command completes successfully and running pip list shows pycard in my package list.

(card-games) me@computer card-games % pip list
Package    Version
---------- --------------
pip        25.0
pycard     0.1.1745085939
setuptools 75.8.0
wheel      0.45.1

The problem shows up when I try to import and use the package in this new project. Here's a simple example...

from pycard.Cards import RankSuitCard, Rank, Suit

if __name__ == '__main__':
    print(RankSuitCard(Rank.ACE, Suit.SPADES))

Executing this gives me the following error...

Traceback (most recent call last):
  File "/Users/me/development/card-games/src/pycardgames/main.py", line 1, in <module>
    from pycard.Cards import RankSuitCard, Rank, Suit
  File "/Users/me/miniconda3/envs/card-games/lib/python3.12/site-packages/pycard/__init__.py", line 1, in <module>
    from src.pycard.Cards import Card, Rank, Suit, RankSuitContext, AcesHighContext, RankSuitCard, CardContext
ModuleNotFoundError: No module named 'src.pycard'

What's interesting is my IDE (PyCharm) seems to understand the import, letting me reference different types, click into them and view source, etc.

My gut is it has something to do with how I'm referencing the classes in the original _init_.py (src.pycard.file), but most of the tutorials I found online use that pattern. I also haven't found much on details in terms of referencing classes in _init_.py and how that impacts importing them once you've build the package.

Any help you guys can provide would be greatly appreciated.


Solution

  • When you say package_dir={"": "src"} in your setup.py, you are saying that your root modules and packages can be found in the src folder. That means src should not be form part of your imports.

    You are likely including src in your imports because your test runner was not finding your source code. Instead of including src in your imports, you could install your package as an "editable" package into your development virtual environment. This is just a fancy way to tell code running from within your venv, that when it is looking for the pycard package, that it should look inside the src directory. Any updates to your source code will be reflected in new test runs without having the reinstall your package. Your can do an editable install like so.

    pip install --editable path/to/cards
    

    path/to/cards should be the path to the directory that contains your setup.py file.

    Alternatively, you could instead tell your test runner where to find your source code by using the PYTHONPATH environment variable. This environment variable is used to tell python which directories to look in when searching for modules eg.

    cd path/to/cards
    export PYTHONPATH=./src
    pytest tests