importing module relative to current script (beyond top level) - python

I have a project a with several versions of main#.py so I organize them into a directory called run. I normally run project a from ./a by calling python run/main1.py. Because main1.py involves imports beyond the top level package, I need to specify sys.path.insert(0, "./") in main1.py.
Now I've created project b which imports main1.py from a. From b\main.py, how do I get main1.py to import a/utils.py specifically?
Requirements:
Project a is a project I worked on long ago so I would like to make limited changes to just its headers. I would like python run/main1.py to work like it currently does.
I may move the projects between different computers so main1.py needs to import utils.py relative to itself. (ie. not importing with absolute path)
I would like the solution to be scalable. b will need to import modules from several other projects structured like a. I feel that expanding the system's PATH variable may just mess things up. Is there a neater solution?
My projects' files are as follows:
a
run
main1.py
utils.py
b
main.py
utils.py
in a/run/main1.py:
import sys
sys.path.insert(0, "./")
from utils import hello # Anyway to specify this to be ../utils.py ?
hello()
in a/utils.py:
def hello():
print('hello from a')
in b/main.py:
import sys
sys.path.append("../")
from a.run import main1
import utils
utils.hello()
in b/utils.py:
def hello():
print('hello from b')
This is the current result. I would like the first line to print 'hello from a':
>>> python run/main1.py:
hello from a
>>> cd ../b
>>> python run/main.py:
hello from b (we want this to be "hello from a")
hello from b

Related

Cannot import module that imports a custom class [duplicate]

This question already has answers here:
Relative imports for the billionth time
(12 answers)
Closed 3 months ago.
I have a directory and module design that looks like this:
MyProject --- - script.py
|
- helpers --- - __init__.py
|
- class_container.py
|
- helper.py
# class_container.py
class MyClass:
pass
# helper.py
from class_container import MyClass
def func():
# some code using MyClass
# script.py
from helpers.helper import func
When I run script.py:
ModuleNotFoundError: No module named 'class_container'
I tried changing the code in helper.py such as:
# helper.py
from helpers.class_container import MyClass
def func():
# some code using MyClass
Then running script.py started working. But when I explicitly run helper.py:
ModuleNotFoundError: No module named 'helpers'
I want to be able run both script.py and helper.py separately without needing to change the code in any module.
Edit: I figured a solution which is changing helper.py such as:
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).parent))
from class_container import MyClass
def func():
# some code using MyClass
Basically I added the directory of helper.py to sys.path by using sys and pathlib modules and __file__ object. And unlike import statement's behaviour, __file__ will not forget it's roots when imported/used from a different module (i.e it won't become the path of script.py when imported into it. It'll always be the path of helper.py since it was initiated there.).
Though I'd appreciate if someone can show another way that doesn't involve messing with sys.path, it feels like an illegal, 'unpythonic' tool.
You have to create a __init__.py file in the MyProject/helpers directory. Maybe you already have created it. If not, create an empty file.
Then in the MyProject/helpers/helper.py, access the module helpers.class_container like this.
from helpers.class_container import MyClass
def func():
# some code using MyClass
You can also use a relative import like this.
from .class_container import MyClass
If you want to run the MyProject/helpers/helper.py independently, add test code in helper.py like this.
from helpers.class_container import MyClass
def func():
# some code using MyClass
if __name__ == '__main__':
func()
And run like this in the MyProject directory.(I assume a Linux environment.)
$ python3 -m helpers.helper
The point is to differentiate Python modules from Python scripts and treat them differently.

Import local packages in python

i've run through many posts about this, but still doesn't seem to work. The deal is pretty cut. I've the got the following hierarchy.
main.py
DirA/
__init__.py
hello.py
DirB/
__init__.py
foo.py
bla.py
lol.py
The__init__.py at DirA is empty. The respective one at DirB just contains the foo module.
__all__.py = ["foo"]
The main.py has the following code
import DirA
import DirB
hey() #Def written at hello.py
foolish1() #Def written at foo.py
foolish2() #Def written at foo.py
Long story short, I got NameError: name 'foo' is not defined. Any ideas? Thanks in advance.
You only get what you import. Therefore, in you main, you only get DirA and DirB. You would use them in one of those ways:
import DirA
DirA.something_in_init_py()
# Importing hello:
import DirA.hello
DirA.hello.something_in_hello_py()
# Using a named import:
from DirA.hello import something_in_hello_py
something_in_hello_py()
And in DirB, just make the __init__.py empty as well. The only use of __all__ is for when you want to import *, which you don't want because, as they say, explicit is better than implicit.
But in case you are curious, it would work this way:
from DirB import *
something_in_dirb()
By default the import * will import everything it can find that does not start with an underscore. Specifying a __all__ restricts what it imported to the names defined in __all__. See this question for more details.
Edit: about init.
The __init__.py is not really connected to the importing stuff. It is just a special file with the following properties:
Its existence means the directory is a python package, with several modules in it. If it does not exist, python will refuse to import anything from the directory.
It will always be loaded before loading anything else in the directory.
Its content will be available as the package itself.
Just try it put this in DirA/__init__.py:
foo = 42
Now, in your main:
from DirA import foo
print(foo) # 42
It can be useful, because you can import some of your submodules in the __init__.py to hide the inner structure of your package. Suppose you build an application with classes Author, Book and Review. To make it easier to read, you give each class its own file in a package. Now in your main, you have to import the full path:
from myapp.author import Author
from myapp.book import Book
from myapp.review import Review
Clearly not optimal. Now suppose you put those exact lines above in your __init__.py, you may simplify you main like this:
from myapp import Author, Book, Review
Python will load the __init__.py, which will in turn load all submodules and import the classes, making them available on the package. Now your main does not need to know where the classes are actually implemented.
Have you tried something like this:
One way
from DirA import hello
Another way
from DirA.hello import hey
If those don't work then append a new system path
You need to import the function itself:
How to call a function from another file in Python?
In your case:
from DirA import foolish1, foolish2

After Python import, call to a definition is generating the NameError

I'm new to python and I was trying to do the following:
I have a folder called app and this folder has two files: main.py and init.py
The
main.py
def generate_report(x1, x2):
print "Generating report..."
print x1
print x2
if __name__ == "__main__":
print "Running: main.py..."
generate_reports(1, 2)
print "Done!"
else:
print "Imported: main.py..."
The
__init__.py
import main
Now in my console if I do:
import app
I get:
Imported: main.py...
Then if I try to do:
generate_report(1, 2)
I get the following error:
NameError: name 'generate_report' is not defined
There are two different approaches to module namespaces in Python. In one, you do import blah and then access blah's elements with blah.spam. In another, you say from blah import spam (or *) and then access blah's elements directly by name.
The problem is, you have to make that decision on every level: if you have a package, you have two such decisions to make: how your script imports package (app in your case), and how your app's __init__.py imports main (btw a horrible name for a module;P).
So, one solution is to write
from app import main
in your __init__.py, and
from app import generate_report
in your script, and another is to write
app.main.generate_report
in your script. Of course, there are two other ways, where some import is direct, and some is fromed.

chained imports from subdirectory in python

I am using someone elses project to add some functionality to mine, and there is a python script I want to import. The problem comes with the import structure of their directories: I have placed their project directory in a subfolder under my main project (needs to stay there so I can keep their project out of my version control) it looks like this:
myproject/
myscript.py
theirproject/
__init__.py
baz.py
secondlayer/
__init__.py
all.py
foo.py
bar.py
all.py is simply a list of import statements which import additional scripts from the secondlayer directory like this:
from secondlayer.foo import *
from secondlayer.bar import * #etc
I would like to import:
from theirproject.secondlayer.all import *
but that fails when python complains "no module named secondlayer.foo"
I have also tried the following:
from theirproject.secondlayer import all
I can get it to work when I place my script in theirproject/ and import all without the "theirproject" prefix, but I really cant have it be like that. I can get further through the import process by importing foo, bar, etc individually like this:
from theirproject.secondlayer import foo
from theirproject.secondlayer import bar #etc
But then those scripts fail to import more stuff from still other scripts (like baz.py) at the same level as secondlayer, so im stuck.
Whats the right way to do this in python 2.7.6?
If you change
from secondlayer.foo import *
from secondlayer.bar import *
to user relative imports like this
from .foo import *
from .bar import *
or like this
from foo import *
from bar import *
it works.
Plus you could do these imports in the __init__.py at secondlayer level so that the import from myscript.py becomes
from theirproject.secondlayer.all import *
See if you have the necessary permissions to import the package from your directory and its corresponding sub directories.
For reference, you may like to see this and its corresponding linked questions :
Python Imports do not work
I ended up solving my problem by adding theirproject/ to my PYTHONPATH. I upvoted junnytony's answer - it helped point me in the right direction, so thanks!

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