Search code examples
pythonpython-3.xpython-3.3

Import arbitrary python source file. (Python 3.3+)


How can I import an arbitrary python source file (whose filename could contain any characters, and does not always ends with .py) in Python 3.3+?

I used imp.load_module as follows:

>>> import imp
>>> path = '/tmp/a-b.txt'
>>> with open(path, 'U') as f:
...     mod = imp.load_module('a_b', f, path, ('.py', 'U', imp.PY_SOURCE))
...
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>

It still works in Python 3.3, but according to imp.load_module documentation, it is deprecated:

Deprecated since version 3.3: Unneeded as loaders should be used to load modules and find_module() is deprecated.

and imp module documentation recommends to use importlib:

Note New programs should use importlib rather than this module.

What is the proper way to load an arbitrary python source file in Python 3.3+ without using the deprecated imp.load_module function?


Solution

  • Found a solution from importlib test code.

    Using importlib.machinery.SourceFileLoader:

    >>> import importlib.machinery
    >>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
    >>> mod = loader.load_module()
    >>> mod
    <module 'a_b' from '/tmp/a-b.txt'>
    

    NOTE: only works in Python 3.3+.

    UPDATE Loader.load_module is deprecated since Python 3.4. Use Loader.exec_module instead:

    >>> import types
    >>> import importlib.machinery
    >>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
    >>> mod = types.ModuleType(loader.name)
    >>> loader.exec_module(mod)
    >>> mod
    <module 'a_b'>
    

    >>> import importlib.machinery
    >>> import importlib.util
    >>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
    >>> spec = importlib.util.spec_from_loader(loader.name, loader)
    >>> mod = importlib.util.module_from_spec(spec)
    >>> loader.exec_module(mod)
    >>> mod
    <module 'a_b' from '/tmp/a-b.txt'>