Импортируйте произвольный исходный файл python. (Python 3.3+)

Как импортировать произвольный исходный файл python (имя файла которого может содержать любые символы и не всегда заканчивается на .py) в Python 3.3 +?

Я использовал imp.load_module следующим образом:

>>> 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'>

Он по-прежнему работает в Python 3.3, но согласно документации imp.load_module он устарел:

Устаревший с версии 3.3: ненужные, поскольку загрузчики должны использоваться для загрузочные модули и find_module() устарели.

Документация модуля

и imp рекомендует использовать importlib:

Примечание Новые программы должны использовать importlib, а не этот модуль.

Каков правильный способ загрузить произвольный исходный файл python в Python 3.3+ без использования устаревшей функции imp.load_module?

Ответ 1

Найдите решение из importlib тестового кода.

Использование 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'>

ПРИМЕЧАНИЕ: работает только в Python 3.3 +.

UPDATE Loader.load_module устарел с Python 3.4. Вместо этого используйте Loader.exec_module:

>>> 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'>

Ответ 2

Более короткая версия решения @falsetrue:

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

Я тестировал его с помощью Python 3.5 и 3.6.

Согласно комментариям, он не работает с произвольными расширениями файлов.