When UnicodeDecodeError become irrational check $LANG


I spent hours this week trying to understand how an installation script can fail on some installations.

In input we have an utf-8 encoded file and we add some xml files, also ‘utf-8’ encoded. These are parsed with Markdown.

python -m lom2mlr.markdown -l -c rationale.md

It is really simple but sometimes we ran into a strange error:

Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/home/edegoute/Projects/lom2mlr/lom2mlr/lom2mlr/markdown/__main__.py", line 3, in <module>
compile()
File "lom2mlr/markdown/__init__.py", line 55, in compile
extensions=extensions)
File "/home/edegoute/Projects/lom2mlr/local/lib/python2.7/site-packages/markdown/__init__.py", line 529, in markdownFromFile
kwargs.get('encoding', None))
File "/home/edegoute/Projects/lom2mlr/local/lib/python2.7/site-packages/markdown/__init__.py", line 441, in convertFile
html = self.convert(text)
File "/home/edegoute/Projects/lom2mlr/local/lib/python2.7/site-packages/markdown/__init__.py", line 375, in convert
newRoot = treeprocessor.run(root)
File "lom2mlr/markdown/test_mlr.py", line 76, in run
print(" " * int(element.tag[1]) + element.text)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xc9' in position 4: ordinal not in range(128)

First, it was difficult to understand how an unicode error can raise an iso-8859-1 problem on utf-8 files. Going deep I found some known problems with ‘codecs.open’ in python2.7 but no solution. I tried to force Markown to treat these files as ‘iso-8859-1’ files, then it ran an utf-8 unicode error at the same line not in the opening. It was sounding too many magic for me.

At that point, I checked again the installation was identical: same python version, same pip version, same eggs versions. I tried some egg upgrade without any success. And finally came the idea to check environment variables. Bingo! On all systems with failing installations we have no localization ($LANG=C). The fix was so simple:

export LANG=en_US.UTF-8

That’s it!

I still don’t understand the magic in the codecs python module. Why it computes a different encoding when the function call already asks for one? The workaround is simple for programmers.

Publicités

Un nouveau salon de discussion francophone pour discuter autour de Python


C’est tout neuf : la communauté Python francophone a un nouveau point de chute pour discuter de tout et de rien.

http://www.reddit.com/r/PythonFr/

N’hésitez pas à vous inscrire !

2010 : liens de la semaine 6


Un seul lien cette semaine, l’article hedbomadaire que je co-écrit pour l’AFPY : Python WAW.
Je viens d’être élu président de l’Association Lyonnaise pour le Développement de l’Informatique  Libre (ALDIL) ce qui va m’obliger à réduire ma participation dans beaucoup d’autres choses, comme cette rubrique.
Ce blog va aussi continuer son ouverture sur le logiciel libre en général pour mieux correspondre à mes activités.

2010 : liens de la semaine 5



Python

Cette semaine Logilab hébergeait un sprint sur Mercurial et une partie de l’AFPY était en déplacement au FOSDEM. La semaine prochaine devrait être riche en retours d’expériences.

L’article de la semaine de l’AFPY :

Python WAW 2010 : Semaine 5

PostgreSQL

PostgreSQL 9.0 introduit la réplication à chaud. C’est une très bonne nouvelle pour ce concurrent d’Oracle :

Hot Standby et Streaming Replication

Web

Vous avez besoin d’images génériques pour tester vos sites ? Ce site reprend le principe du Lorem Ipsum avec les images en autorisant à paramétrer la taille directement dans les urls :

Dummy Image Generator Is the Lorem Ipsum of Images

Un bon article les tests fonctionnels en JQuery :

How to Test your JavaScript Code with QUnit

Pour ceux qui veulent savoir à quoi sert CSS3, voici une série de démos sur ses capacités :

47 Amazing CSS3 Animation Demos

2010 : les liens de la semaine 4


Cette  rubrique va sans doute devenir mensuel suite à la fusion de la partie Python avec la rubrique de l’AFPY. Au programme de cette semaine : Les grammaires en  Python, quelques liens utiles et beaucoup de lien autour des frameworks Web :

Python WAW 2010 : Semaine 4

Une question qui revient souvent : mais où allons-nous héberger notre projet ?

Une page wikipedia a été créé dans le but de recenser les plateformes d’hébergement de projets qu’elles soient gratuites ou payantes :

Comparison of open source software hosting facilities

Enfin un article qui montre qu’il est possible d’écrire un sélectionneur de thème en 200 secondes en jQuery :

Quick Tip: How to Create a Theme-Switcher in 200 Seconds

Installer et tester Django en 10 minutes sur une Ubuntu 9.10


J’ai trouvé une petite application web qui m’intéresse pour le site de l’AFPY : un éditeur de restructured text écrit pour Django. N’ayant jamais utilisé Django j’ai essayé de trouver une documentation pour installer et tester tout ça avec Ubuntu. Une recherche sur le moteur de recherche Exalead m’a permis de trouver la page de référence :

Django on Apache with mod_python

Seulement une page de texte pour une installation réussie… ou presque. Cette page a été mise à jour avant la sortie de la dernière mouture d’Ubuntu et quelques réglages ne fonctionnent plus.

Installation

Le module contenant les exemples a été déplacé dans python-django-doc :

sudo apt-get install libapache2-mod-python python-django python-django-doc
sudo vim /etc/apache2/sites-available/django-example

Configuration

Dans ce fichier vierge copier le texte suivant :

<Location "/django">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    PythonDebug On
    PythonPath "['/usr/share/doc/python-django-doc'] + sys.path"
    SetEnv DJANGO_SETTINGS_MODULE examples.settings
</Location>

Personnalisation

Maintenant qu’Apache est configuré il faut modifier le fichier examples.urls pour le pas polluer la page d’accueil d’apache.

sudo vim /usr/share/doc/python-django-doc/examples/urls.py

En rajoutant django/ au début des expressions de correspondances nous n’interceptons plus la page d’accueil d’Apache :

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^django/$', 'examples.views.index'),
    (r'^django/hello/', include('examples.hello.urls')),
)

Il ne reste plus qu’à activer notre configuration à la mode debian :

sudo a2ensite django-example
sudo /etc/init.d/apache2 reload

Votre site de test Django est disponible et tout cela ne vous a pas pris 10 minutes : http://localhost/django

Voilà qui est fait… il ne me reste plus qu’à regarder de plus près comment fonctionne Django. 🙂

2010 : liens de la semaine 3


Cette rubrique hebdomadaire va bientôt changer. Pour éviter les doublons elle va être fusionné avec celle de Youenn Boussard sur le site de l’AFPY. Elle continuera sur le signe de l’ouverture avec des sujets moins centré sur Python ou avec des ressources plus techniques que celles de l’AFPY.

Heuristique

Les cartes heuristiques ou mindmapping sont au centre de recherche diverses et variées. Voici un article intéressant sur un débordement de cette technologie :

Carte heuristique et résolution de problème assistée par ordinateur, une révolution annoncée

Python

Tout d’abord une initiative amusante avec un site qui essaye de regrouper tous les logos utilisés autour de Python :

Python Logos Part II: Free Form

Une dédicace particulière pour le logo des lillois : PYthon LUnch LillE

Sinon, j’ai découvert ce petit éditeur live de reStructured Text écrit comme un service Python. Un projet à suivre et à aider :  reStructuredText editor

Zope

L’heure est au recensement des ressources avec le renommage de la partie Web du framework Zope 3 en Bluebream. cet article écrit par Baiju Muthukadan permet de faire la synthèse  sur l’état actuel :

BlueBream

Plone 4 aussi est annoncé dans les starting blocks avec la préparation de la première béta. Une nouvelle version implique souvent une migration. Maarten Kleen a écrit un article très intéressant sur la migration d’objets dont la classe de base a changé :

Trying zodbupdate

Enfin un article intéressant sur la résolution de la recherche des interfaces dans Zope 3 :

Zope Views: should have been « request, context »; not « context, request »

Plone

La huitième conférence annuel de Plone aura lieu en Europe, à Bristol, du25 au 31 octobre. À vos carnets :

Bristol 2010

Nettuts+

Un certain nombre de tutoriel que je présente viennent de ce site. Une grande partie de leur contenu est payant et les tutoriels mis en ligne sont souvent de très bonne qualité. Tout d’abord une video sur les outils en lignes permettant de tester le rendu de vos sites sur les navigateurs :

Quick Tip: Top 5 Ways to Browser-Test your Website

Un bon tutoriel sur l’utilisation des transformation XSL pour transformer des données XML enpage web :

Getting Started with XSL(T)

2010 : liens de la semaine 1


Cette semaine a été très productive du côté des blogueurs et en particuliers chez les développement autour du core Python.

Python

Pour ceux qui ne connaissent cette boîte de Pandore qu’est le GIL (Global Interpretor Lock) en Python voici un très bon article qui mesure son effet dans le traitement des entrées/sorties avec des systèmes multi-processeurs ou multi-cores :

The Python GIL Visualized

Jesse Noller rappelle les but initiaux du projet Unladen swallow en partie sponsorisé par Google et signale malicieusement en fin d’article qu’une proposition de fusionner leur travail dans Python Core pour Python 3 :

UNLADEN SWALLOW: PYTHON 3’S BEST FEATURE.

Tarek Ziadé, notre président bien aimé, présente les nouvelles fonctionnalités en cours de discussion pour la prochaine version majeure de Distutils, ce qui intéressera tout ceux qui publient des eggs :

Possible new features for Distutils 2.7

Mark Mruss fait une présentation détaillé des ‘docstring’ – ces commentaires qui décorent vos modules, vos classes et vos fonctions – et montre quelle utilisation avancée de celles-ci peut être faite (documentation, tests) :

Introducing Docstrings

Encore un article sur la préparation de la distribution de code qui montre comment mettre en place facilement des scripts  de pré-traitement et de post-traitement :

Using setuptools entry points

La première revue complète du livre « Dive Into Python 3 » (Au cœur de Python 3) :

Book Review: Dive Into Python 3

Enfin deux articles sur les fonctionnalités que les gestionnaires de projets veulent dans mercurial. Dans le premier cas il s’agit de Python-dev qui est censé être migré entièrement cette année sous mercurial :

Where the Hg transition stands

Le deuxième est un développeur qui fait pas mal de Django (vous aimerez sans doute aussi son article free VS free) :

DVCS Ponies

Plone

Voici la description d’un problème récurrent pour les sites multilignues en Plone : comment réordonner des éléments dans une page :

Ordering objects in folders with multilingual sites

Une des solutions les plus faciles à mettre en place est d’afficher toutes les langues lors du tri pour être sur que le Javascript se comporte correctement. L’autre est de demander au Javascript de ne prendre en compte les rangs réels des contenus déplacés plutôt que le rang à l’affichage.

Alex Clarck montre comment installer Plone juste avec buildout en 5 minutes, sans avoir à installer l’unified installer ou Paster :

No, really, you can (just) use Buildout to install Plone

WEB

Encore une fois je terminerai par des liens plus centrés sur le développement Web en général et jQuery en particulier.

Réaliser des animations en jQuery en 7 étapes :

jQuery Animations: A 7-Step Program

Un visualisateur de contenu (d’images) à l’épreuve des balles :

A Bullet-Proof Content Viewer

Personnaliser les événements et l’API des Événements Spéciaux dans jQuery :

Custom Events, and the Special Events API in jQuery

Week 53 web logs


Python

Tarek Ziadé a écrit le plus utile des tutoriels : How to version your Python projects. Il décrit en quelques pages les bonnes pratiques pratiquées dans la communauté pour géré la numéroration des distributions et comment mettre en place rapidement  le versionnage depuis un environnement de développement mercurial. Ce dernier point jette un pavé de plus pour l’utilisation de ce DRCS qui devrait devenir le standard de la communauté ces prochaines années.

Paul Wilson et Chris McDonough ont cherché un moyen d’implémenter les fonctions génériques chères à Guido Van Rossum. Le résultat de leur recherches a été publié dans la liste de diffusion Zope-dev : Zope3 and Generic Functions

Dans un cycle la fin et le début se confondent

Cette année se termine et ce blog va se franciser de plus en plus pour améliorer la visibilité des communautés françaises des utilisateurs de Python, Zope et Plone.

Tester les développements XML en Python


Présentation du problème

Ces derniers temps je travail beaucoup sur la génération de fichiers XML à partir de données éparses suivants des contraintes externes qui sont impossibles à vérifier manuellement. Pour ce travail je ne peux m’accrocher qu’au schema XML que nous a transmis le fournisseur du service avec une documentation lacunaire. Chaque fichier généré fait entre 700 et 1000 lignes et la schéma pèse plus de 5Mo.

Le problème est que la plupart des outils python permettant d’écrire des fichiers XML de manière aisée ne comporte pas de validateur et ceux qui en contiennent un sont un vrai casse-tête pour l’écriture des fichiers. Après quelques tests j’ai décidé de ne pas utiliser les mêmes modules pour les tests et pour l’écriture : lxml pour le premier et elementree pour le second.

Mise en place des tests

Pour cette extension j’ai utilisé des doctests pour documenter le module qui est un élément à part dans l’application que nous mettons en place. Cela me permettait d’être moins dépendant de l’environnement qui évoluait énormément autour du module.

Je me suis appuyé sur une très bonne documentation de lxml publiée chez codespeak pour bâtir mes tests : Validation with lxml

Ce qu est intéressant dans cette bibliothèque est qu’elle supporte un grand nombre de type de schéma XML pour la validation :

  • DTD
  • RelaxNG
  • XMLSchema
  • Schematron

Lorsqu’il n’y a qu’un fichier à traiter il est possible de le tester directement au chargement ce qui est assez coûteux en mémoire. Lorsqu’il y a plusieurs fichiers à tester il faut d’abord charger le schéma puis tester les fichiers un par un. Là encore il est possible de ne tester que la validité globale du fichier ou d’avoir une sortie plus verbeuse.

    >>> from lxml import etree
    >>> from messagefactory import Message

Création d'un message

    >>> xml_message1 = Message()
    >>> xml_message1.initialize()
    >>> xml_message1.fillMessage()
    >>> xml_message1.finish()

Chargement du schéma
Tout chargment avec etree.parse() doit être fait avec un descripteur
de fichier ouvert ou un tampon StringIO.

    >>> f = open('../tests/output_schema.xsd')
    >>> xmlschema_file = etree.parse(f)
    >>> xmlschema = etree.XMLSchema(xmlschema_file)
    >>> f.close()

Les messages peuvent être ensuite testés à la volée.
Nos messages ne comportent pas de saut de ligne à la fin de chaque 
balise ce qui rend la lecture humaine difficile et la diagnostique 
verbeux inutilisable.

    >>> xml_message1 = StringIO(test_message1.getRawMessage().replace'><', '>\\n<'))
    >>> xml_file1 = etree.parse(xml_message1)

La méthode assertValid(xml_data) retourne une sortie verbeuse
si les données ne sont pas conformes.

    >>> xmlschema.assertValid(xml_file1)

La méthode validate(xml_data) se contente de renvoyer True ou False.

    >>> xmlschema.validate(xml_file1)
    True

Écriture des fichier XML

Le module elementtree possède une petite API extensible pour écrire des fichier XML. Elle était largement suffisante pour mes besoins qui consistaient à mettre bouts à bouts des données éparses suivant un modèle connu.

Le module elementtree.SimpleXMLWriter

L’objet XMLWriter possède les mêmes contraintes que celles vu dans les tests avec lxml : il ne travaille qu’avec un tampon (StringIO) ou un descripteur de fichier. Cela impose de différencier 2 mode de rendu : le mode RAW avec le texte brute et le le mode natif qui dans mon cas renvoi un StringIO. Il travaille comme en mode ligne : une fois qu’il a écrit un élément il ne peut pas revenir en arrière. Il n’est pas possible de faire une modification après coup du texte produit.
L’API permet de créer des bloc de 2 manière la méthode element et le couple start/end.
La méthode element permet de créer une balise et son contenu directement. elle prend comme argument :

  1. tag : le nom du tag à créer (sensible à la casse)
  2. text : une chaîne de caractère qui va être utilisée comme contenu de la balise
  3. attrib : un dictionnaire d’attribut à rajouter dans la balise

C’est pratique pour écrire une feuille ou un bloc venant d’une source externe.

Le couple de méthodes start/end ne font que créer des balises sans contenu et sans vérifier la cohérence des ouvertures/fermetures des balises (d’où l’importance d’une validation). La méthode start possède les même arguments que la méthode element en dehors de l’argument text. La méthode end n’accepte que l’argument tag.

Code du module messagefactory

Le code a été volontairement simplifié et ne met pas en exergue la complexité de création d’une arborescence complète de nœuds avec des répétitions. Je qualifierai cette implémentation de naïve.

from StringIO import StringIO
from elementtree.SimpleXMLWriter import XMLWriter

class Message(object):
    _file = None
    _writer = None

    #----------------------------------------------------------------------
    def __init__(self, encoding='ISO-8859-1'):
         """"""
         self._file = StringIO()
         self._writer = XMLWriter(self._file, encoding)

    #----------------------------------------------------------------------
    def getRawMessage(self):
         """Return message as a string"""
         self._file.flush()
         return self._file.getvalue()

    #----------------------------------------------------------------------
    def getMessage(self):
         """Return message as a StringIO object"""
         self._file.flush()
         return self._file

    #----------------------------------------------------------------------
    def initialize(self):
        """Create the enveloppe"""
        self._writer.declaration()

        self._writer.start('Envelope',
                            attrib={'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
                                    'xsi:noNamespaceSchemaLocation': 'output_schema.xsd',
                             },
        )

        self._writer.flush()

    #----------------------------------------------------------------------
    def finish(self):
        """Close the enveloppe"""
        self._writer.end('Envelope')
        self._writer.flush()
        self._writer.close()

    #----------------------------------------------------------------------
    def fillMessage(self):
        """ Create message content
        """
        self._writer.start('Message')

        # Delivery block
        self._writer.start('delivery')

        self._writer.start('to')
        self._writer.element('address', text=self.agencyCode)
        self._writer.end('to')

        self._writer.start('from')
        self._writer.element('address', text=self.clientCode)
        self._writer.end('from')

        self._writer.end('delivery')

        # Properties block
        self._writer.start('properties')

        self._writer.element('identity', text='1')
        self._writer.element('sentAt', text=self.creation_date.strftime('%y-%m-%dT%H:%M+1'))
        self._writer.element('expiresAt')
        self._writer.element('topic')

        self._writer.end('properties')

        # Manifest block
        self._writer.start('manifest')

        self._writer.start('reference', attrib={'uri': '#Reply-to'})
        self._writer.element('description')
        self._writer.end('reference')

        self._writer.end('manifest')

        # Process block
        self._writer.start('process')

        self._writer.element('type')
        self._writer.element('instance')
        self._writer.element('handle')

        self._writer.end('process')

        self._writer.end('Message')

        self._writer.flush()

.