Python Import From Folder Above Without Using Path? - python

For Python 3, in this sandbox, foo has a function print_bar() I want to use in script1.
myApp/
main_folder/
helper/
__init__.py
foo.py
scripts/
script1.py
In script1:
from ..helpers import foo
foo.print_bar()
I am met with "ValueError: attempted relative import beyond top-level package".
Fine I'll try the sys path stuff thing I see on the other SO questions.
import os
import sys
# Add parent folder path to sys.path
cur_path = os.path.dirname(os.path.realpath(__file__))
parent_path = os.path.dirname(cur_path)
sys.path.append(parent_path)
from helpers import foo
foo.print_bar()
Okay this works but is a bit convoluted. Is there not a simpler way to say, "Go up a directory and the import x?"
Seems like relative import along the lines of "from ..helpers import foo" is what should work but am I not getting it right or am I not using it for its intended purpose?

Following methods can be used to include parent folder in python path:
import sys, os
sys.path.append('..') # method 1
sys.path.append(os.path.abspath('..')) # method 2
sys.path.append(os.pardir) # method 3

Related

python file needs to imports a class from another python file that is in the parent folder

i'm struggling with something that feels like it should be simple.
my current dir looks like this:
root/
└─ __init__.py (tried with it and without)
└─ file_with_class.py
└─ tests_folder/
└─ __init__.py (tried with it and without)
└─ unittest_for_class.py
unittest_for_class.py needs to import the class from file_with_class to test it, i tried to import it in various ways i found online but i just keep getting errors like:
(class name is same as file name lets say its called file_with_class)
File "tests_folder/unittest_for_class.py", line 3, in <module>
from ..file_with_class import file_with_class
ValueError: Attempted relative import in non-package
File "tests_folder/unittest_for_class.py", line 3, in <module>
from file_with_class import file_with_class
ImportError: No module named file_with_class
and others..
what is the correct way to import a class from a .py file that is in the parent folder ?
As a short explanation
import * from ..parent works if your program started at the parent level.
You import submodules which can have cross relations to other submodules or files in the package -> They are only relative inside a package not the os structure.
Option 1
you actually enter via a script in your parent folder and import your mentioned file as a submodule. Nicest, cleanest and intended way, but then your file is no standalone.
Option 2 - Add the parent dictionary to your path
sys.path.append('/path/to/parent')
import parent
This is a little bit dirty as you now have an extra path for your imports but still one of most easiest ones without much trickery.
Further Options and theory
There are quite a few posts here covering this topic relative imports covers quite a few good definitions and concepts in the answers.
Option 3 - Deprecated and not future proof importlib.find_loader
import os
import importlib
current = os.getcwd() # for rollback
os.chdir("..") # change to arbitrary path
loader = importlib.find_loader("parent") # load filename
assert loader
parent = loader.load_module() # now this is your module
assert parent
os.chdir(current) # change back to working dictionary
(Half of an) Option 4
When working with an IDE this might work, Spyder allows the following code. Standard python console does NOT.
import os
current = os.getcwd()
os.chdir("..")
import parent
os.chdir(current)
Following up on #Daraan's answer:
You import submodules which can have cross relations to other submodules or files in the package -> They are only relative inside a package not the os structure.
I've written an experimental, new import library: ultraimport which allows to do just that, relative imports from anywhere in the file system. It will give you more control over your imports.
You could then write in your unittest_for_class.py:
import ultraimport
MyClass = ultraimport("__dir__/../file_with_class.py", "MyClass")
# or to import the whole module
file_with_class = ultraimport("__dir__/../file_with_class.py")
The advantage is that this will always work, independent of sys.path, no matter how you run your script and all the other things that were mentioned.
You can add the parent folder to the search path with sys.path.append() like so:
import sys
sys.path.append('/path/to/parentdir')
from file_with_class import file_with_class
...
See also the tutorial for how Python modules and packages are handled
Just keep from file_with_class import file_with_class. Then run python -m test_folder.unittest_for_class. This supports running the script as if it is a module.

Python import from submodule of package exported by another file

I have imports.py containing:
import os as exported_os
and foo.py containing:
from imports import exported_os
print(exported_os.path.devnull) # works
from imports.exported_os.path import devnull # doesn't
Is there a way to make the second import work? I tried adding __path__ to imports.py and fiddling with it but couldn't get anything.
Actual usecase: os is some_library_version_n and exported_os is some_library_version (I'm trying to avoid having many instances of some_library_version_n across different files).
One approach
Directory structure:
__init__.py
foo.py
imports/
├ __init__.py
└ exported_os/
├ __init__.py
└ path.py
imports/exported_os/__init__.py:
from . import path
from os import * # not necessary for the question
# but it makes `exported_os` more like `os`
# e.g., `exported_os.listdir` will be callable
imports/exported_os/path.py:
from os.path import *
In this way, you can use exported_os as if it is os with a submodule path. Different with import, from takes modules and classes.
Another approach
imports.py:
import os
import sys
ms = []
for m in sys.modules:
if m.startswith('os'):
ms.append(m)
for m in ms:
sys.modules['imports.exported_os' + m[2:]] = sys.modules[m]
Or, by explicitly extending sys.modules you can use exported_os as if os with its submodules.
Why you cannot simply change the name of os
If you open .../lib/python3.9/os.py you can find the following line:
sys.modules['os.path'] = path
So even if you copy .../lib/python3.9/os.py to .../lib/python3.9/exported_os.py, the following does not work:
from exported_os.path import devnull
But if you change the line sys.modules['os.path'] to sys.modules['exported_os.path'] it works.
The error you are getting would be something like:
ModuleNotFoundError: No module named 'imports.exported_os'; 'imports' is not a package
When you code from imports import exported_os, then imports can refer to a module implemented by file imports.py. But when the name imports is part of a hierarchy as in from imports.exported_os.path import devnull, then imports must be a package implemented as a directory in a directory structure such as the following:
__init__.py
imports
__init__.py
exported_os
__init__.py
path.py
where directory containing the top-most __init__.py must be in the sys.path search path.
So, unless you want to rearrange your directory structure to something like the above, the syntax (and selective importing) you want to use is really not available to you without getting into the internals of Python's module system.
Although this is not a solution to your wanting to be able to do an from ... import ... due to your unique versioning issue, let me suggest an alternate method of doing this versioning. In your situation you could do the following. Create a package, my_imports (give it any name you want):
my_imports
__init__.py
The contents of __init__.py is:
import some_library_version_n as some_library_version
Then in foo.py and in any other file that needs this module:
from my_imports import *
This is another method of putting the versioning dependency in one file. If you had other similar dependencies, you would, of course, add them to this file and you could import from my_imports just the names you are interested. You still have the issue that you are importing the entire module some_library_version.
However, we could take this one step further. Suppose the various versions of your library had components A, B and C that you might be interested in importing individually or all together. Then you could do the following. Let's instead name the package some_library_version, since it will only be dealing with this one versioning issue:
some_library_version/init.py
from some_library_version_n import A
from some_library_version_n import B
from some_library_version_n import C
foo.py
from some_library_version import A, C
Most of the answers added are accured but dont add context of why works in that way, GyuHyeon explains it well but it just resumes it into import is a fancy file include system that checks into the std libraries, then the installed ones and finaly into the context provided, context is added on where is called and the from given.
This example gives the various method of importing a specific function dirname(), the lib os is just a folder, if you imagine that os is in your working folder the import path whoud be the same or './os' and beause python, everything is a class, so import will search for the .os/__init__.py so if your library dont have one importing the subdirs it will have no efect.
from os.path import dirname as my_fucntion # (A_2)
from os import path as my_lib # (B_2)
from os.path import dirname # (C_1)
from os import path # (B_1)
import os # (A_1)
if __name__ == '__main__':
print(os.path.dirname(__file__)) # (A_1)
print(path.dirname(__file__)) # (B_1)
print(dirname(__file__)) # (C_1)
print(my_lib.dirname(__file__)) # (B_2)
print(my_fucntion(__file__)) # (A_2)
You could try to go with sys.path.append(...), e.g.:
import sys
sys.path.append(<your path to devnull goes here>)
Maybe not so nice, but you could use the pathlib library and path joins to construct the path (but some assumptions on file structure unfortunately have to be made if you the files are in separate folder structures):
from pathlib import Path
from os import path
sys.path.append(path.join(str(Path(__file__).parents[<integer that tells how many folders to go up>]), <path to devnull>))
Instead of pathlib you could also use the dirname function from os.path.
After appending to the system path, you could just use:
import devnull

Accessing code in adjacent files

I have code in one folder, and want to import code in an adjacent folder like this:
I am trying to import a python file in innerLayer2, into a file in innerLayer1
outerLayer:
innerLayer1
main.py
innerLayer2
functions.py
I created the following function to solve my problem, but there must be an easier way? This only works on windows aswell and I need it to work on both linux and windows.
# main.py
import sys
def goBackToFile(layerBackName, otherFile):
for path in sys.path:
titles = path.split('\\')
for index, name in enumerate(titles):
if name == layerBackName:
finalPath = '\\'.join(titles[:index+1])
return finalPath + '\\' + otherFile if otherFile != False else finalPath
sys.path.append(goBackToFile('outerLayer','innerLayer2'))
import functions
Is there an easier method which will work on all operating systems?
Edit: I know the easiest method is to put innerLayer2 inside of innerLayer1 but I cannot do that in this scenario. The files have to be adjacent.
Edit: Upon analysing answers this has received I have discovered the easiest method and have posted it as an answer below. Thankyou for your help.
Use . and .. to address within package structure as specified by PEP 328 et al.
Suppose you have the following structure:
proj/
script.py # supposed to be installed in bin folder
mypackage/ # supposed to be installed in sitelib folder
__init__.py # defines default exports if any
Inner1/
__init__.py # defines default exports from Inner1 if any
main.py
Inner2/
__init__.py # defines default exports from Inner2 if any
functions.py
Inner1.main should contain import string like this:
from ..Inner2 import functions
If you have to use the current directory design, I would suggest using a combination of sys and os to simplify your code:
import sys, os
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from innerLayer2 import functions
Upon analysing answers I have received I have discovered the easiest solution: simply use this syntax to add the outerLayer directory to sys.path then import functions from innerLayer2:
# main.py
import sys
sys.path.append('..') # adds outerLayer to the sys.path (one layer up)
from innerLayer2 import functions
The easiest way is:
Move the innerLayer2 folder to inside the innerLayer1 folder
Add an empty file named __init__.py on the innerLayer2
On the main.py use the following:
import innerLayer2.functions as innerLayer2
# Eg of usage:
# innerLayer2.sum(1, 2)

Import * from all modules in a package in python

I have a package whose structure is like this:
/backends/
__init__.py
abc.py
def.py
ghi.py
xyz.py
common.py
The modules abc.py, def.py, ghi.py and xyz.py contain some common functions e.g. func0() and func1().
In the common.py module I am importing * from all modules like this:
from abc import *
from def import *
from ghi import *
from xyz import *
I don't think this is a Pythonic way to do this. Think about a few tens of such modules.
What I want is to have a single line statement which imports * from all the modules in the package. Something like this:
from backends import *
I tried this link, but couldn't get what I wanted. I created a variable __all__ in the __init__.py and assigned the list of all modules to it. The I put this import line in the common.py module:
from . import *
And then I tried to access the function func0() which is present in any module (except in __init__.py and common.py) in the package. But it raised an error which reads
ValueError: Attempted relative import in non-package
I need a detailed answer.
Here is a solution I tried myself and it worked,
I'll presume that you will be working with common.py as your main module where you'll be importing the rest modules in it, so:
1 - In the __init__.py file, add:
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if os.path.basename(f)[:-3] != 'common']
2 - In common.py add:
import sys
from os import path
sys.path.append( path.dirname(path.dirname(path.abspath(__file__))))
#print sys.path #for debugging
import backends
#from backends import * #up to you, but previous is better
Voila! Found a solution which I am completely satisfied with. :-)
I left the __init__.py module untouched. And instead put the following codes in the beginning of the common.py module.
import os
pwd = os.path.dirname(os.path.realpath(__file__))
file_names = os.listdir(pwd)
for name in file_names:
if ".pyc" not in name and "__init__" not in name and "common" not in name and ".py" in name:
exec "from "+name[:-3]+" import *"
It worked like charm. Though I don't know whether this is the best solution. I guess in Python 3.x, exec statement will be like following:
exec("from "+name[:-3]+" import *")

Importing files in Python?

How do I import Python files during execution?
I have created 3 file a.py,b.py and c.py in a path C:\Users\qksr\Desktop\Samples
The files contain the code as shown below:
a.py
from c import MyGlobals
def func2():
print MyGlobals.x
MyGlobals.x = 2
b.py
import a
from c import MyGlobals
def func1():
MyGlobals.x = 1
if __name__ == "__main__":
print MyGlobals.x
func1()
print MyGlobals.x
a.func2()
print MyGlobals.x
c.py
class MyGlobals(object):
x = 0
When I execute the code b.py the following error is thrown:
ImportError: No module named a
I believe my working directory is default and all the file a,b,c is just created by me in the samples folder.
How do I import python files in Python?
If you are working in the same directory, that is, b.py is in the same folder as a.py, I am unable to reproduce this problem (and do not know why this problem occurs), but it would be helpful if you post what os.getcwd() returns for b.py.
If that's not the case, add this on top of b.py
import sys
sys.path.append('PATH TO a.py')
OR if they are in the same path,
import sys
sys.path.append(os.basename(sys.argv[0])) # It should be there anyway but still..
There are many ways to import a python file:
Don't just hastily pick the first import strategy that works for you or else you'll have to rewrite the codebase later on when you find it doesn't meet your needs.
I start out explaining the the easiest console example #1, then move toward the most professional and robust program example #5
Example 1, Import a python module with python interpreter:
Put this in /home/el/foo/fox.py:
def what_does_the_fox_say():
print "vixens cry"
Get into the python interpreter:
el#apollo:/home/el/foo$ python
Python 2.7.3 (default, Sep 26 2013, 20:03:06)
>>> import fox
>>> fox.what_does_the_fox_say()
vixens cry
>>>
You invoked the python function what_does_the_fox_say() from within the file fox through the python interpreter.
Option 2, Use execfile in a script to execute the other python file in place:
Put this in /home/el/foo2/mylib.py:
def moobar():
print "hi"
Put this in /home/el/foo2/main.py:
execfile("/home/el/foo2/mylib.py")
moobar()
run the file:
el#apollo:/home/el/foo$ python main.py
hi
The function moobar was imported from mylib.py and made available in main.py
Option 3, Use from ... import ... functionality:
Put this in /home/el/foo3/chekov.py:
def question():
print "where are the nuclear wessels?"
Put this in /home/el/foo3/main.py:
from chekov import question
question()
Run it like this:
el#apollo:/home/el/foo3$ python main.py
where are the nuclear wessels?
If you defined other functions in chekov.py, they would not be available unless you import *
Option 4, Import riaa.py if it's in a different file location from where it is imported
Put this in /home/el/foo4/bittorrent/riaa.py:
def watchout_for_riaa_mpaa():
print "there are honeypot kesha songs on bittorrent that log IP " +
"addresses of seeders and leechers. Then comcast records strikes against " +
"that user and thus, the free internet was transmogified into " +
"a pay-per-view cable-tv enslavement device back in the 20th century."
Put this in /home/el/foo4/main.py:
import sys
import os
sys.path.append(os.path.abspath("/home/el/foo4/bittorrent"))
from riaa import *
watchout_for_riaa_mpaa()
Run it:
el#apollo:/home/el/foo4$ python main.py
there are honeypot kesha songs on bittorrent...
That imports everything in the foreign file from a different directory.
Option 5, Import files in python with the bare import command:
Make a new directory /home/el/foo5/
Make a new directory /home/el/foo5/herp
Make an empty file named __init__.py under herp:
el#apollo:/home/el/foo5/herp$ touch __init__.py
el#apollo:/home/el/foo5/herp$ ls
__init__.py
Make a new directory /home/el/foo5/herp/derp
Under derp, make another __init__.py file:
el#apollo:/home/el/foo5/herp/derp$ touch __init__.py
el#apollo:/home/el/foo5/herp/derp$ ls
__init__.py
Under /home/el/foo5/herp/derp make a new file called yolo.py Put this in there:
def skycake():
print "SkyCake evolves to stay just beyond the cognitive reach of " +
"the bulk of men. SKYCAKE!!"
The moment of truth, Make the new file /home/el/foo5/main.py, put this in there;
from herp.derp.yolo import skycake
skycake()
Run it:
el#apollo:/home/el/foo5$ python main.py
SkyCake evolves to stay just beyond the cognitive reach of the bulk
of men. SKYCAKE!!
The empty __init__.py file communicates to the python interpreter that the developer intends this directory to be an importable package.
If you want to see my post on how to include ALL .py files under a directory see here: https://stackoverflow.com/a/20753073/445131
Bonus protip, whether you are using Mac, Linux or Windows, you need to be using python's idle editor as described here. It will unlock your python world. http://www.youtube.com/watch?v=DkW5CSZ_VII
Tweaking PYTHONPATH is generally not a very good idea.
A better way is to make your current directory behave like a module, by adding a file named __init__.py, which can be empty.
Then the python interpretter allows you to import files from that directory.
Referring to: I would like to know how to import a file which is created in any path outside the default path ?
import sys
sys.path.append(directory_path) # a.py should be located here
By default, Python won't import modules from the current working directory. There's 2 (maybe more) solutions for this:
PYTHONPATH=. python my_file.py
which tells python to look for modules to import in ., or:
sys.path.append(os.path.dirname(__file__))
which modifies the import path on runtime, adding the directory of the 'current' file.
1st option: Add the path to your files to the default paths Pythons looks at.
import sys
sys.path.insert(0, 'C:/complete/path/to/my/directory')
2nd option: Add the path relative to your current root of your environment (current directory), using instead the following:
#Learn your current root
import os
os.getcwd()
#Change your current root (optional)
os.chdir('C:/new/root')
#Add the path from your current root '.' to your directory
import sys
sys.path.insert(0, './Path/from/current/root/to/my/directory')

Categories