Detecting circular imports Detecting circular imports python python

Detecting circular imports


To avoid having to alter every module, you could stick your import-tracking functionality in a import hook, or in a customized __import__ you could stick in the built-ins -- the latter, for once, might work better, because __import__ gets called even if the module getting imported is already in sys.modules, which is the case during circular imports.

For the implementation I'd simply use a set of the modules "in the process of being imported", something like (benjaoming edit: Inserting a working snippet derived from original):

beingimported = set()originalimport = __import__def newimport(modulename, *args, **kwargs):    if modulename in beingimported:        print "Importing in circles", modulename, args, kwargs        print "    Import stack trace -> ", beingimported        # sys.exit(1) # Normally exiting is a bad idea.    beingimported.add(modulename)    result = originalimport(modulename, *args, **kwargs)    if modulename in beingimported:        beingimported.remove(modulename)    return resultimport __builtin____builtin__.__import__ = newimport


Not all circular imports are a problem, as you've found when an exception is not thrown.

When they are a problem, you'll get an exception the next time you try to run any of your tests. You can change the code when this happens.

I don't see any change required from this situation.

Example of when it's not a problem:

a.py

import ba = 42def f():  return b.b

b.py

import ab = 42def f():  return a.a


Circular imports in Python are not like PHP includes.

Python imported modules are loaded the first time into an import "handler", and kept there for the duration of the process. This handler assigns names in the local namespace for whatever is imported from that module, for every subsequent import. A module is unique, and a reference to that module name will always point to the same loaded module, regardless of where it was imported.

So if you have a circular module import, the loading of each file will happen once, and then each module will have names relating to the other module created into its namespace.

There could of course be problems when referring to specific names within both modules (when the circular imports occur BEFORE the class/function definitions that are referenced in the imports of the opposite modules), but you'll get an error if that happens.