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...

7
  • Try removing the . before the values. Commented May 28, 2021 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 Commented May 28, 2021 at 11:32
  • @furas: (1) is SyntaxError, (2) and (3) yield another problem ImportError: attempted relative import with no known parent package Commented May 28, 2021 at 13:09
  • you should show structure of folders and files - so we could reproduce this situation and test it. Commented May 28, 2021 at 13:13
  • @furas: Added explicit directory tree Commented May 28, 2021 at 13:19

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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?
I see, seems I misunderstood a little. Does it fit your problem now?
Yes! That is exactly what I was looking for, thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.