I posted a solution on the other question but someone pointed me to this. So let me give an answer to the "how" part of the OP's question "Why and how do you define a 'package'?"
The simple answer is you can edit __package__
and add the folder containing the root package to sys.path
. But how do you do this cleanly and not totally clutter up the top of the Python script?
Suppose your code some_script.py
resides somewhere within a directory structure which looks like:
project_folder
|-- subdir
|-- ab
|-- cd
|-- some_script.py
|-- script1.py
|-- script2.py
|-- script3.py
|-- script4.py
|-- other_folder
|-- script5.py
And you need your package to be subdir.ab.cd
without knowing ab
or cd
or even the number of nested levels (as long as none of the intermediate levels are called "subdir" as well). Then you could use the following:
import os
import sys
if not __package__:
__package__, __root__ = (
(lambda p, d:
(".".join(p[-(n := p[::-1].index(d) + 1):]), os.sep.join(p[:-n])))(
os.path.realpath(__file__).split(os.sep)[:-1], "subdir"))
sys.path.insert(0, __root__)
from .script1 import *
from ..script2 import *
from ...script3 import *
from subdir.script3 import *
from script4 import *
from other_folder.script5 import *
Suppose your code some_script.py
resides somewhere within a directory structure which looks like:
project_folder
|-- ab
|-- cd
|-- some_script.py
|-- script1.py
|-- script2.py
|-- script3.py
|-- other_folder
|-- script4.py
And you need your package to be ab.cd
without knowing ab
or cd
but the depth of the package is guaranteed to be 2. Then you could use the following:
import os
import sys
if not __package__:
__package__, __root__ = ( #
(lambda p, n: (".".join(p[-n:]), os.sep.join(p[:-n])))(
os.path.realpath(__file__).split(os.sep)[:-1], 2))
sys.path.insert(0, __root__)
from .script1 import *
from ..script2 import *
from script3 import *
from other_folder.script4 import *
With the sys.path
including the project folder, you also of course do any absolute imports from there. With __package__
correctly computed, one can now do relative imports as well. A relative import of .other_script
will look for other_script.py in the same folder as some_script.py
. It is important to have one additional level in the package hierarchy as compared to the highest ancestor reached by the relative path, because all the packages traversed by the ".."/"..."/etc will need to be a Python package with a proper name.