37

How can I load a Python module in Python 3.4 given its full path?

A similar question How to import a module given the full path? covers Python versions before 3.4, but the conclusion is that support in Python 3.4 is deprecated for the presented answers, so any solution for Python 3.4 is appreciated.

Note that this question is not a duplicate of Import abitrary python source file. (Python 3.3+), since answers for this also use loader.load_module() which is deprecated in Python 3.4, as said in answer, with details in Consider leaving importlib.abc.Loader.load_module(), and documentation in importlib.

So a supported solution for module import by full path in Python 3.4 is needed.

Community
  • 1
  • 1
EquipDev
  • 3,807
  • 8
  • 27
  • 53

4 Answers4

20

This should work for all python files, regardless of file extension:

import importlib.machinery

modulename = importlib.machinery.SourceFileLoader('modulename','/Path/To/module.py').load_module()

This method was mentioned in the deprecation message in the imp.load_module documentation.

Evil Genius
  • 419
  • 5
  • 13
  • 2
    Thanks for point this out, and it looks fully supported in current Python 3.4, so that must be the replacement, thus the answer :-) – EquipDev Apr 26 '15 at 07:30
  • 3
    FYI: Deprecated since version 3.6: Use importlib.abc.Loader.exec_module() instead. [Source](https://docs.python.org/3/library/importlib.html) – Trolldejo May 17 '17 at 09:08
  • 1
    importlib.machinery.ExtensionFileLoader seems to work for C/C++ extension modules. – Greg Kramida Oct 15 '18 at 18:40
10

The supported and non-deprecated method according to the 3.6 docs is like this:

def import_file(full_name, path):
    """Import a python module from a path. 3.4+ only.

    Does not call sys.modules[full_name] = path
    """
    from importlib import util

    spec = util.spec_from_file_location(full_name, path)
    mod = util.module_from_spec(spec)

    spec.loader.exec_module(mod)
    return mod
4

The already-given answer works, but is unnecessarily clunky. An easier method:

import sys, os, importlib

sys.path.append(os.path.dirname(filename))
mname = os.path.splitext(os.path.basename(filename))[0]
imported = importlib.import_module(mname)                       
sys.path.pop()

imported is the imported module; you can use it as normal, through imported.method(arg). The final line isn't strictly necessary, but it's cleaner not to leave unnecessary entries in sys.path (particularly if you're going to run the code multiple times). This works in 3.4, and doesn't use anything marked as deprecated.

Tom Hunt
  • 850
  • 7
  • 17
  • 2
    If ``mname`` is "/my/own/dir/stat.py" then the code it will import the Python Standard Library module ``stat``, and not the specified module, due to the accidental clash of name with a module in the Python Standard Library. So this does not work in all cases. – EquipDev Feb 20 '15 at 07:56
  • 1
    True. I think, but am not certain, that this can be fixed by doing `sys.path.insert(0, os.path.dirname(filename))` instead of `.append()` (and, of course, doing `.pop(0)` afterward). Depends on whether `sys.path` has standard search-path semantics. Of course, if you did this then removing the entry afterward is even more important, to prevent files in that directory from shadowing the standard library. (On a more fundamental level, it's probably a bad idea to name your modules the same as standard library modules anyway. But.) – Tom Hunt Feb 20 '15 at 16:06
  • Yes, putting it first in the search list may help in the case, but other modules that happens to have name as a standard library may then shadow a standard library. Agree that you should avoid using existing standard library module names. But, I think that all these work-around considerations are symptoms that there is missing a simple way to just import a file as a module... just like it was possible in previous versions of Python. – EquipDev Feb 21 '15 at 18:19
  • 2
    Be careful with sys.path.pop() or sys.path.pop(0). You are presuming that sys.path is not modified by your target imported modules. It would be prudent then to do a sys.remove(os.path.dirname(filename)) instead. – DevPlayer Oct 28 '16 at 06:12
1

The function below works in Python 3.4 - it is used to load and run specific functions in modules, but you need to add the folder to the sys path.

sys.path.append("path_to_your_file_to_import")
tool = {'file':'test_tool.py', 
    'function':'sum_even_numbers', 
    'args':['list'], 
    'return':['int']
}

args = [1,2,3]

def run(tool, args, silent='Y'):
        if silent == 'N':
            print('main called ' + tool['file'] + '->' + tool['function'] + ' with ', args, ' = ', tool['return'])
        mod = __import__( os.path.basename(tool['file']).split('.')[0])
        func = getattr(mod, tool['function'])
        tool['return'] = func(args)
        return tool['return']

Call it via

run(tool, args)
acutesoftware
  • 1,092
  • 3
  • 15
  • 33
  • This is just plain `import` based on name, like the statement `import test_tool`, and not import of a module at a given location (path). Also, the `self` part makes it look like copy-paste from a class, thus the plain call of `run(tool, args)` will result in syntax error. – EquipDev Dec 10 '14 at 06:16
  • @EquipDev you are right - the folder needs to be added to the sys path. I have edited to show this, and also removed the self param. It was from a class I am working on - https://github.com/acutesoftware/AIKIF/blob/master/aikif/tools.py – acutesoftware Dec 10 '14 at 12:24
  • Using the regular import (__import__) mechanish with `sys.path` won't let you import a module in filename "sys.py", since that will collide with the built-in `sys` module. – EquipDev Dec 10 '14 at 13:46
  • So what exactly do you want to be able to import - are these standard python modules, or do you want to be able to run code from any definable path? One way is to copy the file from its path to a known local path, rename it with a GUID and import that. – acutesoftware Dec 11 '14 at 02:36
  • Just want to import a file as a module based on path to the file. It is not supposed to be rocket science. It was easy before Python 3.3, possible in Python 3.3, but with 3.4 I can't figure how to do it a supported way. – EquipDev Dec 11 '14 at 21:35