Say I have a collection of scripts organized for convenience in a directory structure like so:
root
│ helpers.py
│
├───dir_a
│ data.txt
│ foo.py
│
└───dir_b
data.txt
bar.py
foo.py
and bar.py
are scripts doing different things, but they both load their distinct data.txt
the same way. To avoid repeating myself, I wish to put a load_data()
function in helpers.py
that can be imported while running scripts in subdirectories dir_a
and dir_b
.
I have tried to import helpers in dir_a/foo.py
using a relative import like this:
from ..helpers import load_data
foo_data = load_data('data.txt')
# ...rest of code...
But directly running foo.py
as a script fails on line 1 with an ImportError
:
Traceback (most recent call last):
File "path/to/file/foo.py", line 1, in <module>
from ..helpers import load_data
ImportError: attempted relative import with no known parent package
Is the spirit of what I'm attempting possible in python (i.e. simple and without modifying sys.path
)? From Case 3: Importing from Parent Directory on this site, the answer seems to be no, and I need to restructure my collection of scripts to avoid importing one level up.
Bonus question: if not possible, what is the rationale for why not?
If you create an empty
__init__.py
in the root directory (which turns root into a package) and then call yourfoo.py
script like this:python -m root.dir_a.foo
it should work, I think, so long as you call it from above 'root'. I've just tried something similar on my machine and it worked.
Alternatively, because you have now made 'root' a package, you can just:
from root import helpers
Of course, I assume your root directory isn't really called 'root', in which case you should change it appropriately. I'm also assuming you're using Python3 - it won't work in 2.7.
As to the rationale of why this isn't possible without making root a package, I think the idea is that your code should be location agnostic. In other words, you shouldn't rely on your foo.py knowing where it is on the system. I'm not an expert, but actually it maybe that foo.py doesn't know this, in which case it has no way of resolving
../helpers
.