4

I feel like I'm missing something simple and basic. Here's a toy setup

PythonProject/
main.py
x/
    a.py
    y/
        b.py

b.py has a function foo with no dependencies

def foo():
    print("Hello World")

a.py needs foo from b.py to work and imports it directly

import y.b
def bar():
    #Do some stuff
    y.b.foo()

main.py needs bar from a.py

import x.a
x.a.bar()

Now, running a.py works just fine, it successfully imports b and finds foo. Trying to run main.py however breaks with an import error: specifically "import b" fails during "import a"

I get the impression that what needs to happen is that b needs to be exposed by an __init__.py in a/ but I'm unsure what the pythonic way of doing this would be.

What is the preferred solution to importing a module (a) which imports another module (b) preferably without bringing PythonProject awareness to a?

Zaez
  • 91
  • 1
  • 6
  • I copied your code, verbatim, and the problem is not reproducible. What is the exact error you are receiving? Can we see more code? – StardustGogeta Mar 24 '16 at 00:49
  • Thank you for replying! I edited the code in the example to make the directory names different from the file names. When I run the exact code I have posted now, I get: `Traceback (most recent call last): File "main.py", line 1, in import x.a File "/Users/rsmirol/PythonProject/x/a.py", line 1, in import y.b ImportError: No module named 'y'` – Zaez Mar 24 '16 at 02:23
  • This happens on `python3 main.py` – Zaez Mar 24 '16 at 02:25
  • Okay, I will try it now. – StardustGogeta Mar 24 '16 at 02:25
  • I found a solution! I will post it as an answer. – StardustGogeta Mar 24 '16 at 02:33
  • do you have any `__init__.py` files? – Paul H Mar 24 '16 at 02:40
  • In the actual project I have empty `__init__.py` files in the x and y equivalents. They were not necessary for reproducing the same error. – Zaez Mar 24 '16 at 02:48
  • your `__init__` files should contain import statements that expose the functions you want to make public. https://docs.python.org/3/tutorial/modules.html#packages – Paul H Mar 24 '16 at 02:56

2 Answers2

3

This was never properly answered even though Paul H alluded to the answer.

It's very simple and mentioned but not directly stated here.

If you have a directory like this:

sound/                      Top-level package
  __init__.py               Initialize the sound package
  formats/                  Subpackage for file format conversions
          __init__.py
          wavread.py
          wavwrite.py
          aiffread.py
          aiffwrite.py
          auread.py
          auwrite.py
          ...
  effects/                  Subpackage for sound effects
          __init__.py
          echo.py
          surround.py
          reverse.py
          ...
  filters/                  Subpackage for filters
          __init__.py
          equalizer.py
          vocoder.py
          karaoke.py
          ...

The __init__.py file in (for example "effects") needs to include

import effects.echo
import effects.surround
import effects.reverse

To call a submodule from within a submodule, as you asked, for example, echo calls surround, then you will need to import surround into echo using import effects.surround as surround

Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
Tom Logan
  • 341
  • 3
  • 15
  • although - if someone wants to add to this so that a main() function within echo doesn't need to be called with echo.main(arg1, etc) but rather echo(arg1, etc) that'd be great – Tom Logan Sep 27 '17 at 16:48
  • 1
    If you import `effects.surround as surround` wouldn't that make the module not work when run individually? – Victor Sg Jul 01 '22 at 13:12
0

From Importing files from different folder in Python:

main.py

import sys
sys.path.insert(0, 'C:/path/x')
import a
a.bar()

a.py

import sys
sys.path.insert(0, 'C:/path/x/y')
import b
def bar():
    #Do some stuff
    b.foo()

b.py

def foo():
    print("Hello World")
Community
  • 1
  • 1
StardustGogeta
  • 3,331
  • 2
  • 18
  • 32
  • 2
    So to generalize, we could us os to find the absolute path to a module and add it to the python path? Do you think this is something that makes sense to do in `__init__.py` for the modules? – Zaez Mar 24 '16 at 02:42
  • 3
    @Zaez Don't mess with the path. use `__init__.py` files. http://stackoverflow.com/a/21995949/1552748 – Paul H Mar 24 '16 at 02:43
  • Yes, it could make sense wherever you need to be importing those files. Also, you could always place your most-used files inside the actual Python `libs` directory. – StardustGogeta Mar 24 '16 at 02:44
  • 2
    @PaulH I don't see a solution that works without messing with the path directly or in `__init__.py` files. What am I missing? The problem essentially is that the project root doesn't care about y, but cares about x which cares about y, so when x calls `import y.b' there is an import error that it can't find y. – Zaez Mar 24 '16 at 02:47
  • 2
    each directory needs an init file. each init file should import the functions it wants to expose to the user. there's no need to modify the path. – Paul H Mar 24 '16 at 02:49
  • @Paul I would argue that editing the `sys.path` is much easier than tediously adding `__init__.py` files to every directory that gets in the way. – StardustGogeta Mar 24 '16 at 02:50
  • 3
    that's not how packages should be built: https://docs.python.org/3/tutorial/modules.html#packages – Paul H Mar 24 '16 at 02:55
  • @Paul I see your point, but I don't know that Zaez wants to do anything more than a couple small files. I hardly think that warrants a whole new package setup, but I guess it would work just as well. – StardustGogeta Mar 24 '16 at 02:58
  • 1
    if that were the case, all of these functions would be in a single file at the top-level of the directory structure. – Paul H Mar 24 '16 at 03:02
  • @Paul Good point! Either way, you should post your solution as an answer as well. That way, it presents two different options to Zaez. – StardustGogeta Mar 24 '16 at 03:04
  • Thanks for the link @PaulH! I guess my question then becomes, in the example you linked, what if there was a .py file in the top level sound folder which imported from formats/effects/filters? Lets call it audio.py. External to the package, if I wanted to call a function in audio.py, I would not be able to use `from sound.audio import dothething` as the imports in audio.py would result in the same error Im seeing here. What do we do in `_init__.py` to solve that? – Zaez Mar 24 '16 at 03:34