Surface level look
Normally, you can't. When importing a file, Python only searches the current directory, the directory that the entry-point script is running from, and sys.path
which includes locations such as the package installation directory (it's actually a little more complex than this, but this covers most cases).
The reason you don't see this problem for importing installed modules is because they are installed in a location that is already on your path, or the location is added to the path by the installing utility (pip
for example).
You can add to the Python path at runtime:
import sys
sys.path.insert(0, '../lib')
import file
You can also use sys.path.append('../lib')
, but then it will be searched for last in your path and may be overridden by previous path entries.
I have read through the import documentation extensively, and as far as I can tell, the answer to your question is no, there is no way to do this in a purely "Pythonic" way.
More in depth look:
Looking deeper into the import documentation explains this:
The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope. The search operation of the import statement is defined as a call to the __import__() function, with the appropriate arguments. The return value of __import__() is used to perform the name binding operation of the import statement.
Looking more closely at __import__:
__import__(name, globals=None, locals=None, fromlist=(), level=0)
Note: This is an advanced function that is not needed in everyday Python programming, unlike importlib.import_module().
This function is invoked by the import statement. It can be replaced (by importing the builtins module and assigning to builtins.__import__) in order to change semantics of the import statement, but doing so is strongly discouraged as it is usually simpler to use import hooks (see PEP 302) to attain the same goals and does not cause issues with code which assumes the default import implementation is in use. Direct use of __import__() is also discouraged in favor of importlib.import_module().
level specifies whether to use absolute or relative imports. 0 (the default) means only perform absolute imports. Positive values for level indicate the number of parent directories to search relative to the directory of the module calling __import__() (see PEP 328 for the details).
This makes me think that you can specify a level
in some way that may make the import automatically look on the parent path. I'm guessing that when import
is called from your app.py
that is not in it's own directory, level
is set to 0
and it searches the same level and deeper.
When you call import
in app.py
from a subfolder, level
is still set to 0
and thus can't find the other directories above it. I am investigating a "Pythonic" way of setting this level to 1
when running your script which would appear to fix this problem.