3

Note: This is Python 3.5

Given some project thing such that:

   thing/
       top.py (imports bar)
       utils/
           __init__.py (empty)
           bar.py (imports foo; has some functions and variables)
           foo.py (has some functions and variables)

I can import utils.bar in top.py, and when running top.py the file bar.py must contain import utils.foo to get to foo.py's code.

To run bar.py directly though, bar.py must have import foo instead (import utils.foo fails). If I make that change then top.py no longer works.

How can I run top.py or bar.py and still use the same import construct in bar.py for both use cases? I feel like I'm missing some simple step or construct, but perhaps expecting bar.py to work independently in this way is asking too much (short of conditional imports or such).

Edit: I've accepted the proper answer, but if you still insist on doing this with some contortions, the following worked for me:

In bar.py

try:
    import foo
except ImportError as err:
    from . import foo

Then you can run bar.py directly (in my case, as I develop bar.py I'm using some experimental code in an if __main__ block at the bottom of the file). Once my code is well-integrated I remove the conditional and the main part (since it's not meant to run standalone).

MartyMacGyver
  • 9,483
  • 11
  • 47
  • 67
  • Are you sure that to run top.py, you need to have the file bar.py containing `import utils.foo`? It should be working with `import foo` only. – Fehnryr Nov 07 '16 at 00:50
  • Yes - otherwise I get an ImportError. – MartyMacGyver Nov 07 '16 at 00:52
  • Ok. The issue is specific to Python3. My previous comment is relevant only for Python2. – Fehnryr Nov 07 '16 at 01:15
  • Well, it's not exactly specific to 3, but it's constrained by it (there are likely ways to make this work for both, but it's not easy and not worth it here). – MartyMacGyver Nov 07 '16 at 01:18
  • For future references: [http://stackoverflow.com/questions/12172791/changes-in-import-statement-python3](http://stackoverflow.com/questions/12172791/changes-in-import-statement-python3) – Fehnryr Nov 07 '16 at 01:22
  • Thanks! I wanted to make sure there wasn't a way to do what I was trying to do more concisely than the conditional import I added above in the last edit. (There's no such way and that's OK.... as for Py2 vs Py3, I try to stick with Python 3 constraints whenever possible now.) – MartyMacGyver Nov 07 '16 at 01:32

2 Answers2

3

Its typical to have all your entrypoints outside the module, than all imports can be relative. For example

thing/
    app.py
    thing/
        __init__.py
        top.py
        utils/
            __init__.py
            bar.py
            foo.py

And app.py can look like

from thing import app
if __name__ == '__main__':
    app.main()

Than in top.py you have

from .utils import bar

And in bar you have

from . import foo

Or

from thing.utils import foo

Or

from ..utils import foo

So if you need two entrypoints, you can either make app take a command line argument for the second entry point, or you can make another file like app that imports bar

Nick Humrich
  • 14,905
  • 8
  • 62
  • 85
  • Thank you! I added an edit to my question demonstrating a way to do this via a kind of conditional import (since now I know that's the only way to coerce Python into doing this for the temporary use case I have in mind). – MartyMacGyver Nov 07 '16 at 01:29
1

https://docs.python.org/2.5/whatsnew/pep-328.html try to use import .foo and for python 3 see Relative imports in Python 3

Community
  • 1
  • 1
Dragos Pop
  • 428
  • 2
  • 8