Можно ли перевести модуль в Python? (ResourceX переадресован на ResourceXSimulated) - PullRequest
1 голос
/ 18 сентября 2009

Я хочу смоделировать MyApp, который импортирует модуль (ResourceX), для которого требуется ресурс, который в данный момент недоступен и не будет работать.

Решением для этого является создание и импорт фиктивного модуля ResourceX (с именем ResourceXSimulated) и перенаправление его в MyApp в качестве ResourceX. Я хочу сделать это, чтобы избежать взлома большого количества кода и получить всевозможные исключения из MyApp.

Я использую Python, и он должен выглядеть примерно так:

«Импортировать ResourceXSimulated как ResourceX»

«ResourceX.getData ()», фактически вызывает ResourceXSimultated.getData ()

Будем рады узнать, поддерживает ли Python такой тип перенаправления.

Приветствие.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ: У меня есть доступ к исходным файлам.

ОБНОВЛЕНИЕ: я думаю добавить как можно меньше кода в MyApp относительно использования модуля fake и добавить этот код рядом с операторами импорта.

Ответы [ 5 ]

4 голосов
/ 18 сентября 2009

Просто измените все строки 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 - пакет, это может потребовать больше усилий.

1 голос
/ 18 сентября 2009

Это возможно с помощью хака 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
1 голос
/ 18 сентября 2009

Это называется «исправление обезьян», и это довольно широко используемый метод в динамических языках, таких как 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
1 голос
/ 18 сентября 2009

Да, это возможно. Некоторые закуски:

Вы можете «перенаправлять» модули, манипулируя sys.modules. Он хранит список импортированных модулей, и там вы можете сделать так, чтобы ваш модуль отображался под тем же именем, что и исходный. Вы должны сделать это, прежде чем любой модуль, который импортирует модуль, который вы хотите подделать.

Вы также можете создать пакет с другим именем, но в этом пакете фактически используйте оригинальное имя модуля для вашего совершенно другого модуля. Это работает хорошо, пока оригинальный модуль не установлен.

Ни в одном из этих случаев вы не можете использовать оба модуля одновременно. Для этого вам нужно обезьяна-патч оригинального модуля.

И, конечно: вполне возможно просто вызвать новый модуль со старым именем. Но это может сбить с толку.

0 голосов
/ 18 сентября 2009

Да, Python может сделать это, и пока методы, представленные в модуле ResourceXSimulated, «выглядят и пахнут», как и в оригинальном модуле, приложение не должно видеть большой разницы (кроме, я полагаю, фиктивные заполнители данных, разное время отклика и т. д.)

...