2010-09-17 4 views
0

Учитывая объект A, который содержит вызываемый объект B, существует ли способ определить «A» изнутри B.__call__()?Доступ к объекту, содержащему инъецируемый объект?

Это предназначено для использования в тестовой инъекции, где A.B первоначально является методом.

Код что-то вроде этого:

# Class to be tested. 
class Outer(object): 
    def __init__(self, arg): 
    self.arg = arg 
    def func(self, *args, **kwargs): 
    print ('Outer self: %r' % (self,)) 

# Framework-provided: 
class Injected(object): 
    def __init__(self): 
    self._replay = False 
    self._calls = [] 
    def replay(self): 
    self._replay = True 
    self._calls.reverse() 
    def __call__(self, *args, **kwargs): 
    if not self._replay: 
     expected = (args[0], args[1:], kwargs) 
     self._calls.append(expected) 
    else: 
     expected_s, expected_a, expected_k = self._calls.pop() 
     ok = True 
     # verify args, similar for expected_k == kwargs 
     ok = reduce(lambda x, comp: x and comp[0](comp[1]), 
        zip(expected_a, args), 
        ok) 
     # what to do for expected_s == self? 
     # ok = ok and expected_s(this) ### <= need "this". ### 
     if not ok: 
     raise Exception('Expectations not matched.') 

# Inject: 
setattr(Outer, 'func', Injected()) 
# Set expectations: 
# - One object, initialised with "3", must pass "here" as 2nd arg. 
# - One object, initialised with "4", must pass "whatever" as 1st arg. 
Outer.func(lambda x: x.arg == 3, lambda x: True, lambda x: x=='here') 
Outer.func(lambda x: x.arg == 4, lambda x: x=='whatever', lambda x: True) 
Outer.func.replay() 
# Invoke other code, which ends up doing: 
o = Outer(3) 
p = Outer(5) 
o.func('something', 'here') 
p.func('whatever', 'next') # <- This should fail, 5 != 4 

Возникает вопрос: Есть ли способ (черная магия штраф) в Injected.__call__() для доступа, что «я» бы в не перезаписаны Outer.func() , использовать как «это» (строка с надписью «###»)?

Естественно, что код немного сложнее (вызовы могут быть настроены на произвольный порядок, могут быть установлены значения возврата и т. Д.), Но это минимальный пример, который я мог бы предложить, который демонстрирует как проблема и большинство ограничений.

Outer.funcOuter.func Невозможно ввести функцию с аргументом по умолчанию вместо того, компаратор/валидатор).

Теоретически, я мог издеваться над «Outer» полностью для своего вызывающего абонента. Однако, создавая ожидания, вероятно, будет больше кода, а не повторное использование в другом месте. Любой подобный случай/проект также должен был бы переопределить «Outer» (или его эквивалент) как макет.

ответ

2

Данный объект А, который содержит вызываемый объект B, есть ли способ, чтобы определить, «А» от внутренней Б. вызова()?

Не в общем случае, то есть, с неограниченной общностью вы требуете в этом тексте - если B не хранит ссылку на A в некотором роде, Python наиболее конечно, не держать его от имени B «s.

Для вашего предполагаемого случая использования, это еще хуже: это не помогло бы, даже если сам Bсделал сохранить ссылку на A (как объект метод будет своего класса), так как вы попирая все более B без обращения к этому setattr, что эквивалентно назначению. В классе Outer нет следа, что в более счастливые времена у него был атрибут func с определенными характеристиками: этот атрибут больше не стирается setattr.

Это не настоящая инъекция зависимостей (шаблон дизайна, требующий сотрудничества с вложенным объектом), это исправление обезьяны, одно из моих любимых мотивов и его разрушительный, невосстанавливающий характер (который вы в настоящее время боретесь с) является частью того, почему я задумываюсь об этом.

+0

Вы правы, это не инъекция зависимостей, а вот выбивание. Во всяком случае, мне кажется, это так сложно, как я боялся. Мне нужно будет заменить метод * и *, необходимо заменить '__init__', чтобы снова заменить метод при создании экземпляра, на этот раз с помощью метода проксирования, который передал бы содержащий объект объект' B' ... выполнимый, но не поддерживаемый , так что лучше всего оставить в качестве Gedankenexperiment. – Gabe

Смежные вопросы