Можно ли перенаправить модуль в Python?(ResourceX перенаправлен на ResourceXSimulated)

StackOverflow https://stackoverflow.com/questions/1443173

Вопрос

Я хочу смоделировать 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, «выглядят и пахнут» так же, как в исходном модуле, приложение не должно видеть особой разницы (кроме, я предполагаю, фиктивных заполнителей данных). , разное время отклика и тому подобное).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top