Можно ли перенаправить модуль в Python?(ResourceX перенаправлен на ResourceXSimulated)
-
22-07-2019 - |
Вопрос
Я хочу смоделировать MyApp, который импортирует модуль (ResourceX), которому требуется ресурс, который в данный момент недоступен и не будет работать.
Решением этой проблемы является создание и импорт фиктивного модуля ResourceX (с именем ResourceXSimulated) и перенаправление его в MyApp как ResourceX.Я хочу сделать это, чтобы не ломать много кода и не получать всевозможные исключения от MyApp.
Я использую Python, и это должно быть что-то вроде:
«Импортировать ResourceX, смоделированный как ResourceX»
«ResourceX.getData()» фактически вызывает ResourceXSiultated.getData().
С нетерпением ждем возможности узнать, поддерживает ли Python такой тип перенаправления.
Ваше здоровье.
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:У меня есть доступ к исходным файлам.
ОБНОВЛЯТЬ:Я подумываю добавить в MyApp как можно меньше кода, касающегося использования поддельного модуля, и добавить этот код рядом с операторами импорта.
Решение
Просто измените все строки import ResourceX
в MyApp
к import ResourceXSimulated as ResourceX
, и строки типа from ResourceX import Y
к from ResourceXSimulated import Y
.
Однако, если у вас нет доступа к MyApp
исходный код или есть другие причины не менять его, вы можете поместить свой модуль в sys.modules
до MyApp
загружается сам:
import ResourceXSimulated
sys.modules['ResourceX'] = ResourceXSimulated
Примечание:если ResourceX
это пакет, возможно, потребуется больше усилий.
Другие советы
Это называется обезьяньим исправлением, и это довольно широко используемый метод в динамических языках, таких как Python.
Итак, предположительно у вас есть класс:
class MyOriginal(object):
def method_x(self):
do_something_expensive_you_dont_want_in_testing()
obj = MyOriginal()
obj.method_x()
поэтому при тестировании вы хотите сделать что-то другое вместо method_x
, но оно должно быть прозрачным.Итак, вы просто воспользуетесь преимуществами динамического языка Python:
def new_method_x(self):
pretend_were_doing_something_expensive()
test_obj = MyOriginal()
test_obj.method_x = new_method_x # here's the monkeypatch
test_obj_method_x() # calls the new method
Это возможно с sys.modules
взломать, как уже было сказано.
Обратите внимание: если у вас есть контроль над модулем ResourceX
конечно, лучше, если он позаботится об этом сам.На самом деле это распространенный шаблон при написании модулей, которые работают лучше, когда присутствует какой-либо ресурс, например:
# foo.py
'''A module that provides interface to foo.
Falls back to a dummy interface if foo is not available.
'''
try:
from _foo import *
except ImportError:
from _foo_dummy import *
Иногда люди делают это более объектно-ориентированным способом:
# foo.py
'''A module that provides interface to foo if it exists or to a dummy interface.
Provides:
frobnicate() self-explanatory
...
'''
class DummyFoo:
def frobnicate(self):
pass
...
class UnixFoo(DummyFoo):
def frobnicate(self):
a_posix_call()
...
class GenericFoo(DummyFoo):
def frobnicate(self):
do_something_complicated()
...
# Create a default instance.
try:
if (system == UNIX)
instance = UnixFoo(system)
else:
instance = GenericFoo()
except Exception:
instance = DummyFoo()
# Now export the public interface.
frobnicate = instance.frobnicate
Да, это возможно.Некоторые стартеры:
Вы можете «перенаправить» модули, манипулируя sys.modules.Он хранит список импортированных модулей, и там вы можете сделать так, чтобы ваш модуль отображался под тем же именем, что и исходный.Однако вы должны сделать это перед любым модулем, который импортирует модуль, который вы хотите подделать.
Вы также можете создать пакет с другим именем, но в этом пакете фактически использовать исходное имя модуля для вашего совершенно другого модуля.Это работает хорошо, пока исходный модуль не установлен.
Ни в одном из этих случаев вы не можете использовать оба модуля одновременно.Для этого вам необходимо пропатчить оригинальный модуль.
И конечно:Вполне возможно просто вызвать новый модуль со старым именем.Но это может сбить с толку.
Да, Python может это сделать, и пока методы, представленные в модуле ResourceXSimulated, «выглядят и пахнут» так же, как в исходном модуле, приложение не должно видеть особой разницы (кроме, я предполагаю, фиктивных заполнителей данных). , разное время отклика и тому подобное).