Exemple de plugin pour Trac

Trac, outil de gestion de projet de développement, est conçu pour recevoir de nouvelles fonctionnalités par ajout de plugin. Ce billet montre quelques techniques pour réaliser des plugins eux-mêmes extensibles.

Le schema suivant montre les éléments essentiels du modèle de plugin que Trac offre.

Pour créer un plugin il suffit de créer une classe qui hérite de Component. Ainsi Trac va charger la classe au démarrage.

class Core(Component):
 """
 Main class of the Trac Cron Plugin. This is the entry point
 for Trac plugin architecture
 """
 pass

Un composant est un objet passif qui est appelé lorsque Trac est activé. Comme c’est une application web, c’est sur requête du client que tout s’active. Pour répondre à ces sollicitations le plugin doit implémenter une interface connue que Trac va appeler.

Par exemple le plugin peut définit sa propre interface d’administration. Pour cela il doit implémenter IAdminPanelProvider er ITemplateProvider

class Core(Component):
 """
 Main class of the Trac Cron Plugin. This is the entry point
 for Trac plugin architecture
 """    

 implements(IAdminPanelProvider, ITemplateProvider)

 # IAdminProviderPanel

 def get_admin_panels(self, req):
  if ('TRAC_ADMIN' in req.perm) :
   yield ('tracini', 'trac.ini', 'cron_admin', u'Trac Cron')

 def render_admin_panel(self, req, category, page, path_info):
  req.perm.assert_permission('TRAC_ADMIN')
  data = {}
  return 'cron_admin.html', data

 # ITemplateProvider

 def get_htdocs_dirs(self):
  return []

 def get_templates_dirs(self):
  from pkg_resources import resource_filename
  return [resource_filename(__name__, 'templates')]

L’implémentation au sens Trac consiste pour un plugin à s’enregistrer auprès de Trac comme fournisseur des interfaces par le biais de l’appel à la fonction

implements(*args)

en passant les interfaces voulues.

Dans l’exemple ci-dessous Trac va appeler la fonction get_admin_panel() quand il voudra créer la liste des panneau de configuration et render_admin_panel() quand il voudra afficher la panneau de configuration du plugin donné.

Mais on peut créer des plugins actifs qui initient des traitements périodiquement. Pour cela Trac ne propose rien, il faut faire soit même un plugin qui crée une Thread lors de son instanciation.

class Core(Component):
  def__init__(self,*args,**kwargs):
    Component.__init__(self, *args, **kwargs)
    Timer(60 , self.wake_up).start()

  def wake_up(self):
    print "Hello, one minute again"
    Timer(60 , self.wake_up).start()

Il existe un plugin qui se charge de toute la mécanique d’ordonnancement laissant le développeur se concentrer sur la tâche à faire. Il s’agit de TracCronPlugin.

Cela nous amène à expliquer comment écrire des plugin extensibles. La règle d’or est de ne pas lier son plugin avec du code interne ou même des class internes. Il faut toujours penser une fonctionnalité comme une interface.

La première étape est de bien isoler l’interface de la fonctionnalité. Par exemple on peut isoler l’interface d’un tâche comme devant avoir une méthode pour être lancée, une méthode pour s’identifier et une méthode de description. Alors l’interface d’une tâche peut se définir ainsi:

class ICronTask(Interface):
 """
 Interface for component task
 """

 def wake_up(self, *args):
  """
  Call by the scheduler when the task need to be executed
  """
  raise NotImplementedError

 def getId(self):
  """
  Return the key to use in trac.ini to cinfigure this task
  """
  raise NotImplementedError

 def getDescription(self):
  """
  Return the description of this task to be used in the admin panel.
  """
  raise NotImplementedError

Supposons que dans Trac il y ait des plugin qui implémentent cette interface (nous avons vu plus haut comment faire), il reste alors au plugin utilisateur d’utiliser un point d’extension en instanciant la classe ExtensionPoint comme ceci:

class Core(Component):
 task_event_list = ExtensionPoint(ITaskEventListener)

 def do_something_with_task(self):
  for task in self.cron_tack_list:
   # do something with a task
   task.wake_up()

On voit qu’un point d’extension est une séquence qui permet d’itérer sur les composants (plugin) qui implémentent l’interface passé en paramètre du constructeur de  ExtensionPoint

L’exemple complet peut être trouver en regardant le code source de TracCronPlugin

Cet article, publié dans Application, Langage, est tagué , . Ajoutez ce permalien à vos favoris.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s