Описание проблемы
У меня есть пакет с OS-зависимыми модулями. Эти модули имеют одинаковый API, каждый.
В коде клиента я могу сделать что-то вроде:
import platform
if platform.system() == "Windows":
from my_package import os_win32 as my_package_platform
elif platform.system() == "Linux":
from my_package import os_linux as my_package_platform
# use my_package_platform in client code
Однако я хотел бы сделать что-то вроде
from my_package import my_package_platform
и автоматически импортировать нужный модуль.
Возможное решение 1: Реальный модуль импортирует все из указанного c модуля
На самом деле есть модуль my_package_platform.py
:
import platform
import importlib as imp
import importlib.util as impu
import sys
exports = ["symbol1", "symbol2"]
def update_globals(mod, var_list):
for var in var_list:
globals()[var] = mod.__dict__.get(var)
if platform.system() == "Windows":
mod = imp.import_module(".os_win32", __package__)
elif platform.system() == "Linux":
mod = imp.import_module(".os_linux", __package__)
else:
raise NotImplementedError("not implemented for platform %s".format(platform.system()))
update_globals(mod, exports)
Однако это похоже на работу кажется немного хакерским. Также необходимо сохранить переменную exports
.
Возможное решение 2: Использование манипуляции sys.meta_path
для создания «виртуального модуля»
Добавьте следующее в __init__.py
пакета :
import platform
import importlib.abc
import importlib.util as impu
import sys
class _VirtualModuleFinder(importlib.abc.MetaPathFinder):
def find_spec(self, fullname: str, path, target=None):
if fullname == __package__ + ".my_package_platform":
if platform.system() == "win32":
return impu.find_spec(__package__ + ".os_win32")
elif platform.system() == "Linux":
return impu.find_spec(__package__ + ".os_linux")
else:
return None
sys.meta_path.append(_VirtualModuleFinder())
Это работает довольно элегантно, однако кажется немного излишним. С другой стороны, дополнительный Finder
вызывается только тогда, когда модуль не найден другими способами.
Одно предупреждение: модуль spe c будет содержать исходное имя модуля. (Что может быть ошибкой или функцией - я не до конца понимаю последствия).
Вопросы
- Какие недостатки вы видите для каждого метода?
- Есть ли лучшие методы или "лучшие практики"?