I've tried reading through questions about sibling imports and even the package documentation, but I've yet to find an answer.
With the following structure:
├── LICENSE.md
├── README.md
├── api
│ ├── __init__.py
│ ├── api.py
│ └── api_key.py
├── examples
│ ├── __init__.py
│ ├── example_one.py
│ └── example_two.py
└── tests
│ ├── __init__.py
│ └── test_one.py
How can the scripts in the examples
and tests
directories import from the
api
module and be run from the commandline?
Also, I'd like to avoid the ugly sys.path.insert
hack for every file. Surely
this can be done in Python, right?
TLDR
This method does not require setuptools, path hacks, additional command line arguments, or specifying the top level of the package in every single file of your project.
Just make a script in the parent directory of whatever your are calling to be your
__main__
and run everything from there. For further explanation continue reading.Explanation
This can be accomplished without hacking a new path together, extra command line args, or adding code to each of your programs to recognize its siblings.
The reason this fails as I believe was mentioned before is the programs being called have their
__name__
set as__main__
. When this occurs the script being called accepts itself to be on the top level of the package and refuses to recognize scripts in sibling directories.However, everything under the top level of the directory will still recognize ANYTHING ELSE under the top level. This means the ONLY thing you have to do to get files in sibling directories to recognize/utilize each other is to call them from a script in their parent directory.
Proof of Concept In a dir with the following structure:
Main.py
contains the following code:sib1/call.py contains:
and sib2/callsib.py contains:
If you reproduce this example you will notice that calling
Main.py
will result in "Got Called" being printed as is defined insib2/callsib.py
even thoughsib2/callsib.py
got called throughsib1/call.py
. However if one were to directly callsib1/call.py
(after making appropriate changes to the imports) it throws an exception. Even though it worked when called by the script in its parent directory, it will not work if it believes itself to be on the top level of the package.For siblings package imports, you can use either the insert or the append method of the [sys.path][2] module:
This will work if you are launching your scripts as follows:
On the other hand, you can also use the relative import:
In this case you will have to launch your script with the '-m' argument (note that, in this case, you must not give the '.py' extension):
Of course, you can mix the two approaches, so that your script will work no matter how it is called:
You need to look to see how the import statements are written in the related code. If
examples/example_one.py
uses the following import statement:...then it expects the root directory of the project to be in the system path.
The easiest way to support this without any hacks (as you put it) would be to run the examples from the top level directory, like this:
Here is another alternative that I insert at top of the Python files in
tests
folder: