python fontsize Qu'est-ce qu'un mixin, et pourquoi sont-ils utiles?




python axes label (12)

Dans " Programming Python ", Mark Lutz mentionne "mixins". Je viens d'un arrière-plan C / C ++ / C # et je n'ai pas entendu le terme avant. Qu'est-ce qu'un mixin?

En lisant entre les lignes de cet exemple (auquel j'ai lié parce que c'est assez long), je suppose qu'il s'agit d'utiliser l'héritage multiple pour étendre une classe par opposition à un sous-classement 'correct'. Est-ce correct?

Pourquoi voudrais-je faire cela plutôt que de mettre la nouvelle fonctionnalité dans une sous-classe? D'ailleurs, pourquoi une approche mixin / héritage multiple serait-elle préférable à l'utilisation de la composition?

Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?


Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?

Un mixin est une forme limitée d'héritage multiple. Dans certaines langues, le mécanisme d'ajout d'un mixin à une classe est légèrement différent (en termes de syntaxe) de celui de l'héritage.

Dans le contexte de Python en particulier, un mixin est une classe parente qui fournit des fonctionnalités aux sous-classes mais qui n'est pas destinée à être instanciée elle-même.

Ce qui pourrait vous amener à dire, "c'est juste l'héritage multiple, pas vraiment un mixage" est si la classe qui pourrait être confondue pour un mixin peut réellement être instanciée et utilisée - alors en effet c'est une différence sémantique, et très réelle.

Exemple d'héritage multiple

Cet exemple, issu de la documentation , est un OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

Il sous-classe à la fois le Counter et le OrderedDict du module de collections .

Les deux Counter et OrderedDict sont destinés à être instanciés et utilisés de leur propre chef. Cependant, en les sous-classant tous les deux, nous pouvons avoir un compteur qui est ordonné et réutilise le code dans chaque objet.

C'est un moyen puissant de réutiliser le code, mais cela peut aussi être problématique. S'il s'avère qu'il y a un bug dans l'un des objets, le fait de le réparer sans précaution pourrait créer un bug dans la sous-classe.

Exemple de Mixin

Les mixins sont généralement promus comme le moyen de réutiliser le code sans problèmes de couplage potentiels que l'héritage multiple coopératif, comme le OrderedCounter, pourrait avoir. Lorsque vous utilisez des mixins, vous utilisez des fonctionnalités qui ne sont pas aussi étroitement couplées aux données.

Contrairement à l'exemple ci-dessus, un mixin n'est pas destiné à être utilisé seul. Il fournit des fonctionnalités nouvelles ou différentes.

Par exemple, la bibliothèque standard a deux mixins dans la bibliothèque socketserver .

Les versions forking et threading de chaque type de serveur peuvent être créées à l'aide de ces classes de mixage. Par exemple, ThreadingUDPServer est créé comme suit:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

La classe de mélange vient en premier, car elle remplace une méthode définie dans UDPServer. La définition des différents attributs modifie également le comportement du mécanisme de serveur sous-jacent.

Dans ce cas, les méthodes mixin remplacent les méthodes de la définition d'objet UDPServer pour autoriser la concurrence.

La méthode surchargée semble être process_request et fournit également une autre méthode, process_request_thread . Ici, il vient du code source :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

Un exemple contredit

C'est un mixin qui est principalement à des fins de démonstration - la plupart des objets évolueront au-delà de l'utilité de cette repr:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

et l'utilisation serait:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

Et utilisation:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

Peut-être que quelques exemples vont aider.

Si vous construisez une classe et que vous voulez qu'elle agisse comme un dictionnaire, vous pouvez définir toutes les __ __ méthodes nécessaires. Mais c'est un peu douloureux. Comme alternative, vous pouvez juste en définir quelques uns, et hériter (en plus de tout autre héritage) de UserDict.DictMixin (déplacé vers collections.DictMixin dans py3k). Cela aura pour effet de définir automatiquement tout le reste de l'API du dictionnaire.

Un deuxième exemple: la boîte à outils GUI wxPython vous permet de faire des contrôles de liste avec plusieurs colonnes (comme, disons, l'affichage du fichier dans l'Explorateur Windows). Par défaut, ces listes sont assez basiques. Vous pouvez ajouter des fonctionnalités supplémentaires, telles que la possibilité de trier la liste par une colonne particulière en cliquant sur l'en-tête de colonne, en héritant de ListCtrl et en ajoutant des mixins appropriés.


Tout d'abord, vous devez noter que les mixins existent uniquement dans les langages à plusieurs héritages. Vous ne pouvez pas faire un mixin en Java ou en C #.

Fondamentalement, un mixin est un type de base autonome qui offre une fonctionnalité limitée et une résonance polymorphe pour une classe enfant. Si vous pensez en C #, pensez à une interface que vous n'avez pas à implémenter car elle est déjà implémentée; vous en héritez et profitez de ses fonctionnalités.

Les mixines ont généralement une portée étroite et ne sont pas destinées à être étendues.

[edit - pourquoi?]

Je suppose que je devrais expliquer pourquoi, puisque vous avez demandé. Le gros avantage est que vous n'avez pas à le faire vous-même encore et encore. En C #, le plus grand endroit où un mixin pourrait bénéficier peut être du pattern de disposition . Chaque fois que vous implémentez IDisposable, vous voulez presque toujours suivre le même modèle, mais vous finissez par écrire et réécrire le même code de base avec des variations mineures. S'il y avait un mixage Disposal extensible, vous pourriez vous épargner beaucoup de frappe supplémentaire.

[edit 2 - pour répondre à vos autres questions]

Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?

Oui. La différence entre un mélange et un héritage multiple standard est juste une question de sémantique; une classe qui a plusieurs héritages peut utiliser un mix dans le cadre de cet héritage multiple.

Le but d'un mixin est de créer un type qui peut être "mélangé" à tout autre type via l'héritage sans affecter le type d'héritage tout en offrant des fonctionnalités bénéfiques pour ce type.

Encore une fois, pensez à une interface déjà implémentée.

Personnellement, je n'utilise pas de mixins, car je me développe principalement dans un langage qui ne les supporte pas, donc j'ai du mal à trouver un exemple décent qui suffira à fournir ce "ahah!" moment pour vous. Mais je vais essayer à nouveau. Je vais utiliser un exemple qui est artificiel - la plupart des langages fournissent déjà cette fonctionnalité d'une manière ou d'une autre - mais j'espère que cela expliquera comment les mixins sont censés être créés et utilisés. Voici:

Supposons que vous ayez un type que vous voulez pouvoir sérialiser vers et depuis XML. Vous voulez que le type fournisse une méthode "ToXML" qui renvoie une chaîne contenant un fragment XML avec les valeurs de données du type, et un "FromXML" qui permette au type de reconstruire ses valeurs de données à partir d'un fragment XML dans une chaîne. Encore une fois, ceci est un exemple artificiel, alors peut-être que vous utilisez un flux de fichier, ou une classe XML Writer de la bibliothèque d'exécution de votre langage ... peu importe. Le point est que vous voulez sérialiser votre objet en XML et récupérer un nouvel objet à partir de XML.

L'autre point important dans cet exemple est que vous voulez le faire de manière générique. Vous ne voulez pas avoir à implémenter une méthode "ToXML" et "FromXML" pour chaque type que vous voulez sérialiser, vous voulez des moyens génériques pour vous assurer que votre type va le faire et ça marche. Vous voulez la réutilisation de code.

Si votre langue le supporte, vous pouvez créer le mixin XmlSerializable pour faire votre travail pour vous. Ce type implémenterait les méthodes ToXML et FromXML. Il serait possible, en utilisant un mécanisme qui n'est pas important pour l'exemple, de rassembler toutes les données nécessaires à partir de n'importe quel type avec lequel construire le fragment XML retourné par ToXML et il serait également capable de restaurer ces données lorsque FromXML est appelé.

Et c'est tout. Pour l'utiliser, vous auriez tout type qui doit être sérialisé en XML hérité de XmlSerializable. Chaque fois que vous deviez sérialiser ou désérialiser ce type, vous appeliez simplement ToXML ou FromXML. En fait, étant donné que XmlSerializable est un type à part entière et polymorphe, vous pourriez concevoir un sérialiseur de document qui ne connaît rien à votre type d'origine, n'acceptant, disons, qu'un tableau de types XmlSerializable.

Imaginons maintenant d'utiliser ce scénario pour d'autres choses, comme créer un mixin qui assure que chaque classe qui le mélange enregistre chaque appel de méthode, ou un mixin qui fournit la transactionnalité au type qui le mélange. La liste peut s'allonger encore et encore.

Si vous pensez simplement à un mixin comme un petit type de base conçu pour ajouter une petite quantité de fonctionnalités à un type sans affecter autrement ce type, alors vous êtes en or.

Espérons. :)


Un mixin est un type spécial d'héritage multiple. Il y a deux situations principales où les mixines sont utilisées:

  1. Vous voulez fournir beaucoup de fonctionnalités optionnelles pour une classe.
  2. Vous voulez utiliser une fonctionnalité particulière dans un grand nombre de classes différentes.

Pour un exemple de numéro un, considérons le système de demande et de réponse de werkzeug . Je peux créer un objet de requête simple en disant:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Si je veux ajouter accepter le support de l'en-tête, je le ferais

from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
    pass

Si je voulais créer un objet de requête prenant en charge les en-têtes, les etags, l'authentification et la prise en charge de l'agent utilisateur, je pouvais le faire:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin):
    pass

La différence est subtile, mais dans les exemples ci-dessus, les classes de mixage n'ont pas été conçues pour se suffire à elles-mêmes. Dans l'héritage multiple plus traditionnel, le AuthenticationMixin (par exemple) serait probablement quelque chose de plus comme Authenticator . Autrement dit, la classe serait probablement conçue pour se suffire à elle-même.


Je pense qu'il y a eu quelques bonnes explications ici, mais je voulais donner un autre point de vue.

Dans Scala, vous pouvez faire des mixins comme cela a été décrit ici, mais ce qui est très intéressant, c'est que les mixins sont fusionnés pour créer un nouveau type de classe à hériter. En substance, vous n'héritez pas de plusieurs classes / mixins, mais plutôt, générez un nouveau type de classe avec toutes les propriétés du mixin à hériter. Cela a du sens puisque Scala est basé sur la JVM où l'héritage multiple n'est pas actuellement supporté (à partir de Java 8). Ce type de classe mixin, en passant, est un type spécial appelé Trait in Scala.

Il est fait allusion à la façon dont une classe est définie: class NewClass étend FirstMixin avec SecondMixin avec ThirdMixin ...

Je ne sais pas si l'interpréteur CPython fait la même chose (mixin class-composition) mais je ne serais pas surpris. Aussi, venant d'un fond C ++, je n'appellerais pas un ABC ou une 'interface' équivalente à un mixin - c'est un concept similaire mais divergent dans l'utilisation et l'implémentation.


mixin permet d'ajouter des fonctionnalités dans une classe, c'est-à-dire que vous pouvez interagir avec les méthodes définies dans un module en incluant le module dans la classe désirée. Bien que ruby ​​ne supporte pas l'héritage multiple, mais fournit mixin comme une alternative pour y parvenir.

Voici un exemple qui explique comment l'héritage multiple est réalisé en utilisant mixin.

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

Je pense à eux comme une manière disciplinée d'utiliser l'héritage multiple - parce que finalement un mixin est juste une autre classe python qui (pourrait) suivre les conventions sur les classes qui sont appelées mixins.

Ma compréhension des conventions qui régissent quelque chose que vous appelleriez un Mixin est un Mixin:

  • ajoute des méthodes mais pas des variables d'instance (les constantes de classe sont OK)
  • hérite seulement de l' object (en Python)

De cette façon, il limite la complexité potentielle de l'héritage multiple et facilite le suivi du flux de votre programme en limitant l'endroit où vous devez regarder (par rapport à l'héritage multiple complet). Ils sont similaires aux modules ruby.

Si je veux ajouter des variables d'instance (avec plus de flexibilité que ce qui est autorisé par un héritage simple) alors j'ai tendance à opter pour la composition.

Cela dit, j'ai vu des classes appelées XYZMixin qui ont des variables d'instance.


Cette réponse vise à expliquer les mixins avec des exemples qui sont:

  • autonome : bref, sans avoir besoin de connaître les bibliothèques pour comprendre l'exemple.

  • en Python , pas dans d'autres langues.

    Il est compréhensible qu'il y ait eu des exemples d'autres langages tels que Ruby puisque le terme est beaucoup plus commun dans ces langages, mais c'est un fil de Python .

Il doit également considérer la question controversée:

L'héritage multiple est-il nécessaire ou non pour caractériser un mixin?

Définitions

Je n'ai pas encore vu une citation d'une source "autoritaire" disant clairement ce qu'est un mixin en Python.

J'ai vu 2 définitions possibles d'un mixin (si elles doivent être considérées comme différentes des autres concepts similaires tels que les classes de base abstraites), et les gens ne sont pas entièrement d'accord sur ce qui est correct.

Le consensus peut varier entre différentes langues.

Définition 1: pas d'héritage multiple

Un mixin est une classe telle qu'une méthode de la classe utilise une méthode qui n'est pas définie dans la classe.

Par conséquent, la classe n'est pas destinée à être instanciée, mais plutôt à servir de classe de base. Sinon, l'instance aurait des méthodes qui ne peuvent pas être appelées sans lever une exception.

Une contrainte que certaines sources ajoutent est que la classe peut ne pas contenir de données, seulement des méthodes, mais je ne vois pas pourquoi cela est nécessaire. En pratique cependant, beaucoup de mixins utiles n'ont pas de données, et les classes de base sans données sont plus simples à utiliser.

Un exemple classique est l'implémentation de tous les opérateurs de comparaison à partir de seulement <= et == :

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

Cet exemple particulier aurait pu être réalisé via le décorateur functools.total_ordering() , mais le jeu consistait à réinventer la roue:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

Définition 2: héritage multiple

Un mixin est un modèle de conception dans lequel une méthode d'une classe de base utilise une méthode qu'elle ne définit pas, et cette méthode est destinée à être implémentée par une autre classe de base , et non par la dérivée comme dans la définition 1.

Le terme classe mixin fait référence aux classes de base qui sont destinées à être utilisées dans ce modèle de conception (TODO ceux qui utilisent la méthode, ou ceux qui l'implémentent?)

Il n'est pas facile de décider si une classe donnée est un mixin ou non: la méthode pourrait simplement être implémentée sur la classe dérivée, auquel cas nous revenons à la définition 1. Vous devez considérer les intentions de l'auteur.

Ce modèle est intéressant car il est possible de recombiner des fonctionnalités avec différents choix de classes de base:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

Occurrences Python faisant autorité

Dans la documentation officielle de collections.abc, la documentation utilise explicitement le terme Méthodes Mixin .

Il indique que si une classe:

  • implémente __next__
  • hérite d'un seul Iterator classe

alors la classe obtient une méthode de __iter__ gratuitement.

Par conséquent, au moins sur ce point de la documentation, mixin ne nécessite pas d'héritage multiple et est cohérent avec la définition 1.

La documentation pourrait bien sûr être contradictoire à différents points, et d'autres bibliothèques Python importantes pourraient utiliser l'autre définition dans leur documentation.

Cette page utilise également le terme Set mixin , qui suggère clairement que des classes comme Set et Iterator peuvent être appelées classes Mixin.

Dans d'autres langues

  • Ruby: Clairement, ne nécessite pas d'héritage multiple pour mixin, comme mentionné dans les principaux livres de référence tels que Programming Ruby et The Ruby Programming Language

  • C ++: Une méthode qui n'est pas implémentée est une méthode virtuelle pure.

    La définition 1 coïncide avec la définition d'une classe abstraite (une classe qui a une méthode virtuelle pure). Cette classe ne peut pas être instanciée.

    La définition 2 est possible avec l'héritage virtuel: Héritage multiple de deux classes dérivées


Je déconseille les mix-ins dans le nouveau code Python, si vous pouvez trouver un autre moyen de le contourner (comme la composition au lieu de l'héritage, ou juste les méthodes de correction de singe dans vos propres classes) qui n'est pas beaucoup plus effort.

Dans les classes à l'ancienne, vous pouvez utiliser des mix-ins pour saisir quelques méthodes d'une autre classe. Mais dans le monde du nouveau style, tout, même le mix-in, hérite de l' object . Cela signifie que toute utilisation de l'héritage multiple introduit naturellement des problèmes MRO .

Il existe des moyens de faire fonctionner le MRO à héritage multiple en Python, notamment la fonction super (), mais cela signifie que vous devez faire toute la hiérarchie de classe en utilisant super (), et il est beaucoup plus difficile de comprendre le flux de contrôle.


J'ai lu que vous avez un arrière-plan ac #. Un bon point de départ pourrait donc être une implémentation de mixin pour .NET.

Vous pouvez consulter le projet codeplex sur http://remix.codeplex.com/

Regardez le lien du Symposium lang.net pour avoir un aperçu. Il y a encore plus à venir sur la documentation sur la page codeplex.

ce qui concerne Stefan


OP a mentionné qu'il / elle n'a jamais entendu parler de mixin en C ++, peut-être parce qu'ils sont appelés Curlyly Recurring Template Pattern (CRTP) en C ++. En outre, @Ciro Santilli a mentionné que mixin est implémenté via la classe de base abstraite en C ++. Alors que la classe de base abstraite peut être utilisée pour implémenter mixin, elle est excessive car la fonctionnalité de la fonction virtuelle au moment de l'exécution peut être obtenue en utilisant le modèle au moment de la compilation sans la surcharge de la recherche de table virtuelle au moment de l'exécution.

Le motif CRTP est décrit en détail here

J'ai converti l'exemple de python dans la réponse de @Ciro Santilli en C ++ en utilisant la classe template ci-dessous:

#include <iostream>
#include <assert.h>

template <class T>
class ComparableMixin {
public:
    bool operator !=(ComparableMixin &other) {
        return ~(*static_cast<T*>(this) == static_cast<T&>(other));
    }
    bool operator <(ComparableMixin &other) {
        return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
    }
    bool operator >(ComparableMixin &other) {
        return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
    }
    bool operator >=(ComparableMixin &other) {
        return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
    }
};

class Integer: public ComparableMixin<Integer> {
public:
 Integer(int i) {
     this->i = i;
 }
 int i;
 bool operator <=(Integer &other) {
     return (this->i <= other.i);
 }
 bool operator ==(Integer &other) {
     return (this->i == other.i);
 }
};

int main() {

    Integer i(0) ;
    Integer j(1) ;

    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

Je viens d'utiliser un python mixin pour implémenter des tests unitaires pour les pulsations python. Normalement, un milter parle à un MTA, rendant les tests unitaires difficiles. Le test mixin remplace les méthodes qui communiquent avec le MTA et crée un environnement simulé piloté par des scénarios de test.

Donc, vous prenez une application milter non modifiée, comme spfmilter, et mixin TestBase, comme ceci:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

Ensuite, utilisez TestMilter dans les scénarios de test pour l'application milter:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='[email protected]')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup





mixins