12

So, I've seen a few similar questions on Stack Overflow, but nothing seems to address my issue, or the general case. So, hopefully this question fixes that, and stops my headaches. I have a git repo of the form:

repo/
   __init__.py
   sub1/
      __init__.py
      sub1a/
         __init.py
         mod1.py
   sub2/
      __init__.py
      mod2.py

How do I import mod2.py from mod1.py and vice versa, and how does this change depending on whether mod1.py or mod2.py are scripts (when each respectively is importing-- not being imported)?

Eli
  • 36,793
  • 40
  • 144
  • 207

3 Answers3

6

The simplest solution is to put the directory containing repo in your PYTHONPATH, and then just use absolute-path imports, e.g. import repo.sub2.mod2 and so on.

Any other solution is going to involve some hackery if you want it to cover cases where you're invoking both the python files directly as scripts from arbitrary directories - most likely sys.path mangling to effectively accomplish the same thing as setting PYTHONPATH, but without having to have the user set it.

Amber
  • 507,862
  • 82
  • 626
  • 550
  • 2
    I'm fine with editing PYTHONPATH, but is there a standard way of enforcing this? I don't want all new users of the repo to have to manually go in and update their PYTHONPATH. – Eli May 03 '13 at 07:56
  • 1
    If you don't want people to have to manage `PYTHONPATH` themselves, then you may need to consider `sys.path` mangling. (E.g., use `__file__` plus `os.abspath` to look up where the script is located, then get the directory the right number of levels above it to be `repo`'s parent, and then `sys.path.insert` it.) – Amber May 03 '13 at 14:47
3

If you are using Python 2.6+, you have two choices:

  • Relative imports
  • Adding repo to your PYTHONPATH

With relative imports, a special dot syntax is used:

in package sub1:

from .sub2.mod2 import thing

in package sub1a:

from ..sub2.mod2 import otherthing

Note that plain import statements (import module) don't work with relative imports.

A better solution would be using absolute imports with your Python path set correctly (example in bash):

export PYTHONPATH=/where/your/project/is:$PYTHONPATH

More info:

Community
  • 1
  • 1
Ilmo Euro
  • 4,925
  • 1
  • 27
  • 29
0

A script or module can import modules that are either

  1. on the system path, or
  2. part of the same package as the importing script/module.

For modules these rules apply without exception. For scripts, the rules apply, but the wrinkle is that by default when you run a script, it is not considered to be part of a package.

This means that by default a script can only import modules that are on the system path. By default the path includes the current directory, so if you run a script, it can import modules in the same directory, or packages that are subdirectories. But that's it. A script has no notion of "where it is" in the directory tree, so it can't do any imports that require specific relative path information about enclosing directories. That means you cannot import things "from the parent directory" or "from a sibling directory". Things that are in those directories can only be imported if they are on the system path.

If you want to make a script "know" that is in a package, you can give it a __package__ attribute. See this previous question. You can then use explicit relative imports (e.g., from ...sub2 import mod2) normally from within that script.

Community
  • 1
  • 1
BrenBarn
  • 242,874
  • 37
  • 412
  • 384