tags:

views:

7920

answers:

7

Imagine this directory structure:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

I'm coding mod1, and I need to import something from mod2. How should I do it?

I tried from ..sub2 import mod2 but I'm getting an "Attempted relative import in non-package".

I googled around but found only "sys.path manipulation" hacks. Isn't there a clean way?


Edit: all my __init__.py's are currently empty

Edit2: I'm trying to do this because sub2 contains classes that are shared across sub packages (sub1, subX, etc.).

Edit3: The behaviour I'm looking for is the same as described in PEP 366 (thanks John B)

A: 

Why you even need this? Why you just do not import it as

from app.sub2 import mod2
Matej
Because I'm trying to import mod2 from inside mod1.. I tried your suggestion but I get a "No module named app.sub2".. I'm sorry, am I missing something?
Joril
A: 

I think that what you have to ask yourself is:

  • Why i need to do this?
  • Is my package separation well done?

I don't know the context why you want to do it this way. But for me a cleaner design would be to have the following packages structure:

app/
   init.py
   sub1/
      init.py
      mod1.py
      sub12/
           init.py
           mod2.py

Then you only have to do:

from sub12 import mod2
Lucas S.
I'm sorry, I should've explained why I'm trying to do this.. It's because sub2 contains classes that are shared across subpackages (sub1, subX...)
Joril
A: 

Don't do relative imports. They'll only make your code more fragile. If you do an absolute import as Matej suggested, you'll be less vulnerable to changes in sys.path and changes in file locations.

Allen
How could this answer have been improved? By not writing it at all, 's how. Relative imports are a supported feature. You essentially pretty much accuse the Python devs of not knowing their shit.
jae
You think Python doesn't contain mistakes? :)
Allen
I fail to see what is wrong with this answer.
Matt Joiner
+31  A: 

Everyone seems to want to tell you what you should be doing rather than just answering the question.

The problem is that you're running the module as '__main__' by passing the mod1.py as an argument to the interpreter.

From PEP 328:

Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

In Python 2.6, they're adding the ability to reference modules relative to the main module. PEP 366 describes the change.

John B
this still doesn't work :@
Matt Joiner
The answer here involves messing with sys.path at every entry point to your program. I guess that's the only way to do it.
Nick Retallack
A: 

On a related note, Python 3 will change the default handling of imports to be absolute by default; relative imports will have to be explicitly specified.

Ross
+3  A: 
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. You run python main.py.
  2. main.py does: import app.package_a.module_a
  3. module_a.py does import app.package_b.module_b

Alternatively 2 or 3 could use: from app.package_a import module_a

That will work as long as you have app in your PYTHONPATH. main.py could be anywhere then.

So you write a setup.py to copy (install) the whole app package and subpackages to the target system's python folders, and main.py to target system's script folders.

nosklo
@nosklo +1 well explained
systempuntoout
+2  A: 
def import_path(fullpath):
""" Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module

I'm using this snippet to import modules from paths, hope that helps

iElectric
I'm using this snippet, combined with the imp module (as explained here [1]) to great effect. [1]: http://stackoverflow.com/questions/1096216/override-namespace-in-python/1096247#1096247
Xiong Chiamiov
Probably, sys.path.append(path) should be replaced with sys.path.insert(0, path), and sys.path[-1] should be replaced with sys.path[0]. Otherwise the function will import the wrong module, if there is already a module with the same name in search path. E.g., if there is "some.py" in current dir, import_path("/imports/some.py") will import the wrong file.
Alex Che
I agree! Sometimes other relative imports will make precedance. Use sys.path.insert
iElectric