Search code examples
pythonpython-importpython-modulepython-packagingpython-wheel

Building a python wheel : "no module named ______"


I am trying to build a python wheel on a complex program, and I have issues with imports. So I managed to reproduce it on a elementary example. The program works fine when directly called, but does not works when trying to import it from an installed wheel.

Here is my example structure :

wheel_test
|-setup.py
|-toto
  |-__init__.py
  |-main.py
  |-tata.py
  |-subfolder
    |-__init__.py
    |-titi.py

main.py

# coding: utf8
from tata import tata
import subfolder.titi as titi

def test_fct():
  t1 = tata()
  print(f't1 number = {t1.random_number}')
  print(f'Is t1 number even: {titi.is_even(t1.random_number)}')

test_fct()

(Here t1 is an instance of class tata, which just store a random number in self.random_number, and the titi.py file just contains an is_even(number) function, telling if a number is even or not)

This runs just fine when launched :

>  python "some_path\wheel_test\toto\main.py"
t1 number = 11
Is t1 number even: False

But then, I want to make a wheel of this, using a setup file : setup.py

# coding: utf8
import os
from setuptools import find_packages, setup

root = os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))
os.chdir(root)

setup(
  name='toto',
  packages=find_packages(),
  version='0.1',
  description='Wheel test',
  author='Arkeen',
  license='None',
)

(I copied the os.chdir lines from [this post])

This create a toto-0.1-py3-none-any.whl file. I then run $ pip install path_to_newly_created_whell/toto-0.1-py3-none-any.whl, which seems OK.

But when I want to run this code :

# coding: utf8
import toto.main as maintoto
maintoto.test_fct()

I got the following error :

File "some_path\wheel_test\toto\main.py", line 2, in <module>
  from tata import tata
ModuleNotFoundError: No module named 'tata'

So why this outcome ? Why does this code runs fine with python but not as a wheel ?


Edit : Thanks @Countour-Integral for pointing me the right direction. Still, I had to go deeper into Python modules, packages, relative and absolute import to fully understand this. So, to anyone here having a similar issue, I would recommend to read this :


Solution

  • You are getting a ModuleNotFoundError because the interpreter is searching for a global module named tata, and not looking in the current directory. You are also repeating this behavior, in your first code block.

    In the following snippet you are running main.py as python path/to/main.py

    import subfolder.titi as titi # Does not throw any error
    

    the reason this does not throw any error is because, you are not running main as a package, so when you reference subfolder it checks in your current working directory.

    Instead when you try to run setup.py to build, the interpreter will run everything as a package, and thus, will not look in any local directories. You'll have to change your imports like this

    # notice the .
    from .subfolder import titi 
    from .tata import tata 
    

    Your file structure looks something like this

    -> module (directory)
    ---- main.py
    ---- tata.py
    ----> subfolder (directory)
    -------- titi.py
    

    to run any of these as a module, instead of using python main.py you'll have to cd 1 step back, into the parent directory of module and use python -m module main.py,and make sure that you are using imports with . to refrence files in the same directory. Normally this is how development of python-pip packages is done, in order to make it easier in the setup.py proccess.