Bienvenue sur IndexError.

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

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

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 (4,984 points)
sélectionné 3-Aou-2015 par yuiio
...