web-dev-qa-db-fra.com

Qu'est-ce qu'un patch de singe?

J'essaie de comprendre, qu'est-ce qu'un patch de singe ou un patch de singe?

Est-ce quelque chose comme une surcharge de méthodes/opérateurs ou une délégation?

At-il quelque chose de commun avec ces choses?

479
Sergei Basharov

Non, ce n'est pas comme ça. C'est simplement le remplacement dynamique d'attributs lors de l'exécution.

Par exemple, considérons une classe qui a une méthode get_data. Cette méthode effectue une recherche externe (sur une base de données ou une API Web, par exemple), et diverses autres méthodes de la classe l'appellent. Toutefois, dans un test unitaire, vous ne souhaitez pas dépendre de la source de données externe. Vous devez donc remplacer de manière dynamique la méthode get_data par un stub qui renvoie des données fixes.

Comme Python les classes sont modifiables et que les méthodes ne sont que des attributs de la classe, vous pouvez le faire autant de fois que vous le souhaitez. Vous pouvez même remplacer les classes et les fonctions d'un module exactement de la même manière. façon.

Mais, comme commentateur l'a fait remarquer, soyez prudent lorsque vous comparez un seul coup:

  1. Si quelque chose d'autre que votre logique de test appelle également get_data, il appellera également votre remplaçant patché par un singe plutôt que l'original - ce qui peut être bon ou mauvais. Méfiez-vous juste.

  2. S'il existe une variable ou un attribut qui pointe également vers la fonction get_data au moment où vous le remplacez, cet alias ne changera pas sa signification et continuera de pointer vers l'original get_data. (Pourquoi? Python ne fait que relier le nom get_data de votre classe à un autre objet fonction; les autres liaisons de noms ne sont pas du tout affectées.)

468
Daniel Roseman

Un MonkeyPatch est un morceau de code Python qui étend ou modifie un autre code au moment de l'exécution (généralement au démarrage).

Un exemple simple ressemble à ceci:

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return "ook ook eee eee eee!"

SomeClass.speak = speak

Source: MonkeyPatch page sur le wiki de Zope.

345
Paolo

Qu'est-ce qu'un patch de singe?

En termes simples, appliquer un singe modifie un module ou une classe pendant l'exécution du programme.

Exemple d'utilisation

Il y a un exemple de correction de singe dans la documentation Pandas:

import pandas as pd
def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

Pour décomposer cela, nous importons d’abord notre module:

import pandas as pd

Ensuite, nous créons une définition de méthode, non liée et libre en dehors du champ de toute définition de classe (étant donné que la distinction entre une fonction et une méthode non liée n'a pas de sens, Python 3 supprime la méthode non liée):

def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

Ensuite, nous attachons simplement cette méthode à la classe sur laquelle nous voulons l’utiliser:

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class

Et ensuite, nous pouvons utiliser la méthode sur une instance de la classe et supprimer la méthode une fois l'opération terminée:

df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

Mise en garde contre le nom

Si vous utilisez un changement de nom (préfixant les attributs avec un double soulignement, ce qui modifie le nom et ce que je ne recommande pas), vous devrez modifier manuellement le nom si vous le faites. Étant donné que je ne recommande pas le nom-brassage, je ne le démontrerai pas ici.

Exemple de test

Comment pouvons-nous utiliser cette connaissance, par exemple, lors de tests?

Supposons que nous devions simuler un appel d'extraction de données vers une source de données externe générant une erreur, car nous souhaitons garantir un comportement correct dans un tel cas. Nous pouvons corriger la structure de données pour assurer ce comportement. (Utilisez donc un nom de méthode similaire à celui suggéré par Daniel Roseman :)

import datasource

def get_data(self):
    '''monkey patch datasource.Structure with this to simulate error'''
    raise datasource.DataRetrievalError

datasource.Structure.get_data = get_data

Et lorsque nous testons le comportement qui repose sur cette méthode en générant une erreur, ce comportement figure dans les résultats du test, s'il est correctement implémenté.

Il suffit de faire ce qui précède pour modifier l’objet Structure pendant toute la durée du processus. Vous devrez donc utiliser des configurations et des démontages dans vos unittests pour éviter de le faire, par exemple:

def setUp(self):
    # retain a pointer to the actual real method:
    self.real_get_data = datasource.Structure.get_data
    # monkey patch it:
    datasource.Structure.get_data = get_data

def tearDown(self):
    # give the real method back to the Structure object:
    datasource.Structure.get_data = self.real_get_data

(Bien que cela soit correct, il serait probablement préférable d’utiliser la bibliothèque mock pour corriger le code. _ Le décorateur mock de patch serait moins sujet aux erreurs Il faudrait plus de lignes de code et donc plus de possibilités d'introduire des erreurs. Je n'ai pas encore revu le code dans mock mais j'imagine qu'il utilise la correction de singe de la même manière.)

110
Aaron Hall

Selon Wikipedia :

En Python, le terme patch monkey fait uniquement référence aux modifications dynamiques d'une classe ou d'un module au moment de l'exécution, motivées par l'intention de corriger le code tiers existant en guise d'une solution de contournement à un bogue ou à une fonctionnalité qui ne fonctionne pas comme vous le souhaitez.

28
David Heffernan

Premièrement: la réparation de singe est un hack diabolique (à mon avis).

Il est souvent utilisé pour remplacer une méthode au niveau du module ou de la classe par une implémentation personnalisée.

Le cas d'utilisation le plus courant consiste à ajouter une solution de contournement pour un bogue dans un module ou une classe lorsque vous ne pouvez pas remplacer le code d'origine. Dans ce cas, vous remplacez le "mauvais" code par l'application de correctifs avec une implémentation dans votre propre module/package.

17
Andreas Jung

La correction de singe ne peut être effectuée que dans des langages dynamiques, dont python est un bon exemple. La modification d'une méthode à l'exécution au lieu de mettre à jour la définition d'objet en est un exemple; de ​​même, l'ajout d'attributs (méthodes ou variables) à l'exécution est considéré comme une correction de singe. Celles-ci sont souvent effectuées lorsque vous travaillez avec des modules dont vous n'avez pas le source, de sorte que les définitions d'objet ne peuvent pas être facilement modifiées.

Ceci est considéré comme mauvais car cela signifie que la définition d'un objet ne décrit pas complètement ou avec précision son comportement réel.

13
Aaron Dufour

La correction de singe entraîne la réouverture des classes ou méthodes existantes dans class au moment de l'exécution et modifie le comportement. Ce comportement doit être utilisé avec prudence, ou vous ne devez l'utiliser que lorsque vous en avez vraiment besoin.

Comme Python est un langage de programmation dynamique, les classes sont modifiables pour vous permettre de les rouvrir et de les modifier, voire de les remplacer.

5
kamal