Contenido

Yapsy, un sistema de plugins pythónico

Me gustan los programas que pueden evolucionar. Un reproductor de vídeo debe evolucionar a medida que lo hacen los formatos de vídeo, adaptándose a los nuevos.

Además, me gustan los programas que puedo extender. Añadir nueva funcionalidad que los desarrolladores originales no pensaron en su momento.

Éstas son dos de las aplicaciones de los plugins, y podemos tenerlas de una forma muy sencilla en Python mediante Yapsy

Yapsy nos ayuda a la gestión de los plugins mediante la búsqueda, carga y gestión automáticas. Veremos también que las creación de nuevos plugins es ridículamente fácil.

Python

Instalación

Para instalar, descargar de pip:

1
pip install yapsy

Creando un plugin

Antes de nada, vamos a crearnos un plugin. Para Yapsy, un plugin es cualquier cosa que hereda de yapsy.IPlugin, aunque hay que registrarlo añadiendo un pequeño archivo adicional de descripción, cuya extensión por defecto es yapsy-plugin

Vamos a hacer dos ejemplos. En el primero los plugins sólo saludarán, de manera que veamos lo fácil que es usar Yapsy. En el segundo, vamos a hacer algo relativamente útil: un validador de sintaxis de archivos. En concreto, vamos a validar archivos JSON y YAML (sólo validaremos la sintaxis, cargando los archivos).

Ejemplo 1: Saludando

Lo primero que necesitamos es el programa principal. Éste lo que hará es cargar los plugins e invocar un método dentro de ellos para que saluden:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from yapsy.PluginManager import PluginManager


def main():
    simplePluginManager = PluginManager()
    simplePluginManager.setPluginPlaces(["plugins"])
    simplePluginManager.collectPlugins()

    for plugin in simplePluginManager.getAllPlugins():
        plugin.plugin_object.greetings()


if __name__ == '__main__':
    main()

Muy poco que decir: se indica dónde están los plugins y se cargan, después se recorren todos ellos para ejecutar el método greetings. Como veis, se buscarán los archivos en el directorio relativo “plugins”.

Nuestro primer plugin:

1
2
3
4
5
from yapsy.IPlugin import IPlugin

class Json(IPlugin):
    def greetings(self):
        print "This is the Json validator"

Como se puede observar, basta con heredar de yapsy.IPlugin.IPlugin.

Sin embargo, esto no le basta a Yapsy, ya que no encontrará el plugin. Yapsy necesita cierta meta-información, que se proporciona en un archivo con extensión yapsy-plugin, aunque se puede cambiar:

1
2
3
4
5
6
7
8
9
[Core]
Name = Json validator
Module = jsonmgr

[Documentation]
Author = Miguel Angel Garcia
Version = 0.1
Website = http://magmax.org
Description = Validates a JSON file

Ejecutando:

1
2
3
$ python example.py
This is the Json validator
$

Podemos crear otro plugin más:

1
2
3
4
5
from yapsy.IPlugin import IPlugin

class Yaml(IPlugin):
    def greetings(self):
        print "This is the Yaml validator"
1
2
3
4
5
6
7
8
9
[Core]
Name = Yaml validator
Module = yamlmgr

[Documentation]
Author = Miguel Angel Garcia
Version = 0.1
Website = http://magmax.org
Description = Validates a YAML file

Ejemplo 2: Validando

Vamos ahora a hacer un pequeño programa que valide la sintaxis de archivos JSON y YAML, pero de forma plugable. Lo primero que necesitamos es un programa (disculpad los 4 niveles de indentación):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import argparse
from yapsy.PluginManager import PluginManager


def main():
    parser = argparse.ArgumentParser(description='Validates file format')
    parser.add_argument('files', nargs='+',
                        help='files to be validated')
    parser.add_argument('--verbose', action='store_true',
                        default=False,
                        help='Shows help about validation')

    args = parser.parse_args()

    simplePluginManager = PluginManager()
    simplePluginManager.setPluginPlaces(["plugins"])
    simplePluginManager.collectPlugins()

    for filename in args.files:
        supported = False
        for plugin in simplePluginManager.getAllPlugins():
            methods = plugin.plugin_object
            if methods.should_manage(filename):
                supported = True
                try:
                    plugin.plugin_object.validate(filename)
                    print('%s sintax ok for %s' % (filename, plugin.name))
                    break
                except Exception as e:
                    print('Invalid file %s for %s' % (filename, plugin.name))
                    if args.verbose:
                        print(e)
        if not supported:
            print('%s cannot be validated' % filename)

if __name__ == '__main__':
    main()

Se puede ver que hay más código. En concreto, se llama a los plugins para comprobar si deben manejar esa extensión o no, y posteriormente para validar.

Ahora nos hacen falta los plugins. Vamos con el de JSON primero:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from yapsy.IPlugin import IPlugin
import json


class Json(IPlugin):
    def should_manage(self, filename):
        return filename.lower().endswith('.json')

    def validate(self, filename):
        with file(filename) as fd:
            json.load(fd)
1
2
3
4
5
6
7
8
9
[Core]
Name = Json validator
Module = jsonmgr

[Documentation]
Author = Miguel Angel Garcia
Version = 0.1
Website = http://magmax.org
Description = Validates a JSON file

Y ahora con el de YAML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from yapsy.IPlugin import IPlugin
import yaml

class Yaml(IPlugin):
    def should_manage(self, filename):
        lower = filename.lower()
        return lower.endswith('.yaml') or lower.endswith('.yml')

    def validate(self, filename):
        with file(filename) as fd:
            yaml.load(fd)
1
2
3
4
5
6
7
8
9
[Core]
Name = Yaml validator
Module = yamlmgr

[Documentation]
Author = Miguel Angel Garcia
Version = 0.1
Website = http://magmax.org
Description = Validates a YAML file

Y ejecutándolo con algunos archivos:

1
2
3
4
5
6
$ python example.py example.json example.yaml validator.py bad.json bad.yaml
example.json sintax ok for Json validator
example.yaml sintax ok for Yaml validator
example.py cannot be validated
Invalid file bad.json for Json validator
Invalid file bad.yaml for Yaml validator

Conclusiones

Yapsy es un sistema de plugins sencillo y fácil de usar, pero no por ello menos potente. Soporta configuración de plugins y auto-instalado de los mismos.

Aquí he mostrado sólo ejemplos sencillos, pero se puede complicar mucho más. Si tenemos distintos tipos de plugins, podemos crear subclases de yapsy.IPlugin.IPlugin y filtrarlos por tipo a la hora de invocar,

Pero para mí la principal ventaja es que no tiene dependencias difíciles: sólo Python estándar. Eso permite usarlo hasta en proyectos muy pequeños. Yo mismo comencé un proyecto parecido hace tiempo… que voy a desmantener tras descubrir Yapsy.

Y como al final el validador me ha gustado, y quizá alguien, algún día, quiera continuarlo, lo he dejado en GitHub.