Packaging d’application python sous maemo 5

Dans le monde java, le packaging d’application est dicté par les standard java. On fait des JAR, des WAR, des EAR… L’ecosystème étant borné par la JVM et le conteneur (web ou ejb), la question du livrable ne se pose guère. Mais en python sous linux comment fait-on ?

La première idée est de générer un paquet pour la distribution cible. En l’occurence il s’agissait de l’application Zourite pour N900. On s’oriente donc vers la création de paquet debian. La création de paquet peut se faire via l’IDE Esbox. Cependant cela n’est pas si simple et un ignorant en packaging linux-debian n’y arrive pas sans faire un petit tour sur le site de debian pour se rendre compte que la création de paquet s’apparante à la création de distribution et que c’est un rôle distinct de celui de développeur d’application.

Heureusement que python (à ma grande surprise pour un vieux langage) dispose d’un écosystème riche à la java. Python inclut un mecanisme de création de livrable par le biais du fameux fichier setup.py. Un détour par la documentation en ligne et un peu de persévérence permet d’arriver à ses fins.

Le fichier setup.py contient la description du paquet python. Il est conseillé d’avoir au préalable organisé les sources du projet en modules dans un paquet dédié. Cela permettra d’embarquer tous les fichiers dans le paquet (répertoire) d’installation. Le fichier setup.py peut installer également des scripts de lancement du programme python. Ces scripts sont eux-même des script python qui pourront changer le répertoire courant du processus pour se rendre dans le répertoire du paquet de l’application afin que l’execution se passe comme en développement.

Voici un exemple de fichier setup.py:

# -*- encoding: UTF-8 -*-
'''
Created on 30 mars 2010

@author: thierry
'''
from distutils.core import setup
setup(name='zourite',
version='0.1.0',
package_dir = {'': 'src'},
packages=['zourite',
'zourite.common',
'zourite.core',
'zourite.gui',
'zourite.plugin',
'zourite.plugin.gmail',
'zourite.plugin.linkedin'],
scripts=['scripts/zourite','scripts/zourite-setup'],
package_data={'zourite': ['*.png']},
author='Thierry Bressure',
author_email='thierry@bressure.net',
maintainer='Thierry Bressure',
maintainer_email='zourite@bressure.net',
url='//blog.zourite.bressure.net',
download_url='//zourite.bressure.net',
description='Zourite Professional Networking Application for N900',
long_description='Zourite is a client for professional Network like LinkedIn. You can stay connected to your contacts and manage you network.',
classifiers=[
'Development Status :: 1',
'Environment :: Hildon',
'Environment :: Maemo',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Developers',
'License :: OSI Approved :: LGPL',
'Operating System :: Maemo :: Maemo 5',
'Programming Language :: Python',
'Topic :: Communications :: Email',
'Topic :: Office/Business',
],

)

Révision globale subversion dans un projet en python chez googlecode

Il est courant dans un projet d’insérer dans les sources la révision du fichier. Cela est accomplit par le système de versionnage de source par un mécanisme de substitution d emot clé. Par exemple le mot clé Revision balisé par $Revision:$ est automatiquement remplacé par subversion par $Revision: 70$ si la révision du fichier est 70.

Ce mécanisme est très utile si on veut pouvoir raccroché un source à un révision en dehors de l’environnement de développement. Cela est en l’occurence utile quand on souhaite remonté un bug dans un projet en python. Imaginons un projet ecrit en python. Python étant un langage interprété, l’application est distribuée sous forme de fichiers sources .py. Lorsqu’une exception survient, python peut rapporter la pile d’execution constituant un début rapport d’erreur. Mais il faut encore associer cette pile à une version de l’application. Quand l’application est en version finale, on peut inclure dans le livrable la version commerciale de l’application mais il est encore plus facile pour le développeur d’inclure dans le rapport la révision des sources. Et c’est encore plus vrai quand l’application est en phase de développement ou de beta-test.

C’est exactement ce qui m’arriva avec Zourite, une application de réseautage professionnel pour N900. Zourite contient un mécanisme de rapport de bug qui envoie la pile d’erreur au site du développement du projet. Ce dernier étant hébergé par googlecode, je n’ai pas d’accès aux mécansime de hook subversion. Je dois donc me contenter de la substitution de révision qui ne fournit pour un fichier que la plus récente révision où ce fichier a été modifié. Or la pile d’éxecution fait appel à plusieurs fichiers donc afin que le développeur reproduise le bug, il me faut absolument la révision gloable des sources qui ont produit le bug.

Subversion fournit un outil svnversion mais celui-ci doit être executé sur un copie local des sources donc il n’est pas utilisable avec l’application livrée. De plus n’ayant pas accès au serveur subversion de googlecode, il m’est impossible d’envisager utiliser un hook pour automatiser le renseignement de la révision globale du projet.

Mais heureusement python contrairement à Java utilise la notion d’import dans son sens originel c’est-a-dire monter en mémoire un module et non pas comme un simple raccourci de notation comme en Java. Donc on peut se servir des instruction d’import des module python pour que l’application parcours tous les modules la composant. Si chaque module va proposer sa révision à un module centralisant les révisions, on est capable de connaitre la révision globale du projet… à l’éxécution.

La mise en place du mécanisme de substitution est assez aisé. Pour un ajout automatique à un nouveau fichier, suivre les instructions ici pour lesquels nous retiendrons les étapes suivantes:

  1. ouvrir le fichier ~/.subversion/conf
  2. dans la section [miscellany] décommenter la ligne enable-auto-props = yes
  3. dans la section [auto-props] ajouter la ligne *.py = svn:eol-style=native;svn:keywords=Revision

Malheureusement les fichiers qui sont déjà sous subversion ne seront pas affectés. Il faut donc un a un leur rajouter la propriété de substitution. Dans ESBox il faut selectionner le fichier et dans le menu contextuel ouvrir le menu Team > Set property:

Ajout propriété svn à un fichier dans ESBox

Puis selectionner pour property name la propriété svn:keywords et pour property content la valeur Revision

Répéter l’opération pour tous les fichiers où subversion doit appliquer la substituoin par mot clé.

Maintenant nos pouvons ajouter le mot clé $Revision$ dans les sources python. Cette valeur sera reprise par chaque module et soumis à un module centrale qui portera la révision globale.

Voic le code source du module centrale qui lui même renseigne sa révision à lui-même !

# -*- encoding: UTF-8 -*-
'''
Created on 28 mars 2010

@author: thierry
'''

CURRENT_VERSION = "1.0-SNAPSHOT"  # this must be modified before any release

def getInstance():
 return SINGLETON

class Version(object):
 '''
 Hold the revision of the application.
 '''        

 def __init__(self):
 '''
 Constructor
 '''
 self.revision = ""

 def submitRevision(self, rev):
 '''
 submit a new revision. If the revision is upper than
 the current revision, it become the new current revision
 '''

 if self.revision < rev:
 self.revision = rev

 def getRevision(self):
 '''
 Return the scm revision for this application
 '''
 return self.revision

 def getVersion(self):
 '''
 Return the marketing version for this application.
 '''
 return CURRENT_VERSION

SINGLETON = Version()  # object that track the revision of current applcation for issue tracking purpose

getInstance().submitRevision("$Revision: 73 $")

Chaque module doit alors à l’instar du module version lui-même, renseigner sa révision. Ainsi chaque module devra contenir en dernière instruction d’import le code suivant:

import version
version.getInstance().submitRevision("$Revision: 74 $")

Quand on aura besoin de connaitre la révision globale de l’application pour soumettre un bug par exemple, il suffit de faire :

versionInstance = version.getInstance()
versionStr = versionInstance.getVersion()
revisionStr = versionInstance.getRevision()

Révision globale subversion dans un projet en python chez googlecode

Il est courant dans un projet d’insérer dans les sources la révision du fichier. Cela est accomplit par le système de versionnage de source par un mécanisme de substitution d emot clé. Par exemple le mot clé Revision balisé par $Revision:$ est automatiquement remplacé par subversion par $Revision: 70$ si la révision du fichier est 70.

Ce mécanisme est très utile si on veut pouvoir raccroché un source à un révision en dehors de l’environnement de développement. Cela est en l’occurence utile quand on souhaite remonté un bug dans un projet en python. Imaginons un projet ecrit en python. Python étant un langage interprété, l’application est distribuée sous forme de fichiers sources .py. Lorsqu’une exception survient, python peut rapporter la pile d’execution constituant un début rapport d’erreur. Mais il faut encore associer cette pile à une version de l’application. Quand l’application est en version finale, on peut inclure dans le livrable la version commerciale de l’application mais il est encore plus facile pour le développeur d’inclure dans le rapport la révision des sources. Et c’est encore plus vrai quand l’application est en phase de développement ou de beta-test.

C’est exactement ce qui m’arriva avec Zourite, une application de réseautage professionnel pour N900. Zourite contient un mécanisme de rapport de bug qui envoie la pile d’erreur au site du développement du projet. Ce dernier étant hébergé par googlecode, je n’ai pas d’accès aux mécansime de hook subversion. Je dois donc me contenter de la substitution de révision qui ne fournit pour un fichier que la plus récente révision où ce fichier a été modifié. Or la pile d’éxecution fait appel à plusieurs fichiers donc afin que le développeur reproduise le bug, il me faut absolument la révision gloable des sources qui ont produit le bug.

Subversion fournit un outil svnversion mais celui-ci doit être executé sur un copie local des sources donc il n’est pas utilisable avec l’application livrée. De plus n’ayant pas accès au serveur subversion de googlecode, il m’est impossible d’envisager utiliser un hook pour automatiser le renseignement de la révision globale du projet.

Mais heureusement python contrairement à Java utilise la notion d’import dans son sens originel c’est-a-dire monter en mémoire un module et non pas comme un simple raccourci de notation comme en Java. Donc on peut se servir des instruction d’import des module python pour que l’application parcours tous les modules la composant. Si chaque module va proposer sa révision à un module centralisant les révisions, on est capable de connaitre la révision globale du projet… à l’éxécution.

La mise en place du mécanisme de substitution est assez aisé. Pour un ajout automatique à un nouveau fichier, suivre les instructions ici pour lesquels nous retiendrons les étapes suivantes:

  1. ouvrir le fichier ~/.subversion/conf
  2. dans la section [miscellany] décommenter la ligne enable-auto-props = yes
  3. dans la section [auto-props] ajouter la ligne *.py = svn:eol-style=native;svn:keywords=Revision

Malheureusement les fichiers qui sont déjà sous subversion ne seront pas affectés. Il faut donc un a un leur rajouter la propriété de substitution. Dans ESBox il faut selectionner le fichier et dans le menu contextuel ouvrir le menu Team > Set property:

Ajout propriété svn à un fichier dans ESBox

Puis selectionner pour property name la propriété svn:keywords et pour property content la valeur Revision

Répéter l’opération pour tous les fichiers où subversion doit appliquer la substituoin par mot clé.

Maintenant nos pouvons ajouter le mot clé $Revision$ dans les sources python. Cette valeur sera reprise par chaque module et soumis à un module centrale qui portera la révision globale.

Voic le code source du module centrale qui lui même renseigne sa révision à lui-même !

# -*- encoding: UTF-8 -*-
'''
Created on 28 mars 2010

@author: thierry
'''

CURRENT_VERSION = "1.0-SNAPSHOT"  # this must be modified before any release

def getInstance():
 return SINGLETON

class Version(object):
 '''
 Hold the revision of the application.
 '''        

 def __init__(self):
 '''
 Constructor
 '''
 self.revision = ""

 def submitRevision(self, rev):
 '''
 submit a new revision. If the revision is upper than
 the current revision, it become the new current revision
 '''

 if self.revision < rev:
 self.revision = rev

 def getRevision(self):
 '''
 Return the scm revision for this application
 '''
 return self.revision

 def getVersion(self):
 '''
 Return the marketing version for this application.
 '''
 return CURRENT_VERSION

SINGLETON = Version()  # object that track the revision of current applcation for issue tracking purpose

getInstance().submitRevision("$Revision: 73 $")

Chaque module doit alors à l’instar du module version lui-même, renseigner sa révision. Ainsi chaque module devra contenir en dernière instruction d’import le code suivant:

import version
version.getInstance().submitRevision("$Revision: 74 $")

Quand on aura besoin de connaitre la révision globale de l’application pour soumettre un bug par exemple, il suffit de faire :

versionInstance = version.getInstance()
versionStr = versionInstance.getVersion()
revisionStr = versionInstance.getRevision()

Sauvegarde des documents N900 sur google doc

Google propose depuis peu de stocker tout type de document sur son service Google Docs. Si la capacité est limitée à 1 Go dans sa version gratuite, pour 5 $ on passe à 20 Go. Cette capacité est alors assez confortable pour stocker les documents et média enregistrés avec la caméra du terminal. Cependanr pouquoi payer alors qu’un compte gmail offre déjà un quota de 7 Go? Parce que gmail a une limite de taille de pièce jointe à 20 Mo alors que google docs pousse la taille max des documents à 250 Mo.

Une recherche sur… Google nous montre qu’une solutions existe pour le N900 en couplant 2 logiciels: Petrovich et Pixelpipe. Mais l’idéal serait un mécanisme automatisée. Autrefois je m’aurais tourné vers un développement Java mais le temps est au Python. Cela tombe bien puisque google fournit une implémentation Python de son API gdata.

Un premier parcours de cette API montre qu’elle permet d’accéder à presque tous les services de Google depuis la base de contacts jusqu’à Youtube. Cela ouvre des perspectives pour les développeurs.

En revenant sur la problématique de sauvegarde, on peut imaginer écrire un script python qui prend les fichiers contenus dans les réepertoires DCIM des partitions pour les mettre dans google docs. Il faudrait prévoir la création d’un répertoires N900 dans google docs afin de bien identifier ces fichiers.

Pour finir ce billet, je dirai que même si mon propos est orienté vers le N900, un tel scripte python marche aussi sur tout autre de bureau.

3 mois dans le panier de python

J’ai commencé à développer en python par nécessité. En effet il y a 3 mois, j’avais le choix entre le C et le Python pour développer pour le N900. Dans les 2 cas les outils existent au travers de l’IDE Esbox. Bien que le C soit officiellement le langage consacré par Nokia pour sa plateforme Maemo, cette dernière bénéficie d’une communauté suffisamment active pour proposer un portage de python avec les binding GTK qui vont bien. J’ai choisi Python car c’est la solution la plus facile pour un développeur java.

Cependant il y a certains aspects à prendre en compte rapidement surtout quand on est habitué à Java.

  1. langage de scripte donc interprété donc erreurs de link visibles qu’au moment de l’execution
  2. pas de typage des variable et attributs
  3. adhérence au c et bibliothèques système
  4. pas d’interface mais héritage multiple

Ces précisions ont leur avantages et leurs inconvénients. Certains obligent à plus de discipline dans l’organisation du code comme le manque d’interface alors que d’autres offrent une implémentation directes de décorateurs grâce au pointeur de fonction et à la forme lambda.

J’apprécie de plus en plus ce langage pour sa productivité et la rapidité du code à l’execution.

Retour dans la matrice

Après 6 mois d’auto-hébergement pour les raisons exposées précédement, ma voilà contraint de revenir à un hébergement externalisé, la faute à un crash disque! Et comme si la loi de Murphy avait encore besoin de se démontrer, ma machine de secour a aussi rendu l’âme. Pas de doute, l’infrastructure d’un particulier ne vaut pas celle d’un hébergeur: c’est un vrai métier.

Cette mauvaise expérience m’a montrer qu’il vaut mieux faire confiance à google ou tout autre service d’hébergement mutualisé. En effet, j’ai pu récupérer certaines de mes données en réactivant mes blogs sur blogger et wordpress. Mais ce qui me chagrinait le plus était le contre-temps que cela allait avoir sur le développement de Zourite dont je venais de perdre le repository parti en fumée dans mon crash disque. Et là je dois dire “merci google” grâce auquel je pu rapidement continuer à travailler en utilisant son service d’hébergement de projet google code. Ainsi Zouite a-elle trouvé une nouvelle maison //zourite.bressure.net.

Ce fut un mal pour un bien. Il me reste maintenant à automatiser un système de sauvegarde de mes données externalisées. De quoi écrire quelque billets…