0

How to break this particular circular import. I added a trivial example added at top to highlight the issue.

module_tit.py

import tat
class Tit
    def tit(x):
        return tat.inst.tat(x)

inst = Tit()

if __name__=='__main__:
    print "tit 12 %s" % inst.tit(12)

module_tat.py

import tit
class Tat:
    def tat(x):
        if x>0:  return tit.inst.tit(x-1)
        else: return 'bottom'

inst = Tat()

if __name__=='__main__:
    print "tat 5 %s" % inst.tat(5)
  • The design of my code strongly requires that tit and tat be in their own modules.
  • It is important the either tit or tat could be the first module loaded.
  • Is this possible in python in any way???

=== original question below ===

I have a base_module with many dozens of constants etc. it is imported by many, and it imports nothing. In it I have a key class in the base module, and it depends upon a pretty printer module to do the printing of Thing:

to_str = None
class Thing_Version1(object):
    def __repr__():
        to_str(self)

In the pretty printer module I have:

def to_str(x):
    ...
import base_module
base_module.to_str = to_str  

When the pretty printer module it sets "base_module.to_str" so that everything works properly. this works 99% of the time, but in the debugger, it is not smart enough to have the pretty printer load (even though in the normal interpreter, my load process does force eventual load of pprint module. The other approach I considered was to import it dynamically:

class Thing_Version1(object):
    def __repr__():
        import pprint
        pprint.to_str(self)

But this also fails. Sometimes it cannot find the 'to_str' function in the pprint module (I assume because of circular reference)

My ultimate goal is to force loading of pprint anytime base_module loads, and allow pprint to depend on the dozens of constants in the base_module.

Is this impossible?

Dan Oblinger
  • 489
  • 3
  • 15
  • I don't believe this has anything to do with circular references. You should be importing pprint in the base module because that's where the contract is. If you import it locally then scope of objects imported is only in that local function, so anyone else wishing to use `pprint.to_str()` will also have to `import pprint`. You have to remember that python is dynamically resolved so `x = 0; def p(): print(x); x = 1; p()` prints 1 not 0. – AChampion Sep 14 '15 at 16:05
  • 2
    Could you give a [mcve] that better demonstrates what you're trying to achieve? Where are these constants defined that `pprint` requires, and why don't you package that into a utility function/module for the rest of your project to use? – jonrsharpe Sep 14 '15 at 16:06
  • @achampion pprint must import base_module as it has dozens of needed constants etc. now you tell me to perform the import of pprint dynamically in order to break the cycle. This sounds right, and if you look at my second code block above, you will see I have a dynamic import of pprint during object printing. This works 99% of the time. sometimes the pprint module will not have the to_str function. I think this occurs when a module is circularly loaded. e.g. when during the execution of its first load, it imported a module that recursively reimported it. I cannot see how to avoid! – Dan Oblinger Sep 14 '15 at 17:12
  • So you have a module called `pprint`, however, the standard python library also has a `pprint` module, have you overlapped with it. Is this the source of the confusion based on which `pprint` module gets imported - you might want to change your `pprint` module name. – AChampion Sep 14 '15 at 17:17
  • @jonrsharpe I edited my code to show exactly what I have in each of the two modules.... and the code works. It only fails when the PyCharm debugger tries to trigger the __repr__ function. But my question is really how do I have two modules A and B, and either one will force the loading of the other no matter which one was loaded first. – Dan Oblinger Sep 14 '15 at 17:18
  • Oh, right - so your `pprint` isn't *the* `pprint`? You should probably pick a different name. – jonrsharpe Sep 14 '15 at 17:20
  • @jonrsharpe good point! I will rename pprint. and maybe I will just give up and merge the source code of these two very different modules. but I view this as a total fail of python import system! – Dan Oblinger Sep 14 '15 at 17:24
  • When your import fails, what is the exception you get? Your new example is invalid because `tit` and `tat` are the modules in each other's namespaces, not the functions (you need to use `tit.tit` and `tat.tat` to get the functions). Once you're running the functions, you'll probably get another error though, since they recurse forever. – Blckknght Sep 14 '15 at 17:34
  • @jonrsharpe a created a Minimal Complete example as you requests. It will fail to run because it contains circular references. I am hoping it is possible to achieve my goals *somehow* using python's import system. thanks! – Dan Oblinger Sep 14 '15 at 17:34
  • @Blckknght well I will finally get this right. Now you seem my code has the circular load issue since module performs a class instantiation during load. I have a fix to this issue. it does a dynamic injection manually, but it fails in the debugger. – Dan Oblinger Sep 14 '15 at 18:44
  • You haven't shown how `pprint` depends on `base_module`. The "import" done by `pprint` should be done in `base_module` as `from pprint import to_str`. Finally, you violate the contract that `__repr__` return a string object -- that could be why the pycharm debugger is failing. – Dunes Sep 14 '15 at 18:56
  • Your MCVE is not very helpful. It has a bunch of typos in it (missing colons, closing quotes) and other errors (missing `self` parameters), and it doesn't demonstrate the issue you say you're having (both modules can be run as scripts and work just fine). Please try again. Give us exactly the code that is giving you an error. Give us the full traceback of the error. – Blckknght Sep 14 '15 at 21:06
  • @Blckknght Apologies. I agree with you. I have spent hours trying to narrow this issue. it does not occur is 'small' code fragments, and it only occurs inside __repr__() executions triggered internally by the debugger. So providing a live version of the error has been problematic. In cleaning the code at some point it stopped failing in the debugger, so I am declaring vistory, though i really don't know how the debugger's invocation was different. Sorry for the noise, but it was not for lack of trying! – Dan Oblinger Sep 14 '15 at 23:38

1 Answers1

0

Unfortunately I can't reproduce your problem. Python has dynamic resolution so circular references are rarely a problem:

fred.py

import wilma
count = 4
def main():
    wilma.pr('Hello')

wilma.py

import fred
def pr(str):
    print(str*fred.count)

Python REPL

>>> import fred
>>> fred.main()
HelloHelloHelloHello

Are you trying too hard to avoid an issue that rarely occurs in Python? Can you provide a minimal example that has the issue you are encountering?

AChampion
  • 29,683
  • 4
  • 59
  • 75
  • In each of my modules I typically have an 'inst' variable which will contain a singleton for classes (like a printer) where you often only use one. The code for those 'inst' variables executes at LOAD time. So it can crash if it tries to leverage the dependencies from other classes which themselves are in the process of dynamically loading. My goal is to have init objects in the classes where their source code is, and to be auto created. but maybe that cannot happen. – Dan Oblinger Sep 14 '15 at 18:31
  • I am accepting your answer, it is correct, but I never managed to express my actual issue. My code always ran in the interpreter, but its __repr__() method did not run when printing values inside the debugger window. but removing more and more initialization code I eventually got it working.... but I don't really know what was different between the internal debugger environment. and the main environment. Thanks – Dan Oblinger Sep 14 '15 at 22:57