Plug n’ Play: um sistema de plugins genérico para python

Nesse post veremos um projeto open source chamado Plug n’ Play. É um sistema de plugins genérico para python. Isso significa que com esse projeto é possível desenvolver de forma modular e também adaptar códigos existentes para que, a partir de então, seja possível adicionar novas funcionalidades sem modificar o código principal do programa.

Esse post é o primeiro de uma série que espero que seja longa. Tenho estudado python desde o início desse ano, de lá pra cá tenho lido bastante sobre a linguagem e lido código de muitas outras pessoas. Em abril enviei meu primeiro pull request (uma pequena correção) para um projeto que encontrei em um dia de estudos de python. Depois, ainda nesse mesmo projeto corrigi meu primeiro bug. 6 meses depois, devido às minhas contribuições, ganhei uma vaga no time (http://github.com/pyroutes). Essa vaga me deu uma nova motivação e tenho certeza que inconscientemente foi o que motivou o desenvolvimento desse primeiro projeto.

Plug n’ Play

Plug n’ Play (PnP) é um projeto open source com o objetivo de ajudar as pessoas a escrever softwares melhores. Com ele é possível escrever código reutilizável e extensível. Seu software será plugável, isto é, terá a possibilidade de receber novas funcionalidades implementadas por terceiros.

O melhor de tudo é que usando o PnP você não estará preso a nennuma tecnologia ou plataforma. Você pode usá-lo em um projeto web, desktop, mobile. Tudo que você precisa é python.

A essencia de um software plugável é você dar oportunidade de um código externo ser chamado. Em certos pontos de seu código você chama os códigos externos que estão interessados daquele evento que está ocorrendo. O problema começa em saber quem são os interessados em um determinado evento do seu software. O PnP resolve isso de uma forma muito simples: Você cria Interfaces, definindo alguns métodos e qualquer código de terceiro pode implementar. E o controle de quem está interessado em quais interfaces é mantido pelo PnP.

O mais interessante é que um plugin também pode definir suas próprias interfaces, que outro plugin pode implementar, ou seja, um plugin também se torna plugável! Entendeu? Ainda comigo? Ok, continuando…

O Framework

O Pnp te tá, basicamente, a oportunidade de fazer três coisas:

  • Definir pontos de extensão do seu software;
  • Descobrir quem são os objetos interessados em cada ponto de extensão do seu software;
  • Carregar arquivos .py externos para que seja possível executar código externo. Os arquivos .py são verdadeiramente os plugins.

Um exemplo

Nada melhor do que um código de exemplo para entermos melhor como tudo funciona. Nosso exemplo será um software simples. Pense em um copiador de arquivos. Para garantir que a cópia foi feita com sucesso, no fim da cópia vamos calcular a hash MD5 dos dois arquivos, o original e a cópia. Pense nesse software de forma não extensível. É um software bastante simples de ser feito. Agora pense qual seria o esforço para adicionar um novo cálculo de hash, um SHA-1 por exemplo. E se você quisesse um terceiro cálculo? Então você pegou a idéia… Vamos ao código:

import hashlib

import sys
import os
import shutil

original = sys.argv[1]
copy = sys.argv[2]

shutil.copyfile(original, copy)

md5_original = hashlib.md5(file(original).read()).hexdigest()
md5_copia = hashlib.md5(file(copia).read()).hexdigest()

if md5_original != md5_copia:
  print "MD5 Check failed"
  # Do something.

Cada vez que for necessário implementar uma nova checagem, teremos que modificar o código principal. Vejamos um exemplo usando PnP, onde o ponto extensível é justamente o cálculo das hashes.

from plugnplay import Interface, set_plugin_dirs, load_plugins

class HashChecker(Interface):

  '''
   Return True/False if the check was OK.
  '''
  def check(original, copy):
    pass

import sys
import os
import shutil

set_plugin_dirs('/tmp/plugins') #Choose your plugin dir here
load_plugins()

original = sys.argv[1]
copy = sys.argv[2]

shutil.copyfile(original, copy)

#Return what checkers do we have
checkers = HashChecker.implementors()
for checker in checkers:
  if not checker.check(original, copy):
    print "Hash Check failed"
    # Do something

Agora temos um código único, independente da quantidade de hashes que vamos calcular. Podemos ter apenas a conferência com MD5 como também podemos ter a conferência com quaisquer outras hashes. Caso não exista nenhum código interessado em ser um HashCkecher, a chamada HashChecker.implementors() retorna uma lista vazia ([]).

Agora precisamos implementar um exemplo de plugin:

from plugnplay import Plugin
#Here we import the extension points of the project we want do extend
from myproject import HashChecker

class MD5Checker(Plugin):
  implements = [HashChecker]

  def check(original, copy):
    #Here comes the MD5 hash checking code.

Basta colocar esse plugin dentro da pasta onde o nosso programa procura pelos plugins (nesse caso /tmp/plugins) e nosso código já será capaz de calcular a hash MD5 dos arquivos.

Exaplicando: A linha mais importante no código do plugin é essa:

  implements = [HashChecker]

Essa linha diz ao PnP que a classe MD5Checker está interessada nos eventos da classe HashChecker e isso é controlado em tempo de execução pelo PnP para que seja possível executar a seguinte linha:

checkers = HashChecker.implementors()

Que retorna uma lista de instâncias dos objetos interessados nos eventos de uma interface específica, nesse caso HashChecker.

Instalação

O projeto Pnp está publicado no Python package Index (http://pypi.python.org/pypi/plugnplay/) e pode ser instalado com um simples comando:

# pip install plugnplay

ou com easy_install

# easy_install plugnplay

E aí, gostou do projeto? Me mande um email dizendo que achou. Faça um fork de repositório e comece a implementar alguma coisa. Ou melhor ainda, implemente um projeto seu usando o PnP. Essa, sem dúvidas, é a melhor forma de perceber problemas e ter novas idéias para que o projeto PnP evolua e seja útil para mais pessoas e mais projetos.

O projeto está hospedado em meu github: https://github.com/daltonmatos/plugnplay. Vai lá, dá uma lida no código. =)

  1. #1 por Maciel de Souza em 18/01/2014 - 17:54

    Foi uma excelente dica, me foi de grande valia!
    Parabéns.

  1. O que aprendi desenvolvendo projetos de código aberto « ~ #_

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: