Получить значение __version__ из файла, не используя imp.load_source?

Ранее я сделал следующее, чтобы получить строку версии:

>>> filepath = './somemodule/__init__.py'
>>> name = 'dummy'
>>> module_source = imp.load_source(name, filepath)
>>> module_source.__version__
1.0.0

Теперь, когда imp устарел в Python 3 (я на 3.7.1), что было бы хорошей заменой load_source которая просто потребует библиотеки std?

Это кажется мне немного запутанным И load_module фактически устарела:

>>> from importlib.machinery import SourceFileLoader
>>> loaded = SourceFileLoader(name, filepath).load_module()
>>> loaded.__version__
1.0.0

РЕДАКТИРОВАНИЕ № 1:

Это обсуждается в разделе Импорт произвольного исходного файла Python. (Python 3.3+) и одно из решений, которое появилось там:

>>> loader = importlib.machinery.SourceFileLoader(name, filepath)
>>> mod = types.ModuleType(loader.name)
>>> loader.exec_module(mod)
>>> mod.__version__
1.0.0

2 ответа

Если вы просто хотите импортировать версию, я обычно выбираю utils которые предлагаются от werkzeug; Я считаю, что вы можете использовать следующее:

from werkzeug.utils import import_string
version = import_string('module.__version__', silent=True)

Вы также можете пройти silent как bool, который скажет функции игнорировать ошибки импорта и возвращать None если возникнут какие-либо ошибки.

"Удобный" способ импортировать модуль - это использовать importlib.import_module(), Это для всех практических целей, так же, как использование import заявление. Тем не менее, он не может импортировать произвольные файлы, которые не находятся в sys.path, так что это не работает для вашего варианта использования.

Чтобы импортировать файл напрямую, importlib Документация содержит этот рецепт:

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Optional; only necessary if you want to be able to import the module
# by name later.
sys.modules[module_name] = module

Наконец, несколько советов: я рекомендую поместить строку версии в отдельный текстовый файл (скажем) __version__.txt и загружая его из __init__.py и все остальное, что нужно знать версию. Таким образом, вам не нужно выполнять код Python для чтения номера версии. Вы можете загрузить данные из таких файлов, используя pkgutil.get_data(__package__, '__version__.txt') в __init__.py и заменить __package__ с подходящим значением при вызове из другого модуля или откройте файл напрямую. get_data() возвращается bytes; вы можете позвонить .decode() на возвращаемое значение, чтобы преобразовать его в строку.

(pkgutil.get_data() Это может показаться окольным путем, но это необходимо, если ваш код импортирован из ZIP-файла или установлен каким-либо другим экзотическим способом. Если ваш код находится в пакете пространства имен или был установлен необычным образом, get_data() может не работать и вернуться None, поэтому вы должны проверить этот случай и избежать сбоев.)

Другие вопросы по тегам