Étant donné un objet A, qui contient un objet appelable B, existe-t-il un moyen de déterminer "A" de l'intérieur B.__call__()
?Accéder à l'objet contenant un objet injecté?
Ceci est à utiliser dans l'injection d'essai, où A.B est à l'origine une méthode.
Le code est quelque chose comme ceci:
# 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
La question est: Est-il possible (magie noire est très bien) dans Injected.__call__()
pour accéder à ce « soi » aurait été en non-écrasement Outer.func()
, à utiliser comme "ceci" (ligne marquée "###")?
Naturellement, le code est un peu plus complexe (les appels peuvent être configurés pour être dans un ordre arbitraire, les valeurs de retour peuvent être définies, etc.), mais ceci est l'exemple minimal que je pourrais trouver. problème et la plupart des contraintes.
Je ne peux pas injecter une fonction avec un argument par défaut au lieu de Outer.func
- qui interrompt l'enregistrement (si une fonction était injectée, ce serait une méthode non liée et nécessite une instance de "Outer" comme premier argument, plutôt qu'un comparateur/validateur). En théorie, je pouvais simuler complètement "Outer" pour son appelant. Cependant, la mise en place des attentes il y aurait probablement plus de code, et non réutilisable ailleurs - tout cas/projet similaire devrait également réimplémenter "Outer" (ou son équivalent) comme un simulacre.
Vous avez raison, ce n'est pas de l'injection de dépendance, c'est du débordement. En tout cas, ça semble être aussi dur que je le craignais. Je devrais remplacer la méthode * et * faudrait remplacer __init__ pour remplacer la méthode à l'instanciation, cette fois avec une méthode de proxy qui passerait l'objet conteneur à 'B' ... faisable, mais pas maintenable , il vaut mieux laisser comme un Gedankenexperiment. – Gabe