Vous êtes ici : Accueil / 2009 / Août / Créer une interface Zope sur mesure

Créer une interface Zope sur mesure

écrit le 08/04/2010 Par alexandre
Il arrive parfois de vouloir créer une interface Zope à la volée et c'est finalement un peu plus compliqué qu'il n'y paraît. Petit tutoriel !

Si vous pensiez utiliser les techniques habituelles sur les classes Python, vous allez vous heurter à une difficulté : les interfaces utilisent une métaclasse. La preuve

>>> class A(object):
...     pass
...
>>> A
<class '__main__.A'>

Ça, c'est pour une classe normale, et pour une interface ? Voyons ça

>>> import zope.interface as zi
>>> import zope.schema as zs
>>> class IMine(zi.Interface):
...     a = zs.Bool(u'Un champs')
...
>>> IMine
<InterfaceClass __main__.IMine>

Oui, on n'a pas une class mais une InterfaceClass.

En Python, si j'avais voulu ajouter des attributs à une classe, j'aurais pu le faire très simplement

>>> A.x = 1
>>> A.x
1
>>> A().x # pour une instance
1

Avec mon interface, Python me permet certes de le faire mais les champs que j'ajouterai ainsi ne seront pas vraiment comptabilisés comme faisant partie de l'interface

>>> IMine.names() # all defined attributes
['a']
>>> IMine.x = zs.Bool(u'Mon champs')
>>> IMine.x
<zope.schema._bootstrapfields.Bool object at ...>

Mais x n'est pas pris en compte

>>> IMine.names()
['a']

Y aurait-il un peu de magie dans les interfaces ? Presque ! Comme nous l'avons vu, les interfaces ne sont pas des classes régulières. Sans entrer dans les détails, les interfaces de Zope sont en réalité basées sur de la programmation orientée prototype (comme en javascript), via un bricolage simple des auteurs de zope.interface (mentionné dans le PEP 245)

Au final, à la création d'une pseudo classe de type interface, un ensemble de traitements est réalisé par le module zope.interface pour recueillir les informations sur les attributs de l'interface.

Serions-nous bloqués pour autant ? Que nenni ! La création dynamique d'une interface reste possible mais se fait via la construction d'une instance InterfaceClass à laquelle nous pouvons fournir un ensemble de paramètres : un nom (name), la liste des interfaces héritées (bases) , un dictionnaire des attributs définis (attrs).

Pour créer notre interface IMine, nous pouvons donc faire

>>> import zope.interface.interface as zii
>>> IMine = zii.InterfaceClass(name = 'IMine',
...                        bases = (zi.Interface,),
...                        attrs = dict( a = zs.Bool(u'Champs a'),
...                                      x = zs.Bool(u'Champs x'),))
>>> IMine
<InterfaceClass __main__.IMine>
>>> IMine.names()
['a', 'x']

Au final on a bien toute la liberté qu'apporte l'aspect dynamique, comme Python nous y a habitués. Mais attention, vous aurez appris une chose : les interfaces ne sont pas des classes !

Actions sur le document