6

Is there a way to do something (like print "funkymodule imported" for example) every time a module is imported from any other module? Not only the first time it's imported to the runtime or reloaded?

andyortlieb
  • 2,326
  • 3
  • 21
  • 33
  • 1
    Why would you want to import a module that's already been imported? – NullUserException Feb 08 '13 at 17:51
  • 1
    This probably isn't possible. [PEP-396](http://www.python.org/dev/peps/pep-0369/) describes such a mechanism, but was withdrawn. – millimoose Feb 08 '13 at 17:52
  • 1
    The only way I can think of is to replace `sys.modules` with a custom mapping, reimplementing `__getitem__`, but the import machinery probably calls directly `PyDict_GetItem` from the C side so even this shouldn't work. – Bakuriu Feb 08 '13 at 17:54
  • Basically I have some placeholder code that i want to log every time it gets used until I've met enough dependencies in my project to fix it up. This module may be imported from numerous other modules. Basically like a fancier #FIXME ... – andyortlieb Feb 08 '13 at 18:02
  • 1
    You can do this much more easily with grep... – Ned Batchelder Feb 08 '13 at 18:13
  • 1
    That's some pretty needless advice. I'm obviously exploring ideas for the dev process. The purpose of my question has no bearing on my curiosity for the answer. – andyortlieb Feb 08 '13 at 18:19
  • 2
    Once I ran into a problem of cyclic dependencies - module A was importing module B, that in turn was importing A again **before A was ready**. That was pretty hard to debug, and I ended up using something like this. In my case at least, using grep wouldn't be enough - since I was not only interested in how many times the import happened, but in their order as well (and I didn't know which modules were problematic). Thus I think it's an interesting question, although not something I'd do in production code. – mgibsonbr Feb 08 '13 at 18:27

1 Answers1

8

One possibility would be to monkey patch __import__:

>>> old_import = __import__
>>> def my_import(module,*args,**kwargs):
...   print module, 'loaded'
...   return old_import(module,*args,**kwargs)
...
>>> __builtins__.__import__ = my_import
>>> import datetime
datetime loaded
>>> import datetime
datetime loaded
>>> import django
django loaded

It worked fine on command line (using Python 2.7.3 on Windows XP), but I dunno if would work in other environments.

To access the module object (instead of just the module name - so you can do something useful with it) just intercept the return value instead of the argument:

>>> def my_import(*args,**kwargs):
...   ret = old_import(*args,**kwargs)
...   print ret
...   return ret
...
>>> __builtins__.__import__ = my_import
>>> import datetime
<module 'datetime' (built-in)>
>>> import django
<module 'django' from 'C:\Python27\lib\site-packages\django\__init__.pyc'>

Update: Just confirmed it works if used inside a python file too - though in this case, the correct way of assigning it is __builtins__['__import__'] = my_import.

mgibsonbr
  • 21,755
  • 7
  • 70
  • 112
  • I want to know every time module A gets imported from any other module. Not how many times module B is importing module A. – andyortlieb Feb 08 '13 at 18:06
  • This code will be called every time a module is imported (using `import module`), no matter who is importing it. Even imports inside imports will work (though for the inner modules, only the first time will show it - since the module body is not run again). – mgibsonbr Feb 08 '13 at 18:14
  • It's a cool solution. I was hoping to find some way to avoid monkeypatching import, especially since there's only exactly one specific module that I care about tracking in this sense. But it could definitely get me what I need. – andyortlieb Feb 08 '13 at 18:29
  • You could probably just do a `return old_import(*args,**kwargs)` at the end after whatever extra stuff you wanted done (unless you wanted to do something afterwards, of course). – martineau Feb 08 '13 at 18:35