Gawel's blurb

Twitter updates

  • Changing the behavior of a #django app is a pain. How do I override the form class used by the app ? Monkey patch...?

Propagande

Feeds

Tags

Commentaires

Archives

Blogroll

Faire du virtualhosting avec zope façon wsgi

J'en avais ras le bol que Zope nécessite des url complètement tordues pour faire du virtual hosting. Ça m'empêchais entre autre d'utiliser Paste#urlmap pour dispatcher certaines url sur d'autres applis que Zope.

Du coup, j'ai tenté un truc tout con: plutôt que d'utiliser les RewriteRule d'Apache, récrire le PATH_INFO en englobant l'application Zope dans une autre. Et ça marche. Fiesta !

Voilà donc à quoi ça ressemble. J'utilise zopeproject. J'ai donc modifier le machin qui créer l'application Zope. A savoir le fichier startup.py comme ceci:

def application_factory(global_conf, conf='zope.conf', vhost='www.gawel.org'):
    vhost = '/++vh++http:%s:80/++' % vhost
    zopeapp = zope.app.wsgi.getWSGIApplication(zope_conf)
    def zopewrapper(environ, start_response):
        environ['PATH_INFO'] = vhost + environ['PATH_INFO']
        return zopeapp(environ, start_response)
    return zopewrapper

Et hop, ça roule. L'avantage, en plus d'avoir une url propre en entrée, c'est que vu que je développe aussi derrière Apache, j'ai juste eu à changer mon fichier debug.ini pour prendre en compte mon virtual host de développement.

En fait j'ai fais un peu mieux que tout ça, car comme dit au début, le but était d'utiliser Paste#urlmap. La source de la bidouille en question est ici.

Aller, pendant que j'y suis, j'en chiais aussi pas mal pour déterminer vers quel backend rediriger les requêtes dans varnish. Tester des ++ dans l'url, ça lui plaisait pas du tout. Vu que j'utilise Apache devant (surtout pour subversion), j'ai trouvé le truc. Il suffit d'activer le module headers:

# a2enmod headers

Puis rajouter un truc du genre dans votre virtualhost Apache:

RequestHeader set VARNISH_BACKEND gawel_org

Vous l'aurez compris, ceci ajoute un header à la requête. Ensuite, dans varnish, on test ce header:

if (req.http.VARNISH_BACKEND ~ "gawel_org") {
    set req.backend = gawel_org;
}

Et le tour est joué. Il faut bien sur que toutes les requêtes entrantes aient ce header. Pour moi ce n'est pas un problème vu que tout passe par apache.

svn: le futur du CMS !!!

Vous êtes comme moi je suis sûr. Editer des document dans des éditeur WYSIWYG, ça vous saoul au plus haut point. Personnellement, je préfère de loin un bon éditeur et du reStructuredText.

La solution je l'avais depuis longtemps. Stocker les billets de mon blog dans un svn et les publier avec un framework quelconque. Ca fait un moment maintenant que j'utilise Zope3 pour ça, mais le code était assez dégueulasse. Je me suis donc dit que si je mettais tout ça un peu au propre, cela pourrait servir à d'autres. Je me suis donc lancé dans un énorme week-end de geeking. le résultat est gp.svnfolder, un package Zope3 permettant de publier un répertoire svn contenant des documents en reStructuredText.

Voyons plutôt. Il nous faut une repository valide:

>>> import os
>>> from os.path import split
>>> curdir = split(os.getcwd())[0]
>>> repos = os.path.join(curdir, 'tests', 'rstfolder')

Avec ça, on peut instancier un dossier svn:

>>> from gp.svnfolder.folder import SVNFolder
>>> folder = SVNFolder()
>>> folder.__name__ = 'blog'
>>> folder.path = u'file://%s' % repos

Et en voir le contenu:

>>> sorted(folder.keys())
[u'doc.rst', u'doc2.rst']

Et accéder aux fichiers:

>>> file = folder['doc.rst']
>>> print file.data
Document 1
==========
<BLANKLINE>

Ensuite il suffit d'utiliser les vues fournies pour rendre ces fichiers en html.

Voila. Je penses qu'avec un peu de motivation je pourrais facilement le rendre compatible avec Mercurial et Bazaar mais ça suffit à faire mon bonheur :)

Zope 3 adapters et widgets

Épris d'une étonnante motivation ces derniers jours, j'ai pondu deux tutoriels sur Zope 3. Un sur les adapters et un sur les widgets.

Ils sont aussi visible sur le site de l'AFPy, bien sûr !

Zope3 et XML avec lxml (suite)

Comme on le sait tous, écrire des expressions python dans des ZPT, c'est mal. J'ai donc voulu aller plus loin afin de pouvoir utiliser les tree xml sans avoir à faire des expressions python.

Avec Zope3, il est possible de rajouter des espaces de nom au expressions TAL (ah, les joies de la component architecture :). J'ai donc implémenté deux espaces de nom supplémentaire me permettant d'accéder aux méthodes find et findall d'un élément xml.

Ma template a donc maintenant cette jolie bouille:

<dl class="lastfm" tal:define="root view/xml">
  <dt class="title" tal:content="view/title" />
  <dd tal:repeat="track root">
      <a tal:attributes=" href track/find:url/text">
        <span tal:content="track/find:name/text" />
        <span class="small"
              tal:content="track/find:artist/text" />
      </a>
  </dd>
</dl>

Ce qui est tout de même plus agréable à regarder. Ce qui est déconcertant c'est la facilité d'implémentation. Il suffit de quelques lignes de code pour obtenir ce résultat.

Zope3 et XML avec lxml

Ce soir j'ai joué avec Zope3 et lxml. Le résultat est plutôt intéressant. On trouve en combinant les ZPT et lxml une bonne alternative au xslt.

Voyez plutôt. Une petite classe de vue:

# -*- coding: utf-8 -*-
import os
import random
from lxml import etree
from zope.publisher.browser import BrowserView

PREFIX = '/tmp'

def getxml(filename):
    """
    return a ElementTree parsed from the file
    """
    fd = open(os.path.join(PREFIX,filename))
    doc = etree.parse(fd)
    fd.close()
    return doc.getroot()

class LastfmView(BrowserView):
    """
    a view to render the xml
    """

    views = ((u'Coup de coeur','recentlovedtracks.xml'),
             (u'Ecouté récemment','recenttracks.xml'))

    def __call__(self):
        self.title, filename = random.choice(self.views)
        self.xml = getxml(filename)
        return super(LastfmView,self).__call__()

Associée à une petite template toute bête:

<dl class="lastfm" tal:define="root view/xml">
  <dt class="title" tal:content="view/title" />
  <dd tal:repeat="track root">
      <a tal:attributes=" href python: track.find('url').text">
        <div tal:content="python: track.find('name').text" />
        <div class="small"
             tal:content="python: track.find('artist').text" />
      </a>
  </dd>
</dl>

Et hop, vous obtenez le portlet lastfm de mon site :)

Vous pouvez voir le source complet ici avec le script bash qui dowload les xml.

Zope3: comment ajouter un objet proprement.

A première vue, le plus simple moyen d'ajouter un objet dans un dossier serait le suivant:

>>> from zope.app.folder import Folder
>>> mycontent = Folder()
>>> mycontent.__name__ = 'myid'

>>> folder = Folder()
>>> folder['myid'] = mycontent

Cela fonctionne bien. L'inconvénient est que l'on perds toute la machinerie de zope.

J'ai donc cherché un moyen plus propre d'ajouter un objet. J'en suis arrivé a la conclusion que le mieux est d'utiliser une vue Adding. Ce qui nous donne:

from zope.app.container.browser.adding import Adding
from zope.app.event.objectevent import ObjectCreatedEvent
from contents import MyContent

class MyContentView(BrowserView):
    """ a simple view """

    def add(self):
        view = Adding( self.context, self.request )
        context = view.add( MyContent() )
        notify(ObjectCreatedEvent(context))

Par ce moyen, on créer un objet proprement. Par exemple l'id seras généré à l'aide d'un INameChooser, etc.