Apr 22

Integració continua: buildbot + codespeed + guppy-pe + resource

Reading time: 3 – 4 minutes

Degut a un requeriment que teniem a la feina he montat un entorn d’integració continua. En escència el que es busca és el següent:

  • Llençar de forma automàtica tests sobre els commits que es fan al codi (buildbot)
  • Tenir un repositori dels resultats dels tests fàcil de consultar (web) (buildbot)
  • Suportar tests sobre rendiment (profiling) automàtics (guppy-pe + resource)
  • Poder comprovar quina és l’evolució d’aquests tests de rendiment amb una eina visual (codespeed)

Per tal d’aconseguir aquests objectius s’ha usat:

  • buildbot: que permet automatitzar l’entorn de compilació i testeix dels commits que es van fent al repositori. (esta programat en python). Per entendre millor buildbot, ús recomano llegir l’apartat: system architecure del seu manual.
  • codespeed: és una eina feta amb python+django+mysql a través d’una interficie HTTP+JSON pot injectar informació a la BBDD i a través de la GUI ens mostra:
    • overview: a través d’una taula mostra les tendències dels resultats dels benchmark associats a un executable.
    • timeline: mostra en una gràfica l’evolució dels resultats arxivats sobre un benchmark concret fets sobre un host.
  • guppy-pe: ens permet extreure dades referents als recursos de sistema que esta consumint una part del codi: classe, funció, variable, etc.
  • resource: és un módul de python que permet saber (resource.getrusage(PID)) quins recursos esta consumint un PID en un moment donat.

Com que la documentació que he fet per la feina l’he hagut de filtrar per no revelar informació interna, la documentació que publico esta en format OpenOffice i PDF perquè sigui senzill per tothom llegir-la i modificar-la si vol.

  • Paquet .tar.gz, conté:
    • fitxer de configuració buildbot, master.cfg
    • codi d’exemple per provar l’entorn, buildbot-test
    • codi del tobami-codespeed modificat perquè sigui més generalista que la versió original
    • integracion-continua-instalacion.odt: document amb notes sobre els procediments que he seguit per la instal·lació de tot plegat.
    • integracion-continua-manual.odt: manual d’usuari de tot plegat. (també la versió en pdf)
  • integracion-continua-manual.pdf: enllaço de forma directa aquest manual per si hi voleu donar un cop d’ull per saber si ús interessa el tema.

Enllaços relacionats:

Apr 16

long polling amb jquery+jsonp+couchdb (cross domain suportat)

Reading time: 6 – 9 minutes

Porto mesos somiant amb fer la prova de concepte que explico en aquest article, intentaré descriure en que consisteix però ja aviso que la cosa és un pèl complicadilla.

Funcionalitats requerides:

  • long polling: l’objectiu és rebre els canvis d’una base de dades de couchdb en temps real sense haver d’anar preguntant si hi ha canvis, sinó que aquest s’envien cada vegada que es donen de forma automàtica.
  • A través de jQuery el que vull és actualitzar una pàgina web de forma asíncrona, de forma que els nous resultats que vagin entrant a la BBDD es vagin mostrant en temps real a la pantalla.
  • JSONP, és una tècnica que ens permet rebre la sortida JSON de CouchDB i després cridar una funció de callback de JavaScript. El problema és que la funció jQuery.getJSON() original de jQuery té algunes mancances que gràcies a el plugin jQuery-JSONP podem solucionar, aquestes són: (copy/paste de la web del plugin)
    • error recovery in case of network failure or ill-formed JSON responses,
    • precise control over callback naming and how it is transmitted in the URL,
    • multiple requests with the same callback name running concurrently,
    • two caching mechanisms (browser-based and page based),
    • the possibility to manually abort the request just like any other AJAX request,
    • a timeout mechanism.
  • CouchDB és una base de dades NoSQL basada en documents que és capaç d’emetre una senyal (trigger) cada vegada que el contingut d’una base de dades canvia. Per més informació sobre el tema es pot consultar a: CouchDB: The Definitive Guide al capítol Change Notifications.
  • Cross-domain: quan es llença una petició XmlHttpRequest (la base del AJAX) amb JavaScript tenim la limitació de només poder-ho fer sobre el domini que serveix la pàgina web, cap altre port ni subdomini. Obviament tampoc un altre host. Per saltar-se aquesta restricción és quan cal recorrer a JSONP.

La prova de concepte ha estat crear una base de dades anomenada: notifcations on es guarden documents que són notificacions a mostrar a la pàgina web.

Després he programat la següent web:

<html>
<head>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="jquery.jsonp-1.1.4.js"></script>
<script type="text/javascript">
function longpoll(since) {
    var url = "http://IP_COUCHDB_SERVER:5984/notifications/_changes?include_docs=true&feed=longpoll&since="+since+"&callback=?";
    console.log("since="+since);
    $.jsonp({
        "url":url,
        "success":function(data) {
            //console.log(data);
            since=data.last_seq;
            try {
                console.log(data.results[0].doc.msg);
            } catch(err) {
                console.log("error:"+err);
            };
            longpoll(since);
        },
        "error":function(msg) {
            //console.log(msg);
            console.log('capturat error');
        }
    });
};

var url = "http://IP_COUCHDB_SERVER:5984/notifications?callback=?";
$.jsonp({
    "url":url,
    "success":function(data) {
        //console.log(data);
        longpoll(data.update_seq);
    },
    "error":function(msg) {
        console.log(msg);
    }
});
</script>
</head>
<body>
cos
</body>
</html>

El codi és força simple de seguir, primer de tot es carreguen les llibreries: jQuery 1.4.2 i jquery-jsonp 1.1.4, ambdues necessaries per cridar el métode $.jsonp que és el que realment farà la feina.

A continuació es declara la funció longpoll que té com a paràmetre el númeral que indica quin ha estat el últim canvi a la base de dades. Aquest s’utiliza per construir la petició que es fa a CouchDB:

var url = "http://IP_COUCHDB_SERVER:5984/notifications/_changes?include_docs=true&feed=longpoll&since="+since+"&callback=?";

La URL el que fa és demanar el següent:

  • els canvis (_changes)
  • incloent els documents que han canviat (include_docs=true)
  • tracta la petició com a long polling (feed=longpoll)
  • mostra els canvis des de la versió X (since=X)
  • quan enviis els canvis fes una crida a la funció de callback definida aquí (callback=?)
    • ‘?’ és substituit per jquery-jsonp per la funció anomenada ‘C’, aquest nom es pot canviar usant paràmetres en la declaració de $.jsonp() que ve a continuació

Els missatges de l’estil ‘console.log()‘ són per tenir un seguiment del que va passant a la consola de javascript del navegador.

$.jsonp() té força paràmetres possibles definits a la API, però en aquesta prova de concepte només uso ‘url’, ‘success’ i ‘error’. El primer esta clar que és, els altres dos són les funcions a cridar quan l’acció va bé o malament respectivament. Dins de la funció posem el codi referent a les accions que volem fer, per exemple, actualitzar la pàgina actual. Com que això només és una prova de concepte el que faig és mostrar missatges per consola i llestos. La part més important és fixar-se que quan la cosa ha anat bé es fa una crida a ella mateix de forma que la cosa no acabi mai. De fet quan hi ha un error es podria també fer una crida a si mateix perquè no pares de provar de llençar la petició un i altre cop, però el que he fet per provar eś que es notifiqui a la consola de javascript i prou.

Fora de la funció longpoll el que es fa és una petició JSONP per saber quina és l’últim número de seqüència de la base de dades, paràmetre necessari per entrar per primera vegada a la funció recursiva de longpoll.

Conclusions

Pot semblar tot una mica enravassat però diria que he simplificat el problema moltíssim, ja que fins ara havia estat teoritzant moltíssim sobre el tema. Fins que ahir i abans d’ahir vaig haver de posar-me a provar-ho a la pràctica per saber exactament com es podia implementar. Sota el meu punt de vista ha quedat tot força net i entenedor.

Pels que sou programadors de webs habitualment haureu tingut necessitats semblants així doncs espero que ús pugui ser tan útil com a mi, de fet, fa unes setmanes que estic treballant amb Tiny Core Linux montant un Quiet PC sobre una DOM de 512MB per usar-la de sistema de monitorització de les meves xarxes i les d’alguns clients, espero que d’aquí uns mesos pugui donar-vos més informació del projecte.

Feb 24

Microfeed – feeds via DBUS

Reading time: 2 – 2 minutes

Avui he descobert microfeed, es tracta d’una implementació d’arquitectura client-servidor que permet accedir a diferents fonts d’informació d’internet publicades via feeds. Potser el que més gràcia m’ha fet és que si volem usar aquesta llibreria per programar, per exemple, una GUI que permeti gestionar aquestes dades l’accés es fa a través de DBUS. És a dir, per un costat microfeed recull les dades de les seves fonts originals (twitter, facebook, identi.ca, etc) i la GUI es comunica amb la llibreria via DBUS. De fet, el que realment m’ha fet gràcia és que això és el que fa Telepathy però encomptes de fer-ho contra feeds ho fa contra jabber/XMPP, MSN, IRC, SIP, etc.

Una forma molt visual d’entendre que fa microfeed és amb aquest esquema:

microfeed_architecture

En principi la única GUI que he trobat que usa aquest backend és Mauku que és una aplicació pensada per Maemo.

Amb el primer cop d’ull a la llibreria no he arribat a trobar com ho fa per rebre els feeds dels llocs com twitter i facebook, imagino que ho farà via polling. Seria interessant saber si també ho pot fer per PubSub. Ja que segons l’esquema només tinc la sensació que el PubSub s’usa via DBUS. Si algún dia em poso a treballar amb la llibreria espero aclararir-ho.

/wp-includes/js/tinymce/themes/advanced/skins/wp_theme/content.css
Feb 12

eines per XMPP

This entry is part 4 of 4 in the series xmpp

Reading time: 2 – 3 minutes

A continuació adjunto una petita descripció d’algunes eines per comunicar-se amb una xarxa XMPP que poden ser molt útils:

Idavoll

Implementació del XEP-0060, o sigui, d’un servei de publish-subscribe (PubSub) esta escrit amb Python i Twisted. Bàsicament el que permet és que sobre un servidor XMPP estàndard hi podem connectar un servei basat en PubSub, o sigui, que nosaltres publiquem una serie d’informació que un seguit de clients consulten perquè hi estan subscrits. És un mètode basat en events (no-polling) molt adient per disfondre certs tipus d’informació.

Switchboard

A vegades programem shell scripts que necessiten enviar el seu resultat a la xarxa XMPP, per exemple, imagineu que volem comunicar la caiguda d’un servei a través de GTalk, doncs aquest toolkit ens simplifica moltíssim aquesta tasca. Esta programat en ruby i a part de poder-se usar des de la CLI també podem integrar-ho com a llibreria dins d’un codi en ruby.

XMPP Poetry CLI tools

El seu nom ja ho diu tot, són una col·lecció d’eines que via CLI ens permeten interactuar amb una xarxa XMPP, algunes de les seves funcions són:

  • disco: recull informació sobre serveis
  • pubsub-config: crea, configura i llança queries contra serveis pub-sub

Aquestes eines estan escrites amb Python, Twisted i Wokkel.

XMPPPHP

Llibreria de PHP5 amb suport de:

  • XMPP 1.0 (pot connectar a: GTalk, LJTalk, jabber.org, etc)
  • Suporta TLS
  • Processa diversos formats XML

Sembla força senzill d’usar, per exemple, programar un bot és tan fàcil com això:

<?php
include("xmpp.php");
$conn = new XMPP('talk.google.com', 5222, 'user', 'password', 'xmpphp', 'gmail.com', $printlog=True, $loglevel=LOGGING_INFO);
$conn->connect();
while(!$conn->disconnected) {
    $payloads = $conn->processUntil(array('message', 'presence', 'end_stream', 'session_start'));
    foreach($payloads as $event) {
        $pl = $event[1];
        switch($event[0]) {
            case 'message':
                print "---------------------------------------------------------------------------------\n";
                print "Message from: {$pl['from']}\n";
                if($pl['subject']) print "Subject: {$pl['subject']}\n";
                print $pl['body'] . "\n";
                print "---------------------------------------------------------------------------------\n";
                $conn->message($pl['from'], $body="Thanks for sending me \"{$pl['body']}\".", $type=$pl['type']);
                if($pl['body'] == 'quit') $conn->disconnect();
                if($pl['body'] == 'break') $conn->send("");
            break;
            case 'presence':
                print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
            break;
            case 'session_start':
                $conn->presence($status="Cheese!");
            break;
        }
    }
}
?>
Jan 14

CouchDB: bases de dades NoSQL

Reading time: 4 – 6 minutes

couchdb logo

couchdb logo

Abans de parlar de CouchDB, si no heu sentit a parlar mai de les bases de dades NoSQL, és important que sapigueu que no són bases de dades ralacionals, ni orientades a objectes. Sinó que es basen en un paradigme diferet, són orientades a documents.

Doncs bé, CouchDB és un projecte de la fundació Apache i és OpenSource, és clar. Algunes de les seves característiques són:

  • RESTful API
  • schema-less document store (document=JSON format w/binary support like attachments)
  • multi-version-concurrency-control model
  • user-defined query structured as map/reduce (javascript, python, C, etc)
  • incremental index update mechanism
  • multi-master replication
  • easily distributable
  • update validation
  • programat amb erlang
  • web based basic admin features
  • binding for python, C, .NET, PHP, Ruby, etc.
  • pros: retrieve information, cons: insert data

Actualment estic estudiant si usar aquest producte en un dels projectes que estic treballant. De fet, encara no tinc clar si aplica al 100% a les necessitats que tinc en el projecte però a priori s’ajusta prou bé. Perquè no penseu que això és una raresa que no coneix ningú informar-vos que Ubuntu One usa couchDB com a backend, pels que no conegueu el servei jo el vaig descobrir gràcies a l’article d’Ars Technica: Code tutorial: make your application sync with Ubuntu One.

Inicialment volia fer un manual de les funcions bàsiques de CouchDB però degut al munt de documentació que he trobat he pensat que era una tonteria re-inventar la roda, així doncs a continuació faré una ressenya de les fonts d’informació que he usat per coneixer aquesta base de dades:

  • CouchDB Implementation: descripció molt detallada i no massa extensa de com funciona per dintre aquest sistema de BBDD especialment dedicada al Pau. Destaco aquest paràgraf:
  • CouchDB is a “document-oriented” database where document is a JSON string (with an optional binary attachment). The underlying structure is composed of a “storage” as well as multiple “view indexes”. The “storage” is used to store the documents and the “view indexes” is used for query processing.

  • Serie d’articles del blog RVZ: una pequeña introducción I, II, III y IV.
  • Llibre: CouchDB: The Definitive Guide, consultable online en format HTML.
  • CouchDB.es, sobre CouchDB y NoSQL.

Enllaços orientats a les consultes:

Abans d’acabar comentar que personalmentel que més m’ha costat d’entendre de tot plegat és el tema map/reduce especialment la part de reduce, ja que no acabava de veure al 100% com funcionava i quina finalitat tenia. Potser l’error més gran que he comès és intentar buscar un paral·lelísme directe entre SQL i NoSQL. Sota el meu punt de vista no són tecnologies substitutories, més aviat complementaries ja que cada una s’ajusta a un tipus de solucions diferents. Per tant, abans que res recomano que confronteu la vostre problemàtica amb cada un dels paradigmes: orientat a objectes, bbdd relacionals i orientat a documents.

Oct 24

Python: afegir suport de plug-ins al nostre codi

Reading time: 6 – 9 minutes

A vegades quan estem fent un programa ens interessa que el nostre codi pugui ser extés sense haver de tocar la seva estructra, fins hi tot el que ens pot interessar és que aquest codi sigui extés en algunes ocasions i en d’altres no. Un altre requeriment que podem tenir també seria que qui l’extengui no siguem nosaltres. Sovint tot això i molt més s’acostuma a fer amb el que anomenem Plug-ins, per cert, sempre m’ha fet molta gràcia la traducció al català de la paraula: ‘afegitons’.

Doncs bé, com que jo de programació no hi entenc gaire li vaig demanar al Pau que m’ajudés a entendre els models de plug-ins que implementaven alguns programes fets amb Python, ja que m’ineressava integrar aquesta funcionalitat en una serie de codis que estic desenvolupament. En aquest post intentaré explicar com funciona el paradigma dels plug-ins que usa Trac.

Primer de tot cal tenir en compte que Trac usa un patró de desplegament d’objectes anomenat Singleton, o sigui que totes les instàncies d’un objecte es refereixes a la mateixa instància. De fet, no sé dir fins a quin punt és necessari que el codi segueixi aquest patró per usar el sistema de plug-ins; tot i que jo diria que almenys les parts del codi que vulguin ser exteses pel model de plug-ins de Trac l’han de seguir.

Els plug-ins de Trac tenen les següents característiques:

  • Un Plug-in és un component que extent la funcionalitat d’un altre component
  • Un Plug-in pot extendre un altre Plug-in

Per tal d’incorporar la filosofia que té Trac per suportar Plug-ins al nostre codi cal importar el component ‘trac.core’, d’aquest component usarem el següent:

  • trac.core.Interface (classe) s’usa per definir quin és el contracte que hauran de seguir els plug-ins.
  • trac.core.ExtensionPoint (funció) quan volem que un component sigui extés usarà aquesta funció per recuperar les implementacions del contracte. Deifineix els punts de hook que té el nostre codi.
  • trac.core.implements (funció) quan un component usa aquesta funció és per implementar un contracte, o sigui, que els plug-ins que es construeixin l’han d’usar.

Abans de seguir explicaré que s’enten per contracte. Un contracte és en escència una classe de tipus interficie (python no té aquest model com a tal) que defineix quins mètodes (o altres classes) poden ser extesos dins el component original. O sigui, que cal no només definir quin és el contracte que s’ofereix sinó també documentar-lo el millor possible, explicant quines són les entrades i sortides que s’esperen de cada un dels mètodes/classes.

Perquè tot plegat s’entengui millor el Pau em va posar el següent exemple:

Imaginem que tenim una classe del tipus DNI que implementa una base de dades de DNIs, on té un metode que ens permet entrar DNIs a la base de dades:

class DNI(trac.core.Component):
  dni_checks = trac.core.ExtensionPoint(IDNIInput)
  def __init__(self):
     self._dnis = []
  def add(self, dni):
     assert not dni in self._dnis, "DNI ja existeix"for dni_check in self.dni_checks:
     if dni_check.check(dni) is False:
       print "El dni %s  sembla no ser correcte" % ( dni )
       return
    self._dnis.append(dni)
  def llista(self):
    print self._dnis

Cal fixar-se que la línia:

dni_checks = trac.core.ExtensionPoint(IDNIInput)

el que fa és carregar els plug-ins que extenen la funcionalitat del codi original. A més cal que ens fixem que el paràmetre que usa la funció és la classe que defineix el contracte sobre el que es fan els plug-ins:

class IDNIInput(trac.core.Interface):
  def check(dni):
    """ Es cridada cada cop que s'entra un nou dni, espera que es retorni un valor boleà"""

Com es pot veure el contracte només defineix un mètode: ‘check’ que ha de tenir un paràmetre d’entrada i espera un valor boleà de sortida.

Cal fixar-se en que la classe DNI cada vegada que afegeixi un element a la base de dades (en aquest cas una simple llista), cridarà a tots els plug-ins que compleixin el contracte per l’ordre en que s’han instanciat (s’han importat al codi original) mitjançant el següent codi.

for dni_check in self.dni_checks:
  if dni_check.check(dni) is False:
    print "El dni %s  sembla no ser correcte" % ( dni )
    return

Un exemple de plug-in sobre el codi anterior i que compleix el contracte especificat podria ser aquest:

import trac.core
import dni
class ValidDNI(trac.core.Component):
  trac.core.implements(dni.IDNIInput)
  def check(self, dni):
    if type(dni) is not type("str"):
      return False
    if len(dni) != 9:
      return False
    return True

Es pot veure com la classe és una instància de ‘trac.core.Component’ (model Singleton) i implementa la interfice ‘dni.IDNIInput’. A nivell funcional el que es fa és ben simple, comprovem que sigui una cadena de texte i que tingui una mida de 9 caràcters, si això es dona retorna un ‘True’ o sinó un ‘False’.

Un exemple de com quedaria el codi principal seria:

import trac.core                                                           

class IDNIInput(trac.core.Interface):
  def check(nom):
    """ Es cridada cada cop que s'entra un nou dni"""
class DNI(trac.core.Component):
  dni_checks = trac.core.ExtensionPoint(IDNIInput)
  def __init__(self):
    self._dnis = []
  def add(self, dni):
    assert not dni in self._dnis, "DNI ja existeix"
    for dni_check in self.dni_checks:
      if dni_check.check(dni) is False:
        print "El dni %s  sembla no ser correcte" % ( dni )
        return
    self._dnis.append(dni)
  def llista(self):
    print self._dnis

Exemples d’ús:

>>> import trac.core
>>> from dni import DNI
>>>
>>> dni_bd = DNI(comp_mgr)
>>> dni_bd.add("38135009C")
>>> dni_bd.add("38135009")
>>> dni_bd.llista()
['38135009C', '38135009']

# importem el plug-in check_dni
>>> import check_dni
>>> dni_bd.add("11111111")
El dni 11111111  sembla no ser correcte
>>> dni_bd.add("11111111A")
>>> dni_bd.llista()
['38135009C', '38135009', '11111111A']

Definim el codi ‘log_dni’ que serà un altre plug-in:

import trac.core
import dni
class LogDNI(trac.core.Component):
  trac.core.implements(dni.IDNIInput)
  def check(self, dni):
    print "Nou dni entrat %s" % (dni)

Seguim amb l’exemple anterior:

# importem ara el plug-in 'log_dni'
>>> import log_dni
>>> dni_bd.add("22222222B")
Nou dni entrat 22222222B

A aquestes altures ja s’han carregat dos plug-ins que treballen un després de l’altre i s’ha pogut apreciar la simplicitat i potència del model. Obviament es poden trobar coses a faltar com per exemple algún element que defineixi el llistat de plug-ins disponibles i que permeti alterar l’ordre en que aquests s’executen però això ja s’hauria de desenvolupar a part.

Espero haver-ho descrit de forma entenedora i sent el més fidel possible a les explicacions del Pau, al que he d’agrair-li l’esforç i dedicació per explicar-me aquest model de Plug-ins de Python.

Aug 06

pkg-config: controlant les llibreries que tenim instal·lades

Reading time: < 1 minute pkg-config és una eina que ens ajuda quan hem de compilar aplicacions i llibreries. Permet descobrir les opcions que s’han d’afegir al compilador.

Alguns exemples ràpids de coses que podem fer:

# descobrir llista de llibreries que tenim instal·lades:
pkg-config --list-all
# cflags per una lliberia concreta
pkg-config nom_llibreria --cflags
# llibreries a incloure al compilador per una llibreria concreta
pkg-config nom_llibreria --libs
Jul 30

cookbook: python logging

Reading time: 2 – 3 minutes

Sovint faig molts scripts de sistema usant python com a llenguatge de programació, doncs bé, sobretot quan aquests scripts s’han de llençar usant el ‘crontab’ va molt bé tenir un bon ‘log’ per saber com van les coses. Així doncs, la setmana passada vaig decidir posar-me a fons amb el tema ‘logging’ de python i ara la forma de fer ‘logs’ que uso per defecte en els meus scripts és:

Snippet de codi python que poso als scripts:

import  logging,logging.config
# logging
logging.config.fileConfig('script_logging.ini')
#logger = logging.getLogger('errorpantalla')
logger = logging.getLogger('errorfitxer')
logger.setLevel(logging.INFO)

Després d’importar la llibreria de ‘logging’ es recuperar el fitxer de configuració on estan definits els paràmetres de ‘logging’.  Un cop carregat, descomento una o les dues línies que hi ha a continuació en funció de si vull logs per pantalla o només contra un fitxer. La última línia només indica fins a quin nivell de depuració he de fer mostrar logs.

El fitxer de configuració:

[loggers]
keys=root,errorpantalla,errorfitxer

[handlers]
keys=fitxer,pantalla

[formatters]
keys=form01

[logger_root]
level=DEBUG
propagate=1
channel=
parent=
qualname=(root)
handlers=

[logger_errorpantalla]
level=DEBUG
propagate=1
channel=errorpantalla
parent=(root)
qualname=errorpantalla
handlers=pantalla

[logger_errorfitxer]
level=DEBUG
propagate=1
channel=errorfitxer
parent=(root)
qualname=errorfitxer
handlers=fitxer

[handler_fitxer]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=form01
filename=paht_to_log_file/log_file
mode=a
maxsize=0
backcount=1
args=('paht_to_log_file/log_file', 'a', 0, 1)

[handler_pantalla]
class=StreamHandler
level=DEBUG
formatter=form01
stream=sys.stderr
args=(sys.stderr,)

[formatter_form01]
format=%(asctime)s %(levelname)s %(message)s
datefmt=

A més vaig trobar un petit script, que carrega una GUI per generar les configuracions dels fitxers de configuració dels ‘logs’, realment útil i simple d’usar:

logging configurator for python

Aquesta GUI l’he trobada al paquet: logging-0.4.9.6.tar.gz concretament al directori ‘tests’. Si no voleu buscar tant també podeu descarregar-lo directament: logconf.py.

Jul 26

Conceptes de Clutter

Reading time: 6 – 9 minutes

Des de fa uns mesos m’he posat a fons amb Clutter, es tracta d’una API més una ABI programada en C per crear interficies d’usuari. Malgrat tractar-se en escència d’una API per crear espais 3D amb objectes 2D, té la capacitat de poder moure els objectes en la coordenada Z. Així doncs, és pot aprofitar la potència d’OpenGL de forma transparent i senzilla i sense haver-se de preocupar de com representar objectes 2D en espais 3D, cosa gens senzilla per un neofit en el món dels gràfics com jo.

Clutter usa el seu propi reactor d’events, però en certs casos pot usar Gobject, Glib i Gtk també. És fàcilment integrable amb DBUS i amb GStreamer. En escència m’ofereix tot el que em cal per un projecte que porto entre mans. Pels que encara no ho tingueu clar, l’interficie gràfica que usa Moblin esta programada amb Clutter com a llibreria gràfica. De fet, és aquest projecte el que li ha donat molta força a Clutter que disposa de 29 programadors a temps complet, dels quals 14 no són d’intel (intel és qui promociona amb més força ‘moblin’).

Bé doncs, aquest impuls que ha patit moblin l’han portat a la versió 0.9.8 que és realment potent i ja gairebé igual que la versió 1.0 que hauria de sortir en breu. Tot i que fins ara Clutter s’ha caracteritzat pel retard en la sortida de les noves versions esperem que aquest cop no sigui així.

Abans d’entrar a definir quins són els elements que té aquesta llibreria, comentar que també disposa de binding de python cosa que pels ‘no-programadors’, com jo, és tota una alegria. També s’ha de dir que a dia d’avui, no hi ha bindings oficials per la branca 0.9 de Clutter que és l’experimental, o futura 1.0. Així doncs, jo només he provat el Clutter fins a la seva versió 0.8, malgrat la segueixo amb lupa la versió 0.9.x; a l’espera de poder començar a fer coses amb ella quan tingui els bindings de python.

Anem al motiu central de l’article, repassar els conceptes que usa Clutter fins a la verió 0.8:

  • Stages: una aplicació de Clutter conté almenys un ‘stage’, aquests contenen actors que són: imatges, rectangles, textos, etc. Un ‘stage’ es comporta de forma semblant a un ‘canvas’ (tapís).
  • Stage Widget: Podem contenir un ‘stage’ dintre d’un objecte finestre de GTK+. En aquests casos es pot usar GTK com a reactor d’events.
  • Actors:  són formes 2D mostrades en un espai 3D. Aquestes formes poden ser; per exemple, formes geomètriques, imatges, textos, etc. Si el que cal és tenir actors tridimencionals en aquest espai el que caldrà és instanciar directament l’API d’OpenGL.  Per mostrar un actor en un ‘stage’ cal fer-ho a través d’un ‘container’.
  • Transformations: es refereix a les transformacions que li podem fer a un ‘actor’ al mostrar-lo:
    • Scaling: aumentar o disminuir la seva mida aparent, no la real.
    • Rotation: es pot rotar sobre els seus eixos X, Y i Z.
    • Clipping: fixar l’objecte sobre el ‘canvas’ això ens permet per exemple, crear una zona d’scrolling al seu intererior.
    • Movement: desplaçar les coordenades de posició de l’objecte.
  • Containers: en si mateix és un tipus especial d’actor, compost per altres actors fills que es posicionen en l’espai respecte la posició del seu contenedor. De fet, si ens hi fixem el propi ‘stage’ és un ‘actor’ de tipus ‘container’. Escencialment hi ha dos tipus de ‘containers’: ClutterContainer i ClutterGroup.
  • Events: la classe ‘actor’ emet una serie de senyals que podem capturar per enllaçar amb funcions, les senyals són:
    • button-press-event: emès quan l’usuari prem el botó del ratolí sobre l’actor.
    • button-release-event: emès quan l’usuari deixa anar el botó del ratolí sobre l’actor.
    • motion-event: quan el ratolí es mou per sobre l’objecte.
    • enter-event: emès a l’entrar sobre la superficie de l’actor.
    • leave-event: emès al sortir de la superficie de l’actor.
  • Timelines: es poden usar per canviar la posició o aparença d’un actor al llarg del temps. Les línies de temps es poden usar soles o amb combinació dels ‘effects’ i els ‘behaviours’. Per cada ‘frame’ que s’ha de dibuixar en el temps s’emet una senyal anomenada ‘new-frame‘, obviament la podem connectar a alguna funció. Al crear una línia de temps hem d’espificar dos paràmetres: la quantitat total de ‘frames’ que tindrà i els ‘frames per segon’ a la que es reproduirà.
  • Score: podem agrupar diverses ‘timelines’ en un ‘score’, això ens permet posar en marxa o parar diverses ‘timelines’ a la vegada.
  • Effects: són una serie de funcions que podem aplicar sobre els actors usant una ‘timeline’ amb l’objectiu de canviar les propietats al llarg del temps, usant un simple càlcul numèric. Sovint aquesta és la forma més simple de crear una animació. És important recordar que els efectes només poden afectar a un actor en una ‘timeline’ i no podem canviar els efectes al llarg del temps, per fer això cal fer-ho amb un ‘behaviour’.
  • Behaviours: tenen la capacitat de canviar una propietat específica d’un actor al llarg del temps aplicant un simple càlcul numèric. A diferència dels ‘effects’ amb els ‘behaviours’ podem controlar més d’un actor a la vegada i canviar els paràmetres dels càlculs que es fan al llarg de la ‘timeline’. Un exemple ben senzill d’aplicació d’això seria que podem fer que la funció que es crides al llarg del temps detecti que s’ha acabat l’efecte aplicat i el faci tornar a començar automàticament, simplement canviant el paràmetre de l’efecte que s’esta aplicant. Els ‘behaviours’ que té Clutter per defecte són:
    • ClutterBehaviourBspline: mou l’actor a través una línia ‘bezier‘.
    • ClutterBehaviourDepth: mou un actor a l’eix Z.
    • ClutterBehaviourEllipse: mou un actor al llarg d’una el·lipse.
    • ClutterBehaviourOpacity: canvia l’opacitat d’un actor.
    • ClutterBehaviourPath: mou un actor al llarg d’un camí definit per una serie de punts.
    • ClutterBehaviourRotate: rotar un actor.
    • ClutterBehaviourScale: canvia la mida aparent d’un actor.

Per fer aquesta referència m’he basat amb la informació de la guia ‘Programming with Clutter‘ escrita per Murray Cumming.