Comment faciliter la lecture de ses routes sous Pyramid ?

Je souhaite vous faire part de mes réflexions à propos de la déclaration de routes sous Pyramid.

Voici un extrait de la déclaration des routes du projet Bookie (Python based delicious.com replacement).

C'est un extrait du fichier bookie/routes.py :

config.add_route("home", "/")
config.add_route("dashboard", "/dashboard")

# auth routes
config.add_route("login", "login")
config.add_route("logout", "logout")
config.add_route("reset", "{username}/reset/{reset_key}")
config.add_route("signup", "signup")
config.add_route("signup_process", "signup_process")

# celery routes
config.add_route("celery_hourly_stats", "jobhourly")

# DELAPI Routes
config.add_route("del_post_add", "{username}/delapi/posts/add")
config.add_route("del_post_delete", "{username}/delapi/posts/delete")
config.add_route("del_post_get", "{username}/delapi/posts/get")
config.add_route("del_tag_complete", "{username}/delapi/tags/complete")

# bmark routes
config.add_route("bmark_recent", "recent")
config.add_route("bmark_recent_tags", "recent/*tags")

config.add_route("bmark_readable", "bmark/readable/{hash_id}")

# user based bmark routes
config.add_route("user_bmark_recent", "{username}/recent")
config.add_route("user_bmark_recent_tags", "{username}/recent/*tags")

config.add_route("user_bmark_edit", "{username}/edit/{hash_id}")
config.add_route("user_bmark_edit_error",
    "{username}/edit_error/{hash_id}")
config.add_route("user_bmark_new", "{username}/new")
config.add_route("user_bmark_new_error", "{username}/new_error")

# config.add_route("bmark_delete", "/bmark/delete")
# config.add_route("bmark_confirm_delete", "/bmark/confirm/delete/{bid}")

# tag related routes
config.add_route("tag_list", "tags")
config.add_route("tag_bmarks", "tags/*tags")

Voici maintenant la liste des fichiers présents dans le dossier views :

.
├── bcelery
├── __init__.py
├── lib
├── models
├── routes.py
├── routes.pyc
├── scripts
├── static
├── templates
├── tests
└── views               <=== ce dossier
    ├── accounts.py
    ├── api.py
    ├── auth.py
    ├── bmarks.py
    ├── delapi.py
    ├── exceptions.py
    ├── __init__.py
    ├── stats.py
    ├── tags.py
    └── utils.py

Maintenant, je vous pose les questions suivantes :

  • En lisant le code source ci-dessous, pouvez vous me dire où sont les fonctions qui gèrent les vues home et dashboard ?
config.add_route("home", "/")
config.add_route("dashboard", "/dashboard")
  • En lisant le code source ci-dessous, pouvez vous me dire où sont les fonctions qui gèrent les vues user_bmark_recent, user_bmark_recent_tags et user_bmark_edit ?
config.add_route("user_bmark_recent", "{username}/recent")
config.add_route("user_bmark_recent_tags", "{username}/recent/*tags")

config.add_route("user_bmark_edit", "{username}/edit/{hash_id}")

Il n'est pas possible de répondre à ces questions facilement… il faut soit ouvrir les fichiers dans le dossier views ou utiliser grep pour faire une recherche.

Dans ce code source, il n'y a que les commentaires au dessus des routes qui nous permettent d'avoir un indice du fichier où se trouvent les fonctions views.

Les chemins complets vers les fonctions views ne sont pas non plus visibles dans l'outil de debug de Pyramid :

Maintenant, voici une autre manière de déclarer ses routes :

config.add_route("home", "/")
config.add_view(name="home", view="bookie.views.home")

config.add_route("dashboard", "/dashboard")
config.add_view(name="dashboard", view="bookie.views.stats.dashboard")

# auth routes
config.add_route("login", "login")
config.add_view(name="login", view="bookie.views.auth.login")

config.add_route("logout", "logout")
config.add_view(name="logout", view="bookie.views.auth.logout")

config.add_route("reset", "{username}/reset/{reset_key}")
config.add_view(name="reset", view="bookie.views.auth.reset")

config.add_route("signup", "signup")
config.add_view(name="signup", view="bookie.views.auth.signup")

config.add_route("signup_process", "signup_process")
config.add_view(name="signup_process", view="bookie.views.auth.signup_process")

…

Ici, en utilisant une déclaration explicite config.add_view je sais directement où sont présentent les fonctions views.

Je trouve cette notation bien plus pratique pour une personne qui souhaite contribuer, comprendre rapidement la structure d'un projet Pyramid.

Qu'en pensez vous ? Quels sont vos pratiques pour faciliter la lecture de vos projets Pyramid ?

Read and Post Comments

Réflexions à propos de NodeJS et de Javascript plus globalement

Cela fait quelques jours que je me pose la question suivante : « Est-ce que Node.js ne va pas devenir une technologie incontournable / majeur dans les 2 ans qui viennent ? »

Le contexte

Je suis un développeur Python depuis de nombreuses années. J'aime ses librairies, j'aime ses outils, j'aime sa communauté.
J'aime tellement sa syntaxe que quand je vois la syntaxe d'autres langages, j'ai une réaction quelque peu épidermique à la lecture du code.
Avec le temps, l'habitude de la syntaxe Python minimaliste proche d'un pseudo code rend difficile la possibilité d'apprécier un autre langage. Je ne sais pas si c'est positif ou négatif, je me pose simplement la question. Enfin ceci est un autre sujet.

Cependant, mon regard se tourne de plus en plus vers Node.js... je n'ai pas franchi le pas, je n'ai rien développé en Node.js… mais je me demande si je ne passe pas à coté de quelque chose d'important.

Les forces de Node.js

  • Bien que Node.js date seulement de 2009 :

  • tous les développeurs web (front-end) connaissent le langage, il est très populaire. Par conséquent, pour passer à Node.js il n'y a pas le frein d'apprendre un nouveau langage comme ça serait le cas avec l'utilisation d'un framework web basé sur Ruby ou Python. Je pense que dans les mois qui viennent, de nombreux développeurs PHP vont passer à Node.js… surtout ceux qui regardaient ailleurs mais qui ne souhaitent pas apprendre un nouveau langage.

  • la possibilité de partager du code (librairies communes…) entre la partie client et serveur peut être intéressant. La question se pose de plus en plus étant donnée qu'on est de plus en plus amené à effectuer beaucoup de traitement coté client (exemple avec Backbone.js).
    Dernièrement, dans tous mes projets de développement web, j'ai utilisé un moteur de template coté client en plus du moteur de template coté serveur.
    Je dois gérer de plus en plus souvent une couche modèle coté client. Des vues coté client… Cela fait donc plein d'éléments en doubles, utilisés une fois sur le serveur, une fois sur le client. Donc deux technologies à maîtriser.
    Ces derniers jours, la visualisation du screencast du framework javascript Meteor m'a mis la puce à l'oreille.
    Autre exemple, il est possible d'utiliser la même API Canvas coté client et coté serveur (avec node-canvas).
  • Node.js semble être très rapide, ça tient très bien la monté en charge.

  • La vitesse de l'interpréteur Javascript est en constante progression V8 (JavaScript engine)

Faiblesses de Node.js

Je ne sais pas si mes remarques sont exactes, je n'ai aucune expérience en Node.js.

  • Exemple dans la partie Bad Use Cases du guide Felix's Node.js Convincing the boss guide il est indiqué que les framework Node.js sont moins matures que ce que l'on peut trouver en Ruby, Python et Php (bon pour ce dernier j'ai des doutes, je pense que la communauté Node.js est déjà plus mature, plus structurée que la communauté Php… enfin bon…).
  • Je ne pense pas que Sequelize soit aussi mature que SQLAlchemy
  • Je n'ai pas trouvé d'outil comme Whoosh en Javascript (c'est un moteur de recherche léger). J'ai tout de même trouvé Node-xapian

Je ne sais pas si il est encore tôt pour passer à Node.js mais si la communauté continue à être aussi dynamique que ces 2 dernières années… alors les pièces manquantes seront bientôt créées.

Est-ce que l'on va retrouver Javascript partout ?

Voici une petite liste :

Quand je vois cette liste, je me pose sérieusement la question : est-ce que Javascript est le langage incontournable de ces 10 prochaines années ? Certes il est déjà incontournable dans le navigateur, mais pour le reste ?


Édition 5 mai 2012 :

  • j'ai publié ce billet sur LinuxFR, j'ai reçu 90 commentaires…
  • j'ai publié ce billet sur la mailing list de l'Afpy, j'ai reçu 25 commentaires… malheureusement les archives de cette liste n'est pas publique

merci beaucoup pour vos retours d'expériences !

Read and Post Comments

Ce qui me fait préférer Python

Depuis que j'ai débuté l'informatique, j'ai appris et utilisé de nombreux langages.
Concernant les langages uniquement, sans parler des APIs ou Framework ni des shells, plus ou moins en ordre chronologique, j'ai commencé sur Amstrad CPC par le Basic, puis l'assembleur z80.
Quand je suis passé au PC, j'ai commencé par le Turbo Basic de Borland, le Quick Basic de Microsoft, le Turbo Pascal de Borland, de l'assembleur x86, un tout petit peu de C, le Visual Basic de Microsoft, le Delphi de Borland.
Je suis ensuite passé à GNU/Linux en 1998, là j'ai découvert et utilisé le Perl, PHP, Python, j'ai approfondi le C et C++. J'ai étudié un peu Java et C#. J'ai utilisé un peu TCL. J'ai jetté un oeil sur Ruby, Ada, Lisp. Je connais de nom Caml, Scheme, Smaltalk, Eiffel ...

Pourquoi cette restrospective ? Parce que parmi tous les langages que j'ai étudié et/ou utilisé, à mes yeux, l'un d'eux sort du lot : Python.

Bien entendu, celui-ci sort du lot dans son domaine d'application. Par exemple, Python n'est pas adapté pour développer le noyau d'un système d'exploitation, ni pour réaliser un moteur de base de donnée dont l'objectif premier est la performance (bien qu'il existe une implémentation de base de donnée 100% développé en Python). Par contre je choisirais Python pour tous autres domaines où les performances bruts ne sont pas au coeur du besoin, par exemple pour :

  • des scripts d'administrations systèmes ou d'analyses de fichiers textuels.

  • tous les développements liés à l'Internet et en particulier au Web: scripts CGI, moteurs de recherche, agents intelligents, objets distribués...

  • l'accès aux bases de données (relationnelles).

  • la réalisation d'interfaces graphiques utilisateurs (par exemple pour des applications "lourdes") basées sur Gtk, Qt, wxWidgets.

  • la réalisation d'applications web

  • le calcul scientifique et l'imagerie. Python ne sert alors pas à écrire les algorithmes, mais à combiner et mettre en oeuvre rapidement des librairies de calcul écrites en langage compilé (C, C++, Fortran, Ada,...).

  • L'apprentissage de la programmation objet.

  • Le prototypage rapide d'applications. L'idée générale est de commencer par écrire une application en Python, de la tester (ou de la faire tester par le client pour d'éventuelles modifications du cahier des charges). Trois cas peuvent alors se présenter:

    • Les performances sont satisfaisantes, après optimisation éventuelle du code Python. On livre alors le produit tel quel au client.

    • Les performances ne sont pas satisfaisantes, mais l'analyse de l'exécution du programme (à l'aide du profiler de Python) montre que l'essentiel du temps d'exécution se passe dans une petite partie du programme. Les fonctions, ou les types de données, correspondants sont alors réécrits en C ou en C++, sans modification du reste du programme.

    • Sinon, il est toujours possible de récrire tout le programme, en utilisant la version Python comme un brouillon.
      Même dans le pire des trois cas, il est très vraisemblable que le temps de développement aura été sensiblement plus court que si le programme avait été développé directement en C ou en C++.

Le but de cet article et des autres articles qui traiteront du même thème est de vous présenter pourquoi j'apprécie Python plutôt qu'un autre langage.

Pour ceux qui me suggéreront Java, je répondrais que Java est un très bon langage, dont les performances (du langage) [1] sont très bonnes et meilleurs que Python, dont l'étendu des librairies, framework... est très importante... mais j'aime le coté agile, léger et simple de Python. D'autre part, je connais le langage Java mais j'ai de très modestes connaissances au niveau des frameworks qui gravitent autour de ce langage. Par conséquence, je ne ferais pas de comparaison entre Java et Python.

Je ne ferais pas non plus (ou alors très très peu) de comparaison avec Ruby car je ne connais pas beaucoup ce langage. Je connais les bases et les concepts de RoR (RubyOnRails) car j'ai survolé le livre Ruby On Rails. D'autre part, j'ai l'impression que la philosophie Python/Ruby sont assez proche.

Qu'est ce qui me fait préférer Python à d'autres langages :

  • D'un point de vue langage (syntaxique, fonctionnalités...) :
    • La syntaxe de Python est très simple et, combinée à des types de données évolués (listes, dictionnaires,...), conduit à des programmes à la fois très compacts et très lisibles. A fonctionnalités égales, un programme Python (abondamment commenté et présenté selon les canons standards) est souvent de 3 à 5 fois plus court qu'un programme C ou C++ (ou même Java) équivalent, ce qui représente en général un temps de développement de 5 à 10 fois plus court et une facilité de maintenance largement accrue [2].
    • Les arguments des fonctions peuvent être optionnels et/ou nommés (voir l'article que j'ai écris à ce sujet : Dans la série ''Ce qui me fait préférer Python'' : ''arguments optionnels et nommés'')
    • Les compréhensions de listes.
    • Python est un langage réflexif (il supporte la métaprogrammation, par exemple la capacité pour un objet de se rajouter ou de s'enlever des attributs ou des méthodes, ou même de changer de classe en cours d'exécution).
    • Introspectif (un grand nombre d'outils de développement, comme le débogueur ou le profileur, sont implantés en Python lui-même).
    • Le système de namespace des modules, système d'importation, et les possibilités offertes par ce mécanisme.
    • Les metaclass.
    • Les décorateurs.
    • Le fait qu'une classe soit un dict.
    • Presque tout est objet (par exemple les chaînes...).
    • self.__dict__.update(locals())
    • Les exceptions et l'arbre d'exceptions.
    • La surcharge de __call__ permettant d'exécuter l'instance d'un objet.
    • Les surcharges d'opérateurs : __repr__ , __str__, __unicode__, ...
    • les docstrings
    • import __future__ qui permet d'activer des fonctionnalités qui seront livrées par défauts dans les futures versions de Python
  • Caractéristique "non technique" de Python (philosophie, communauté, organisation, méthode...)
    • Python convient aussi bien à des scripts d'une dizaine de lignes qu'à des projets complexes de plusieurs dizaines de milliers de lignes;
    • Le langage Python est entièrement libre et les outils, librairies qui gravitent autour de ce langage sont eux aussi la plupart du temps libre.
    • La philosophie agile du langage, des librairies et des personnes qui composent la communauté Python.
    • L'annuaire Python Index qui regroupe à un endroit unique les librairies Python.
    • C'est super plaisant d'écrire du code python : exemple de comparaison "XML-RPC Python vs PHP".
    • La communauté de qualitée : être en contact avec des gens compétant et qui sont capables de réaliser des designs de qualités.
    • Le fait que python soit souvent utilisé comme langage de bindings des outils libres (même Mozilla s'y met).
    • Les designs pattern sont facilement réalisables (par exemple un décorateur, une fabrique, proxy, observateur...)
    • l'avenir avec PyPy: un des buts du projet est de générer une implémentation Python optimisée à partir de PyPy qui tournera plus rapidement que l'implémentation en C actuelle (CPython), bien que ce but ne soit pas encore atteint.
    • le fait que ce soit un langage "multidomaine" : web, application lourde (qt, gtk, wxwindows)
    • la grande quantité de librairies et de framework. Dans la majorité des cas ils/elles sont de grand(e)s qualité(e)s.
    • les PEP : des minis cahiers des charges soumis à l'approbation dans un premier temps de l'ensemble des développeurs Python, pour être affinés ou rejetés, puis sont acceptés ou non par Guido van Rossum (créateur et mainteneur de Python)
  • Outils intégrés ou proches du langage :
    • Les possibilités offertes par le prompt python (et IPython), parfaitement adapté pour tester une api, un objet ou même apprendre le langage.
    • Très bonne portabilité du langage et la librairie standard Python : réalisation assez simplement d'application mutliplateforme.
    • pdb : débogueur fortement intégré au langage.
    • Les possibilités de dépréciations de fonctions / classes ainsi que leurs déplacements automatiques.
    • virtualenv : permet de créer facilement et rapidement un environnement Python de développement ou de test. Peut même servir en production.
    • Les bindings en C facilement réalisable.
    • reStructuredText (docutils)
    • Rapidité et simplicité d'installation de paquet (egg) via easy_install.
    • distutils (Building and installing Python modules)
    • Possibilité de réaliser des modules très rapides grâce à Pyrex. Cet outil permet de transformer du code Python typé en langage C. Le code C est ensuite compilé pour produire un module très performant directement utilisable via Python.
    • Psyco pour accélérer l'exécution de programmes Python sans aucune modification des sources.
  • Outils agiles, tests...
    • Les doctests.
    • pylint : permettant de tester la qualité du code.
    • Les outils de profiling et de recouvrement des tests.
  • Les outils, librairies Python :
  • Les frameworks et outils web :
    • Zope3 : serveur d'application web orienté objet
    • Django : framework de développement Web en Python
    • Pylons : framework de développement Web en Python
    • Routes : URL mapper
    • Cherrypy : framework de développement Web en Python
    • Paste
    • ...

J'arrête la liste ici pour le moment car le but de cet article n'est pas de réaliser une liste exhaustive des outils Python. Je compte plutôt utiliser cet article comme une sorte de sommaire vers d'autres articles que j'écrirai à l'avenir.

[1]The Computer Language Benchmarks Game : http://shootout.alioth.debian.org/
[2]http://www.linux-center.org/articles/9812/python.html
Read and Post Comments

Dans la série « Ce qui me fait préférer Python » : « arguments optionnels et nommés »

Une des fonctionnalités qui me fait préférer Python à d'autre langage est la possibilité d'utiliser des arguments nommés dans les fonctions ou les méthodes.

Exemple en PHP

Je commence par écrire une fonction avec des paramètres optionnels :

<?php

function affiche_contact($nom, $prenom, $telephone1 = "", $telephone2 = "", $email = "")
{
    print("Nom : $nom\n");
    print("Prénom : $prenom\n");
    print("Téléphone1 : $telephone1\n");
    print("Téléphone2 : $telephone2\n");
    print("Email : $email);
}

?>

J'affiche un contact sans spécifier aucun argument optionnel :

<?php
affiche_contact("Dupond", "Pierre");
?>

Jusque là tout va bien. A noter tout de même que sans lire l'entête de la fonction, il n'est pas possible de savoir à quoi correspond chaque argument. De plus il n'est pas possible d'envoyer en premier argument le prénom puis le nom. L'entête de la fonction déclare que c'est d'abord le nom, puis le prénom, il n'est pas possible de changer cet ordre lors de l'appel de la fonction. Vous comprendrez ensuite pourquoi j'ai donné cette précision.

J'affiche un contact mais cette fois je spécifie l'argument optionnel $telephone1 :

<?php
affiche_contact("Dupond", "Pierre", "03 87 20 25 20");
?>

Toujours aucun problème, l'argument $telephone1 est le premier argument optionnel.

Je souhaite maintenant afficher un contact avec un numéro de téléphone et une adresse mail :

<?php
affiche_contact("Dupond", "Pierre", "03 87 20 25 20", "", "dupond.pierre@example.com");
?>

Comme vous pouvez le voir, si je souhaite passer le paramètre $email je suis obligé d'envoyer l'argument $telephone2. Ceci n'est vraiment pas pratique ni élégant. De plus, je dois avoir connaissance de la valeur par défaut du paramètre que je ne souhaitais pas envoyer.

Ce cas de figure est une vrai limite au arguments optionnels des fonctions du langage PHP. J'ai souvent été confronté à ce problème qui est vraiment énervant lorsque l'on souhaite étendre les fonctionnalités d'une fonction sans casser une API existante.

Exemple en Python

Je commence par écrire une fonction avec des paramètres optionnels :

def affiche_contact(nom, prenom, telephone1 = "", telephone2 = "", email = ""):
    print ("""Nom : %s
        Prénom : %s
        Téléphone1 : %s
        Téléphone2 : %s
        Email : %s""" % (nom, prenom, telephone1, telephone2, email))

J'affiche un contact sans spécifier aucun arguement optionnel tout comme avec la syntax PHP :

affiche_contact("Dupond", "Pierre")

Je peux aussi utiliser la syntaxe suivante :

affiche_contact(nom = "Dupond", prenom = "Pierre")

Cette syntaxe nous indique la correspondance de chaque champ envoyé. Cela évite au lecteur de consulter l'entête de la fonction.

Je peux aussi envoyer les aguments dans un ordre quelconque :

affiche_contact(prenom = "Pierre", nom = "Dupond")

J'affiche un contact mais cette fois je spécifie l'arguement optionnel telephone1 :

affiche_contact("Dupond", "Pierre", "03 87 20 25 20")

L'argument telephone1 est le premier arguement optionnel, il n'est donc pas obligatoire de le nommer.

Je souhaite maintenant afficher un contact avec un numéro de téléphone et une adresse mail :

affiche_contact("Dupond", "Pierre", "03 87 20 25 20", email = "dupond.pierre@example.com")

Comme vous pouvez le constater, j'ai envoyé uniquement les paramètres optionnels que je souhaite transmettre, contrairement à PHP je n'ai pas besoin de transmettre le paramètre telephone2.

Les arguments optionnels nommés sont vraiment une fonctionnalité Python très pratique. Cela permet de vraiment choisir les paramètres que l'on souhaite envoyer sans se soucier de l'ordre de ces derniers.

Les arguments nommés ont aussi l'avantage d'indiquer au lecteur le nom de l'argument envoyé sans avoir à regarder l'entête de la fonction.

Solution de contournement en PHP

En PHP, il y a aussi une méthode pour produire plus ou moins le même effet.

Exemple :

<?php

function affiche_contact($data)
{
    if (!isset($data['telephone1'])) $data['telephone1'] = '';
    if (!isset($data['telephone2'])) $data['telephone2'] = '';
    if (!isset($data['email'])) $data['email'] = '';

    print("Nom : " . $data['nom'] . "\n");
    print("Prénom : " . $data['prenom'] . "\n");
    print("Téléphone1 :" . $data['telephone1'] . "\n");
    print("Téléphone2 :" . $data['telephone2'] . "\n");
    print("Email :" . $data['email']);
}

?>

Puis lors de l'appel de la fonction :

<?php

affiche_contact(array("nom": "Dupond", "prenom": "Pierre"));
affiche_contact(array("nom": "Dupond", "prenom": "Pierre", "telephone1": "03 87 20 25 20"));
affiche_contact(array(
    "nom" => "Dupond",
    "prenom" => "Pierre",
    "telephone1" => "03 87 20 25 20",
    "email" => "dupond.pierre@example.com"
));

?>

Ceci est plus long (surtout dans le corps de la fonction) et moins élégant qu'en Python où cette fonctionnalité est directement intégrée au langage.

Conclusion

Je trouve que cette fonctionnalité du langage Python est très pratique bien qu'elle ne soit pas une killer feature du langage.

Références

Pour finir, quelques références :

Read and Post Comments

Mes flux :