0

I'm breaking up a large-ish (for me anyway!) project into 2 or 3 modules, including a 'main' module with the top level menu etc. It will import a two or three 'sub-modules' for different aspects of the application. However those modules will need various settings etc from the main module. Yes, they could be passed as arguments but there are quite a few of them and they are liable to change. Different modules will need different sub-sets of the settings.

I'd like to be able to have the subordinate modules simply 'reach back' for their settings as needed (sort of global across modules as it were).

Here is a mickie-mouse example of what I mean:

test1:

# dummy version of main / sub modules with mutual importing
# this is the 'main module'
# in this trivial example, it would be easy to make mode an argument of the function
# but as an alternatiove can test2 'reach back' to test1? 

from test2 import taskA
mode = 1
ans = taskA()
print(ans)

test2:

# dummy version of main / sub modules with mutual importing
# this is the 'sub module' that executes taskA

from test1 import mode

def taskA():
    print('taska, mode = {0}'.format(mode))    
    return 'Hello World!'

mode = 0
print('test2 initialised')

And the results is

Traceback (most recent call last):
  File "C:\Python33\MyScripts\test1.py", line 6, in <module>
    from test2 import taskA
  File "C:\Python33\MyScripts\test2.py", line 4, in <module>
    from test1 import mode
  File "C:\Python33\MyScripts\test1.py", line 6, in <module>
    from test2 import taskA
ImportError: cannot import name taskA

Presumably this is due to potential circularity. (Though, if its able to detect the circularity - not hard in this case!, ie that taskA has already been imported, you'd think its able to simply break out of the circularity, as opposed to throwing an error).

Is there a way to achieve this? There's several obvious ways to code around it - keep it as one module; pass the settings as arguments (but that will have pitfalls if 'test2' has to modify any of the settings, if only as Im still getting my head around python's handling of mutable objects and binding); move all the settings to a separate module (test0) accessed by both test1 and test2.

Given that these modules are intimately connected (I believe the term is strongly coupled), logically it should all be one module. Except that its getting big.

My question is twofold ... how best to do what I want; but also to understand why Python cant handle mutual imports.

ljk321
  • 16,242
  • 7
  • 48
  • 60
RFlack
  • 436
  • 1
  • 5
  • 19
  • 1
    You, sir, have mutual recursive imports which I've had trouble with in the past myself. Instead of using `from test2 import taskA`, try `import test2` and replace all instances of `taskA` with `test2.taskA`, and do the same with `from test1 import mode` in test2. – BrockLee Sep 28 '15 at 18:47
  • @Leo... same result. (I tried pasting the code for clarity but the formatting is a mess in comments) I think getting the same result is comforting though in a way if you think about it. – RFlack Sep 28 '15 at 18:49
  • @Leo - Taking a second look .... I dont think I did your suggestion correctly. – RFlack Sep 28 '15 at 18:56
  • Hmm, well my only other recommendation us what you already mentioned (keeping everything in one file). As for the mutual imports, if you read the instructions in both files sequentially, you'll see that you can't handle mutual imports because they keep recursively calling each other. – BrockLee Sep 28 '15 at 18:57
  • @Leo - yes your suggestion DOES work. Thinking about this somne more.... – RFlack Sep 28 '15 at 19:05

1 Answers1

1

(1) Try moving your "mode = 1" line before the import. This makes it no longer sequentially dependent on the import statement.

(2) If that doesn't work, put the mode into a separate package and have both test1 and test2 import mode from there.

The basic problem is that you've mingled levels of dependency. You created an artificial link between "mode" and other items in that module.


I don't see where you're having trouble with setting "mode"; I did it just fine on the first try.

test0.py

mode = 2

test1.py

from test0 import mode
from test2 import taskA
mode = 1
ans = taskA()
print(ans)

test2.py

from test0 import mode
def taskA():
    print('taska, mode = {0}'.format(mode))
    return 'Hello World!'

mode = 0
print('test2 initialised')

execution

>>> python2.7 test1.py
test2 initialised
taska, mode = 0
Hello World!

>>>

In your original example: (A) in test1.py, move the mode=1 line to before the import:

mode = 1

from test2 import taskA
ans = taskA()
print(ans)

This switch shows that mode cannot depend on anything in module taskA, breaking the pathological circular dependence.

(B) Run the program with test2.py as the top-level module:

>>> python2.7 test2.py
test2 initialised
taska, mode = 0
Hello World!
test2 initialised

Does that get you where you want to be?

In general, you should design your dependencies in a Directed Acyclic Graph (DAG). C can be simple-minded, but good with this, separating header (.h) and code (.c) files. That's why I suggested the third file to hold your "mode" declaration.

Prune
  • 76,765
  • 14
  • 60
  • 81
  • Im obviously going to have to play with this some more (and do more reading first, though Ive done a fair bit already, which hasn't helped much!). I also did an example using a 3rd settings module, which avoids circularity. But now I am on to the next problem which is that the 'client' modules can't update the settings (directly, I assume I can make a function or method to do that). – RFlack Sep 29 '15 at 12:00
  • 1
    See my added code. Is this the leading example you need? – Prune Sep 29 '15 at 17:42
  • Yes the 3 module version def works, I had made something very similar. (The problem with updating the 'settings' was just the diff between 'import x from M' versus 'import M' and M.x = newvalue etc). Im working on why the original 2 module version here doesnt work; but the key thing is I can see a good way to do what I want. Thanks! – RFlack Sep 29 '15 at 20:06