Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Monkeypatch conditionnel d'une fonction via un décorateur

+4 votes

people.py :

#!/usr/bin/env python
# coding:utf-8
from __future__ import print_function, unicode_literals, absolute_import

def calimero(text):
    print("C'est vraiment trop injuste : %s " % text)

moods.py :

#!/usr/bin/env python
# coding:utf-8
from __future__ import print_function, unicode_literals, absolute_import

from functools import wraps

# from people import calimero
import people

old_calimero = people.calimero

def droopy(text):
    print("You know what, I'm happy : %s" % text)


def joie(func):
    @wraps(func)
    def allez_rigole(*args, **kawrgs):
        people.calimero = droopy
        func(*args, **kawrgs)
    return allez_rigole


# Juste un test
people.calimero = droopy
print('from moods :')
people.calimero('je mange un carambar')

main.py :

#!/usr/bin/env python
# coding:utf-8
from __future__ import print_function, unicode_literals, absolute_import
from people import calimero
from moods import joie

def test1():
    print('test1 :')
    calimero("aujourd'hui c'est lundi")

@joie
def test2():
    print('test2 :')
    calimero("aujourd'hui c'est lundi") # Grrrr

if __name__ == '__main__':
    test1()
    test2()

Et lorsque je lance main.py voici le résultat que j'obtiens :

from moods :
You know what, I'm happy : je mange un carambar
test1 :
C'est vraiment trop injuste : aujourd'hui c'est lundi
test2 :
C'est vraiment trop injuste : aujourd'hui c'est lundi

Je ne comprends pas pourquoi sur la dernière ligne du résultat je n'obtiens pas :
test2 :
You know what, I'm happy : aujourd'hui c'est lundi

Quelqu'un peut-il me dire comment on peut faire un monkeypatch conditionnel via decorateur ?

demandé 3-Aou-2015 par yuiio (134 points)

1 Réponse

+4 votes
 
Meilleure réponse

Ton monkey patch n'affecte que le module people et à lieu après l'import du module par main.py. Donc quand tu utilise calimero(), la variable contient déjà une référence à la fonction originale, et le fait de changer la référence dans people.calimero ne change pas cette référence.

Pour que cette approche fonctionne, ton main.py devrait être:

#!/usr/bin/env python
# coding:utf-8
from __future__ import print_function, unicode_literals, absolute_import
import people
from moods import joie

def test1():
    print('test1 :')
    people.calimero("aujourd'hui c'est lundi")

@joie
def test2():
    print('test2 :')
    # ici on utilise la référence qui est attachée au module
    people.calimero("aujourd'hui c'est lundi") # Grrrr

if __name__ == '__main__':
    test1()
    test2()

Par ailleurs, tu ne restore pas la valeur originale de calimero, il faudrait faire :

def joie(func):
    def wrapper():
         people.calimero = droopy
         func()
         people.calimero = old_calimero
    return wrapper

Faire un monkey patching est généralement une mauvaise idée. Tu vas te heurter à tout un tas de cas auxquels tu n'as pas pensé et qui font tout péter : ordre d'initialisation, copie de références, introspection du code, type checking, meta programming...

Il est très difficile de faire un monkey patching robuste, et généralement cela demande à l'utilisateur de ton code un peu de bonne volonté.

répondu 3-Aou-2015 par Sam (5,000 points)
sélectionné 3-Aou-2015 par yuiio
...