Search code examples
pythonimportdir

How to dynamiclly import changing module?


Step 1: assume this is the changing module, named test_module.py:

#coding=utf8
# changing module    

class hello(object):
    pass


"""
class world(object):
    pass
"""

Step 2: dynamically reload the changing module, named dynamis_changing_import.py:

"""
@note: Dynamicly import changing module
"""

__author__ = 'k9 Lee'


import sys
import importlib


"""
@important: Does not consider thread safty
"""

def dy_import(name, package=None):
    # Fast path: see if the module has already been imported.
    # @doc: imp
    try:
        sys.modules[name]
    except KeyError:
        # if not found, just import
        importlib.import_module(name, package)
    else:
        # del and reload module
        print '1'
        del sys.modules[name]
        print '2'
        #importlib.import_module(name, package)
        #print '3'

Step 3: test in ipython,

then I found sys.modules['test_module'] raise key error that means I can re-import test_module,

but I still can dir(test_module)...

In [1]: import test_module

In [2]: dir(test_module)
Out[2]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'hello']

In [3]: import dynamis_changing_import

In [4]: dynamis_changing_import.dy_import('test_module')
1
2

In [5]: import sys

In [6]: sys.modules['test_module']  # Here, test_module does not exist.
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-6-d2451de5c425> in <module>()
----> 1 sys.modules['test_module']

KeyError: 'test_module'

In [7]: dir(test_module)  # But... dir... is still there...
Out[7]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'hello']

Step 4. still test ipython: I uncomment the last two statement in file dynamis_changing_import.py,

importlib.import_module(name, package)
print '3'

and importlib.import_module has no effect, I restart ipython:

In [1]: import sys

In [2]: import dynamis_changing_import

In [3]: import test_module

In [4]: dir(test_module)
Out[4]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'hello']

In [5]: # Uncomment the method `world` in test_module

In [6]: dynamis_changing_import.dy_import('test_module')
1
2
3

In [7]: dir(test_module)
Out[7]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'hello']

Thank you.


Solution

  • Do not mess with sys.modules. That's a very low-level detail.

    To safely reload a module you can simply:

    For a cross-version solution simply do:

    import sys
    if sys.version_info.major == 3:
        if sys.version_info.minor < 4:
            from imp import reload
        else:
            from importlib import reload
    

    When you want to re-import module X you simply do:

    reload(X)
    

    So the "dynamic import" becomes:

    import moduleX
    

    And whenever you need the module to be reloaded you can just do:

    reload(moduleX)
    

    Sample run:

    $ echo 'def f():print("a")
    > f()' > t.py
    $ python2
    >>> import t
    a
    >>> t.f()
    a
    # in an other shell:
    # $ echo 'def f():print("b")
    # > f()' > t.py
    >>> reload(t)
    b
    >>> t.f()
    b