Detecting circular imports

circular-dependency, python

Solution

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 result
import __builtin__
__builtin__.__import__ = newimport

Problem

I'm working with a project that contains about 30 unique modules. It wasn't designed too well, so it's common that I create circular imports when adding some new functionality to the project. Of course, when I add the circular import, I'm unaware of it. Sometimes it's pretty obvious I've made a circular import when I get an error like `AttributeError: 'module' object has no attribute 'attribute'` where I clearly defined `'attribute'`. But other times, the code doesn't throw exceptions because of the way it's used. So, to my question: Is it possible to programmatically detect when and where a circular import is occuring? The only solution I can think of so far is to have a module `importTracking` that contains a dict `importingModules`, a function `importInProgress(file)`, which increments `importingModules[file]`, and throws an error if it's greater than 1, and a function `importComplete(file)` which decrements `importingModules[file]`. All other modules would look like: ``` import importTracking importTracking.importInProgress(__file__) #module code goes here. importTracking.importComplete(__file__) ``` But that looks really nasty, there's got to be a better way to do it, right?

Original source