1

I'm trying to write some code that is robust against __import__ being changed, because the Pydev debugger overrides __import__.

So, I need a way of accessing the built-in __import__ function.

>>> def fake_import(*args, **kwargs): pass # some other implementation here
... 
>>> import __builtin__
>>> __builtin__.__import__ = fake_import
# can I recover the original value of __import__ here?

There are questions on SO about recovering removed built-ins but in this case the global has been replaced.

Community
  • 1
  • 1
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192

3 Answers3

1

Looking at the code you link, the original builtins.__import__ is passed to the new ImportHookManager instance and stored as the _system_import attribute.

Based on the answers to How to get instance given a method of the instance?, therefore, you could do something like:

__import__ = __import__.im_self._system_import

to restore the original function. Note that the leading underscore marks this attribute as private-by-convention, though, and the implementation/attribute name could be changed without warning.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • That certainly works, but it's brittle. If pydev ever renames the attribute, my code will break. – Wilfred Hughes Feb 19 '15 at 13:46
  • @WilfredHughes that's true, and the leading underscore suggests they'd rather you stayed away from it. You could also try using [`importlib`](https://docs.python.org/2/library/importlib.html). – jonrsharpe Feb 19 '15 at 13:48
1

This is a somewhat tricky issue as python will not be interested in reloading the __builtin__ module as it hasn't changed. You will be forced to delete the __builtin__ module so as to force python to reimport it. You can also bypass __import__ by using importlib (only true in python3, in python2 importlib resorts to __import__).

import sys
import importlib

import __builtin__ as old_builtins

class ExampleImporter(object):
    old_import = __import__
    def __init__(self):
       self.count = 0
    def new_import(self, *args, **kwargs):
        self.count += 1
        print(args, kwargs)
        return self.old_import(*args, **kwargs)
importer = ExampleImporter()

old_builtins.__import__ = importer.new_import
assert __import__ == importer.new_import

# remove builtins from modules so as to force its reimport
del sys.modules["__builtin__"]
# in python3 the following bypasses __import__ entirely, but not in python2
new_builtins = importlib.import_module("__builtin__") 
# restore initial state of __builtin__ module (will not delete new names 
# added to __builtin__)
old_builtins.__dict__.update(new_builtins.__dict__)
# Replace new __builtin__ with old __builtin__ module. Otherwise you'll end up with a 
# mess where different modules have different a __builtin__ module.
sys.modules["__builtin__"] = old_builtins
del new_builtins

assert __import__ == importer.old_import
assert importer.count == 1 # would be 0 in python3
Dunes
  • 37,291
  • 7
  • 81
  • 97
0

Whilst I haven't found a way of recovering __import__ itself, using ihooks I can get a function that acts as __import__ does:

import ihooks
ihooks.ModuleImporter().install()

# or
works_just_like__import__ = ihooks.ModuleImporter().import_module
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
  • Note that `ihooks` is currently undocumented, and removed in 3.x - https://docs.python.org/2/library/undoc.html#miscellaneous-useful-utilities – jonrsharpe Feb 19 '15 at 13:54
  • Also, the [`install` method](https://hg.python.org/releasing/2.7.9/file/753a8f457ddc/Lib/ihooks.py#l382) makes extensive use of `__builtin__`, so may not behave as you expect. – jonrsharpe Feb 19 '15 at 14:04