Yoan Blanc’s weblog

Another lost swiss guy

Python web app as an egg with Paste, Fabric and Spawning

Yoan BlancSat, 28 Nov 2009, , , ,

One of the major success of PHP is how easily you can deploy PHP applications. Thanks mod_php for that. Python or Ruby requires quite more work even though it’s not that painful if your using something like Passenger with Ruby. I’m still looking for a way that I find brilliant. So I played with Paste, Spawning and setuptools to build a very minimalist WSGI application you deploy without any pain using Fabric.

Before we start, you can find the code bellow on GitHub.

First the setup.py file that describe our application:

from setuptools import setup, find_packages

setup(name="app",
      version="0.1",
      description="example application",
      long_description="",
      keywords="",
      author="Yoan Blanc",
      author_email="yoan.at.dosimple.ch",
      url="http://yoan.dosimple.ch/blog/",
      license=’All rights reserved’,
      packages=find_packages(),
      zip_safe=False,
      install_requires=[],
      entry_points={"paste.app_factory": ["main=app:make_app"]},
     )

It’s all standard except the entry point called paste.app_factory that will be used by Paste to create the WSGI application. For the Djangoist here, Paster acts like your manage.py.

Make the WSGI application itself, into app/__init__.py:

def application(environ, start_response):
 start_response("200 OK",
                [("Content-Type", "text/html")])
 return ["<h1>Hello World!</h1>"]

And the factory (called make_app inside setup.py) is required as well in the same file.

def make_app(global_conf, **app_conf):
    return application

Now, you might be wondering how this make_app is called. Paste works with ini files that decribe your configuration, from the app your using to the database, the server, logs, and so on. It’s very handy to replicate the same kind of environment you find when working with Ruby on Rails by having different ini files for each.

So there is the app.ini:

[app:main]
use = egg:app

[server:main]
use = egg:Spawning
host = 127.0.0.1
port = 8000
num_processes = 1
threadpool_workers = 1

The first section describes the application to use. It’s the egg that setuptools will create and install. That’s right, your application is exactily like any other python package. It means dependencies resolution, updates,… And the second part describe which server paster will have to use to run it. Spawning is a very efficient server that handles graceful code reloading. Pure happiness.

Before you can run it using :

$ paster serve app.ini

You’ll have to install it.

# python setup.py develop

develop means it symlinks the package to your directory and you won’t have to reinstall it later to take into account the changes made.

It should run just perfectly. Now let’s worry about deploying it somewhere else. Local web applications are nice but sharing them is cool too.

This is the fabfile.py, Fabric is more or less like Capistrano (Ruby):

from __future__ import with_statement
from fabric.api import env, run, put, local, cd

env.hosts = ["localhost"]
env.path = "~/www/app"

def build_egg():
    local("python setup.py bdist_egg")

def cleanup():
    local("rm -rf build")
    local("rm -rf dist")

def start():
    with cd(env.path):
        run("PYTHONPATH=app.egg paster serve app.ini"
            "--daemon --pid-file=app.pid")

def stop():
    with cd(env.path):
        run("paster serve --stop-daemon "
            "--pid-file=app.pid")

def deploy():
    build_egg()
    put("app.ini", env.path)
    put("dist/*.egg", env.path+"/app.egg")
    cleanup()

A pythonic Makefile, more or less that has special power to deal with deployment. This how you can use it.

$ fab deploy
$ fab start

It created a .egg, uploaded it to the destination and started the Spawning server using Paster. It will run on the port 8080, to see it on the port 80 I’d use something like Nginx as a proxy. But it’s a different story (and very simple by the way).

After you made some changes simple run:

$ fab deploy

to see them live.

$ fab stop

Will stop the server.

How awesome is that?

There is a lot more that can be done regarding how you’re playing with the eggs. Using virtualenv to keep multiple, sane environments and maybe your own cheeseshop where all your eggs can live (see Ask’s Chishop)

I’m very fresh on that field but am very happy seeing that such good tools exist. Please share your experience, I’m looking forward knowing what kind of process is used in the real word out there to.

La force de PHP est et a été sa facilité de déploiement, on dépose un fichier et c’est parti mon kiki. Merci Apache + mod_php. Scénario pas toujours aussi simple dans les autres environnements d’applications web (si je mets de côté tout ce qui est CGI bien évidemment). Pour avoir bossé dans un environnement Ruby on Rails ces six derniers mois, un serveur fonctionnant sous Passenger est assez simple par rapport à ça. Recherchant toujours comment mettre ceci en place dans le monde Python, voici une approche assez minimaliste (certainement trop) pour déployer une application WSGI sous forme de paquet Python via Paster, Spawning et Fabric.

Ne voulant polluer cet espace-là, merci de regarder les exemples de code dans la zone anglophone. Ils sont également disponible sur GitHub.

Si Ruby a les gems, Python a les eggs (bien que certains Python soient ovovivipares) centralisés sur PyPI. Les avantages que je vois à créer un paquet pour son application est tout ce qui vient avec setuptools (et ses alternatives), c’est-à-dire : gestion des dépendances, exclusion des tests (et de leurs dépendances), versions, et certainement séparation en de multiple paquets.

Une fois le fichier setup.py créé, il va s’agir d’écrire un application web Python, ici WSGI mais il y a pas que ça et ça devrait couvrir 98% des besoins.

Ian Bicking a mis au point un système d’outils plus ou moins lié à WSGI nommé Paste qui permet de configurer et lancer une application web via des fichier de configuration INI (quand on est cool on fait du YAML parait-il). Paste requiert qu’on lui fournisse une méthode retournant une application WSGI, cette méthode dite fabrique, reçoit tous les éléments de configuration du fichier .ini. Chose très pratique afin d’avoir divers environnements tel que Ruby on Rails le pratique, comme : développement, test, production. Libre à chacun de faire comme il l’entend.

Le fichier .ini en question définit le paquet Python de nombre application et le serveur web à utiliser, ici Spawning qui a la force de se recharger automatiquement en cas de modification de fichiers détectée.

Travaillant avec un paquet, il est nécessaire de l’installer avant de pouvoir s’en servir. L’installation avec develop crée un lien symbolique où les modifications seront prises en compte sans devoir réinstaller.

Vous devriez pouvoir visualer ce hello world sur le port 8080 de votre machine.

Dernière étape, la plus intéressante peut-être, déployer votre application sur une machine distante, même si distante signifie localhost c’est pour l’exercice.

Si Ruby a Capistrano (et d’autres), Python lui a Fabric qui peut être vu comme un makefile pythonique avec des super pouvoirs lié à SSH.

Le fichier donné ici permet de créer le fichier .egg, de l’envoyer sur l’autre machine (votre serveur) via deploy, puis de le démarrer avec start. Tous les autres deploy’s ne demanderont pas de redémarrage puisque Spawning s’occupe de ça directement. Une fois que vous en avez terminé, un simple stop l’arrêtera. Je trouve judicieux de lancer les applications en tant que simple utilisateur sur un port comme 8080. Il est quand même de bon ton d’avoir un véritable serveur web tel que Apache, Lighty ou Nginx servant de proxy vers votre application et s’occupant de tous les fichiers statiques par la même.

In fine, la solution de l’egg exécuté tel quel est un peu brutale. Je vous invite à vous pencher vers virtualenv et Chishop. Et recherchant toujours une bonne manière de combiner tout ceci, vos solutions m’intéressent.

Bien du plaisir!

About

meYoan Blanc is a web developer that lives in Switzerland (Jaquet-Droz 6, 2300 La Chaux-de-Fonds) and works as a freelance.

Get my vCard or contact me by phone (skype:yoan.blanc) or email ().

Misc

RSS, list.blogug.ch

This site reflects only my opinion and is not affiliated with anyone else.

copyright 2006-2009 — doSimple.ch