pythonpython-importpython-module

My privately developed and installed module gives "ModuleNotFoundError: No module named 'jbpy'" at import


I'm implementing my own Python module package, called jbpy. I'm using setuptools with a pyproject.toml file as the build system.

I'm working on Ubuntu 24.04, but I also get the error under WSL on a Windows 11 machine. I have also tried to install the module as root, to make it globally available. That also produces the same error, when I try to import it.

The module builds fine, and there are no problems installing the module (I use pip to install the wheel file).

Edit: It seems that it's unclear what I'm asking, so I will try to elaborate. I have built the module package, and installed it in Python 3 on my machine, by using pip.

As far as I can see it's installed correctly, and the directory it's installed in, is in one of the paths where Python is looking for packages. I have tried my best to show this, with the information below.

But when I try to import it I get this error: "ModuleNotFoundError: No module named 'jbpy'".

The Python in the shebang of the import test script is the same as the Python I get by calling python3 from my command line.

So either Python isn't looking in the paths I think it's looking in, or I have missed something crucial when implementing the package, and setting up the pyproject.toml file.

How do I solve this? I have googled and googled, but all the solutions I have found didn't help solve the problem. They helped me clean up my code, and also helped me understand more about implementing and installing a Python module package. But they didn't solve the main problem, which is that Python can't find the package at import time, although it's telling me that it is indeed installed. So Python's own import command doesn't agree with the rest of Python?

I use this command to build the package:

python3 -m build

And I use this command to install the package:

python3 -m pip install dist/jbpy-0.0.2.9-py3-none-any.whl

When I try to import the module I get this error:

$ ./importtest.py 
Traceback (most recent call last):
  File "/home/jbang/development/projects/jbpy/test/./importtest.py", line 8, in <module>
    import jbpy.logging
ModuleNotFoundError: No module named 'jbpy'

This is the python in my bash path:

$ which python3
/usr/bin/python3

The importtest.py script is very simple:

#!/usr/bin/python3
# -*- coding: UTF-8 -*-

# Test importing the module

import jbpy.logging

if __name__ == "__main__":
    print("Modules imported")

I'm sure the module is installed, so why can't Python find it?

This is the details about how the module was installed:

$ python3 -m pip install dist/jbpy-0.0.2.9-py3-none-any.whl
Defaulting to user installation because normal site-packages is not writeable
Processing ./dist/jbpy-0.0.2.9-py3-none-any.whl
Installing collected packages: jbpy
  Attempting uninstall: jbpy
    Found existing installation: jbpy 0.0.2.8
    Uninstalling jbpy-0.0.2.8:
      Successfully uninstalled jbpy-0.0.2.8
Successfully installed jbpy-0.0.2.9

$ pip list | grep jbpy
jbpy                       0.0.2.9

$ pip show jbpy
Name: jbpy
Version: 0.0.2.9
Summary: A package with utility functions to make my Python life easier.
Home-page: 
Author: 
Author-email: Jens Bang <jbpy@snej.dk>
License: 
Location: /home/jbang/.local/lib/python3.12/site-packages
Requires: 
Required-by:

And the configuration of module paths in Python is this:

sys.executable='/usr/bin/python3'

sys.path = [
    '/home/jbang/development/projects/jbpy/test',
    '/usr/lib/python312.zip',
    '/usr/lib/python3.12',
    '/usr/lib/python3.12/lib-dynload',
    '/home/jbang/.local/lib/python3.12/site-packages',
    '/usr/local/lib/python3.12/dist-packages',
    '/usr/lib/python3/dist-packages',
    '/usr/lib/python3.12/dist-packages'
]

sysconfig.get_default_scheme()='posix_local'
sysconfig.get_preferred_scheme("prefix")='posix_local'
sysconfig.get_preferred_scheme("home")='posix_home'
sysconfig.get_preferred_scheme("user")='posix_user'

sysconfig.get_paths('posix_local') = {
    'stdlib'      : '/usr/lib/python3.12',
    'platstdlib'  : '/usr/lib/python3.12',
    'purelib'     : '/usr/local/lib/python3.12/dist-packages',
    'platlib'     : '/usr/local/lib/python3.12/dist-packages',
    'include'     : '/usr/include/python3.12',
    'platinclude' : '/usr/include/python3.12',
    'scripts'     : '/usr/local/bin',
    'data'        : '/usr/local'
}
sysconfig.get_paths('posix_home') = {
    'stdlib'      : '/usr/lib/python',
    'platstdlib'  : '/usr/lib/python',
    'purelib'     : '/usr/lib/python',
    'platlib'     : '/usr/lib/python',
    'include'     : '/usr/include/python',
    'platinclude' : '/usr/include/python',
    'scripts'     : '/usr/bin',
    'data'        : '/usr'
}
sysconfig.get_paths('posix_user') = {
    'stdlib'     : '/home/jbang/.local/lib/python3.12',
    'platstdlib' : '/home/jbang/.local/lib/python3.12',
    'purelib'    : '/home/jbang/.local/lib/python3.12/site-packages',
    'platlib'    : '/home/jbang/.local/lib/python3.12/site-packages',
    'include'    : '/home/jbang/.local/include/python3.12',
    'scripts'    : '/home/jbang/.local/bin',
    'data'       : '/home/jbang/.local'
}

sysconfig.get_python_version()='3.12'
sysconfig.get_platform()='linux-x86_64'
os.name='posix'
sysconfig.get_path('stdlib')     = '/usr/lib/python3.12'
sysconfig.get_path('platstdlib') = '/usr/lib/python3.12'
sysconfig.get_path('purelib')    = '/usr/local/lib/python3.12/dist-packages'
sysconfig.get_path('platlib')    = '/usr/local/lib/python3.12/dist-packages'
sysconfig.get_path('include')    = '/usr/include/python3.12'
sysconfig.get_path('scripts')    = '/usr/local/bin'
sysconfig.get_path('data')       = '/usr/local'

My pyproject.toml file looks like this:

[build-system]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"

[project]
name = "jbpy"
version = "0.0.2.9"
authors = [{name = "Jens Bang", email = "xxxx@xxxx.xx"}]
description = "A package with utility functions to make my Python life easier."
readme = "README.md"
requires-python = ">=3.6"
license = "BSD-3-Clause"
classifiers = [
    "Programming Language :: Python :: 3",
]

[tool.setuptools.packages.find]
where = ["src"]  # list of folders that contain the packages (["."] by default)
include = ["argparse*", "logging*", "json*"]  # package names should match these glob patterns (["*"] by default)
exclude = ["__*"]  # exclude packages matching these glob patterns (empty by default)
namespaces = false  # to disable scanning PEP 420 namespaces (true by default)

My files are arranged like this:

.
├── changelog.md
├── dist/
├── generate_package.sh
├── install_package.sh
├── pyproject.toml
├── README.md
├── requirements.txt
├── src
│   ├── argparse.py
│   ├── general.py
│   ├── __init__.py
│   ├── jbpy.egg-info/
│   ├── json.py
│   └── logging.py
├── test/
└── venv/

Solution

  • Your pyproject.toml file is not identifying anything to be installed.

    I created a copy of your files listed above and looked at the resulting source distribution. Note the lack of any python files in the distribution:

    $ tar tf dist/jbpy-0.0.2.9.tar.gz
    jbpy-0.0.2.9/
    jbpy-0.0.2.9/PKG-INFO
    jbpy-0.0.2.9/README.md
    jbpy-0.0.2.9/pyproject.toml
    jbpy-0.0.2.9/setup.cfg
    jbpy-0.0.2.9/src/
    jbpy-0.0.2.9/src/jbpy.egg-info/
    jbpy-0.0.2.9/src/jbpy.egg-info/PKG-INFO
    jbpy-0.0.2.9/src/jbpy.egg-info/SOURCES.txt
    jbpy-0.0.2.9/src/jbpy.egg-info/dependency_links.txt
    jbpy-0.0.2.9/src/jbpy.egg-info/top_level.txt
    

    If I make the following changes, it works:

    1. mkdir src/jbpy
    2. mv src/* src/jbpy (ignore error)
    3. make this change to pyproject.toml:
    --- pyproject.toml.orig 2025-07-23 15:19:47.524811800 -0700
    +++ pyproject.toml      2025-07-23 15:20:00.118691500 -0700
    @@ -16,6 +16,6 @@
    
     [tool.setuptools.packages.find]
     where = ["src"]  # list of folders that contain the packages (["."] by default)
    -include = ["argparse*", "logging*", "json*"]  # package names should match these glob patterns (["*"] by default)
    -exclude = ["__*"]  # exclude packages matching these glob patterns (empty by default)
    +include = ["*"]  # package names should match these glob patterns (["*"] by default)
    +exclude = []  # exclude packages matching these glob patterns (empty by default)
     namespaces = false  # to disable scanning PEP 420 namespaces (true by default)
    

    After building, the source dist now includes the python files:

    $ tar tf dist/jbpy-0.0.2.9.tar.gz
    jbpy-0.0.2.9/
    jbpy-0.0.2.9/PKG-INFO
    jbpy-0.0.2.9/README.md
    jbpy-0.0.2.9/pyproject.toml
    jbpy-0.0.2.9/setup.cfg
    jbpy-0.0.2.9/src/
    jbpy-0.0.2.9/src/jbpy/
    jbpy-0.0.2.9/src/jbpy/__init__.py
    jbpy-0.0.2.9/src/jbpy/argparse.py
    jbpy-0.0.2.9/src/jbpy/general.py
    jbpy-0.0.2.9/src/jbpy/json.py
    jbpy-0.0.2.9/src/jbpy/logging.py
    jbpy-0.0.2.9/src/jbpy.egg-info/
    jbpy-0.0.2.9/src/jbpy.egg-info/PKG-INFO
    jbpy-0.0.2.9/src/jbpy.egg-info/SOURCES.txt
    jbpy-0.0.2.9/src/jbpy.egg-info/dependency_links.txt
    jbpy-0.0.2.9/src/jbpy.egg-info/top_level.txt
    

    and the import works:

    $ ~/py3/scripts/pip install dist/jbpy-0.0.2.9-py3-none-any.whl
    Processing c:\users\johnson\temp\t\dist\jbpy-0.0.2.9-py3-none-any.whl
    Installing collected packages: jbpy
    Successfully installed jbpy-0.0.2.9
    $ ~/py3/scripts/python importtest.py
    logging
    Modules imported
    

    (the "logging" output is from the fake logging.py file I created)