1

I am trying to build a Python package. For simplification, I will only mention the parts which are relevant to the problem: The package (directory) is called moranpycess and inside it there are three relevant files:

  1. The initfile for the whole package __init__.py:
from .Individual import Individual
from .MoranProcess import MoranProcess
  1. A module called Individual which contains a class Individual.

  2. A module called MoranProcess which contains a class MoranProcess. At the top it imports the previous module with: import Individual.

I install the package with python -m pip install .
Then I run a test to see if the package can be imported properly: python -c 'import moranpycess'.

I get:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/runner/work/angry-moran-simulator/angry-moran-simulator/moranpycess/__init__.py", line 18, in <module>
    from .MoranProcess import MoranProcess
  File "/home/runner/work/angry-moran-simulator/angry-moran-simulator/moranpycess/MoranProcess.py", line 22, in <module>
    import Individual
ModuleNotFoundError: No module named 'Individual'
Error: Process completed with exit code 1.

This is strange to me since it seems that the Python interpreter can find the package, the package imports the respective classes but the interpreter also tries to execute the top-level module imports and (I don't know why) it does not find the module...
Am I doing something wrong here?

EDIT:

directory structure:

└── moranpycess
    ├── Individual.py
    ├── MoranProcess.py
    └── __init__.py

Update

I am considering the solution proposed here:
https://stackoverflow.com/a/49375740/2340598
However I don't know if this is the "right" way to organize a package...

maciek
  • 1,807
  • 2
  • 18
  • 30
  • Try removing the `.` before the values. – Bhavyadeep Yadav May 28 '21 at 10:41
  • maybe you should use relative import with dot `import .Individual` or `from . import Individual`. OR maybe you should use again `from .Individual import Individual` – furas May 28 '21 at 11:32
  • @furas: (1) is SyntaxError, (2) and (3) yield another problem `ImportError: attempted relative import with no known parent package` – maciek May 28 '21 at 13:09
  • you should show structure of folders and files - so we could reproduce this situation and test it. – furas May 28 '21 at 13:13
  • @furas: Added explicit directory `tree` – maciek May 28 '21 at 13:19
  • The answer you linked is horrid, please don't use it. Can you import with `from moranpycess.Individual import Individual` instead? – Arne May 31 '21 at 10:05
  • @Arne: This seems to work... Then in the initfile for the package I have `from .MoranProcess import MoranProcess` because if the user imports the whole package (intended usecase) I want to present the `MoranProcess` class directly in pkg's namespace. Would you say that setup is correct? – maciek May 31 '21 at 14:21

1 Answers1

1

Relative imports are a bit harder to get right than absolute imports. They are also more brittle (e.g., if you move a file doing the import, it breaks), so unless you have a good reason to use relative imports I'd suggest to just use absolute ones.

Following that, your public API as it is defined in your topmost __init__.py would look like this:

from moranpycess.MoranProcess import MoranProcess

__all__ = ["MoranProcess"]  # import hint that some tools use

While non-public objects can still be imported internally (e.g. in MoranProcess.py) in the following way:

from moranpycess.Individual import Individual 

The good thing about absolute imports is that no matter where in your package (or outside of it) you are, they always look the same.


If done like this, users that have installed your package should be able to use your objects like this:

from moranpycess import MoranProcess  # directly gets the object, not module

You should try to avoid messing with sys.path in order to get your imports to work, mainly because it isn't necessary. If all you're doing is writing regular python code that is meant to be used in a regular way, the builtin import mechanics should serve your use cases just fine.

If I were to import a package and noticed that sys.path changed, I'd suspect shenanigans and start looking for an alternative.

Arne
  • 17,706
  • 5
  • 83
  • 99
  • I am not sure `from moranpycess.Individual import Individual ` is required in the initfile since it is an internal class only used (and imported) by the `MoranProcess` module, not by the user. And I do not want to expose it to the user. Following that, I don't think it should be in the `__all__` field too... Would that be OK to remove it? Would the `MoranProcess` module still find it? and if so - how should it import the class `Individual`? – maciek Jun 01 '21 at 11:09
  • I see, seems I misunderstood a little. Does it fit your problem now? – Arne Jun 01 '21 at 11:25
  • Yes! That is exactly what I was looking for, thanks! – maciek Jun 02 '21 at 12:09