Accessing code in adjacent files - python

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)

Related

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

Python Import From Folder Above Without Using Path?

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

Value Changes in python with Context Changes

I have a project with folder structure like this:
MainFolder/
__init__.py
Global.py
main.py
Drivers/
__init__.py
a.py
b.py
In Global.py I have declared like this:
#in Global.py file
global_value=''
Now when I tried the below script:
#in main.py
import Global
from Drivers import a
Global.global_value=5
a.print_value()
In a.py file
from MainFolder import Global
def print_value():
print Global.global_value
The output supposed to be like this:
5
But all I am getting is :
''
Anyone with this solution what happens when context changes??
In my opinion you should not do that. To have some form of common value, write the value to a file/db and then fetch the value from that file.
If that doesn't suite the needs, here's some resources I found, might help you out:
I've not tested this, but this one should work (fetched from Import a module from a relative path)
import os, sys, inspect
# realpath() will make your script run, even if you symlink it :)
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
if cmd_folder not in sys.path:
sys.path.insert(0, cmd_folder)
# use this if you want to include modules from a subfolder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
# Info:
# cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
# __file__ fails if script is called in different ways on Windows
# __file__ fails if someone does os.chdir() before
# sys.argv[0] also fails because it doesn't not always contains the path
More:
Importing Python modules from different working directory
Python accessing modules from package that is distributed over different directories

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')

Python: How to access variable declared in parent module

Using the structure from the Python docs:
sound/
__init__.py
effects/
__init__.py
echo.py
surround.py
reverse.py
Say I want to import sound.effects and get a list of available effects. I could do this by declaring a module-level variable in sound.effects and then appending to it when each .py file is imported. So sound/effects/__init__.py might look like this:
effectList = []
import echo
import surround # Could write code to import *.py instead
...
From my main code I can now access sound.effects.effectList to get a list of effects, but how do I access effectList from within echo.py to do the actual append? I'm stuck trying to get access to the variable:
# None of these work :-(
# from . import self
# from .. import effects
# import sound.effects
sound.effect.effectList.append({'name': 'echo'})
What people commonly do in this situation is create a common.py file in the module.
sound/
__init__.py
effect/
__init__.py
common.py
echo.py
surround.py
reverse.py
Then you move the code from __init__.py to common.py:
effectList = []
import echo
import surround # Could write code to import *.py instead
...
Inside __init__.py you have this:
from common import *
So now in echo.py you'd have this:
import common
common.effectList.append({'name': 'echo'})
Anything importing sound would use it like this
import sound.effect
for effect_name,effect in sound.effect.effectlist.items():
#....
I've only just started using this myself, but I believe it's common practice in the python community.
I think you should leave the "making available" to the __init__.py inside the effects package rather than have all the modules auto populate the effectList. A couple of reasons I can think of.
You can't import any of the effects except via the package if you did manage to get this work somehow (they'd except an effectList in the importing module).
You have to manually do the append in every effect you write. It would be better if you just implemented an import *.py like thing in your __init__.py that loaded everything up in the current directory and made it available.
Something like this in your __init__.py.
import os, glob
effectslist = []
for i in glob.glob("*.py"):
if i == "__init__.py":
next
print "Attempting to import %s"%i
try:
mod = __import__(os.path.splitext(i)[0])
effectslist.append(mod)
except ImportError,m:
print "Error while importing %s - %s"%(i,m)

Categories