Wilfred Hughes
Wilfred Hughes

Reputation: 31171

Is it possible to recover __import__ after overwriting it?

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.

Upvotes: 1

Views: 912

Answers (3)

Dunes
Dunes

Reputation: 40713

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

Upvotes: 1

Wilfred Hughes
Wilfred Hughes

Reputation: 31171

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

Upvotes: 0

jonrsharpe
jonrsharpe

Reputation: 122052

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.

Upvotes: 1

Related Questions