Planète AFPy RSS

[cubicweb] Moving forward on CubicWeb development

Publié le 2016-02-08 14:50:00

Some of us at Logilab made some kind of retrospective of the processes surrounding CubicWeb (core) development. One of the important point is that we decided that Logilab would not resume the bi-monthly meetings with other developers and users to discuss and update the roadmap.

Instead, we will do more Blog Driven Development (as started by Denis on this blog) to keep the community up to date with our internal effort. We will continue to use the mailing-list to discuss with everybody what we contribute to CubicWeb.

We will also try to organize a sprint in the coming months, but we are not able to commit on a date for now, because we are under a heavy load with customer work (which is a good thing for us and CubicWeb, of course).

Another topic was to set up a kind of working agreement for those developing on the core. For a start, we agreed on the following points:

  • we stop the two-steps review process
  • integrators commit to handling pending reviews under a 7 days time frame
  • an integrator is not allowed to integrate her own patches
  • incoming patches should not make the QA numbers go down, and should be py3k compatible.

QA numbers are still to be defined in a forthcoming internal sprint on continuous integration system. Current integrators are Julien, David, Denis, Florent and Sylvain. If you're interested, let us know on the mailing-list.

[cubicweb] Using JSONAPI as a Web API format for CubicWeb

Publié le 2016-02-08 14:03:00

Following the introduction post about rethinking the web user interface of CubicWeb, this article will address the topic of the Web API to exchange data between the client and the server. As mentioned earlier, this question is somehow central and deserves particular interest, and better early than late. Of the two candidate representations previously identified Hydra and JSON API, this article will focus on the later. Hopefully, this will give a better insight of the capabilities and limits of this specification and would help take a decision, though a similar experiment with another candidate would be good to have. Still in the process of blog driven development, this post has several open questions from which a discussion would hopefully emerge...

A glance at JSON API

JSON API is a specification for building APIs that use JSON as a data exchange format between clients and a server. The media type is application/vnd.api+json. It has a 1.0 version available from mid-2015. The format has interesting features such as the ability to build compound documents (i.e. response made of several, usually related, resources) or to specify filtering, sorting and pagination.

A document following the JSON API format basically represents resource objects, their attributes and relationships as well as some links also related to the data of primary concern.

Taking the example of a Ticket resource modeled after the tracker cube, we could have a JSON API document formatted as:

GET /ticket/987654
Accept: application/vnd.api+json

  "links": {
    "self": ""
  "data": {
    "type": "ticket",
    "id": "987654",
    "attributes": {
      "title": "Let's use JSON API in CubicWeb"
      "description": "Well, let's try, at least...",
    "relationships": {
      "concerns": {
        "links": {
          "self": "",
          "related": ""
        "data": {"type": "project", "id": "1095"}
      "done_in": {
        "links": {
          "self": "",
          "related": ""
        "data": {"type": "version", "id": "998877"}
  "included": [{
    "type": "project",
    "id": "1095",
    "attributes": {
        "name": "CubicWeb"
    "links": {
      "self": ""

In this JSON API document, top-level members are links, data and included. The later is here used to ship some resources (here a "project") related to the "primary data" (a "ticket") through the "concerns" relationship as denoted in the relationships object (more on this later).

While the decision of including or not these related resources along with the primary data is left to the API designer, JSON API also offers a specification to build queries for inclusion of related resources. For example:

GET /ticket/987654?include=done_in
Accept: application/vnd.api+json

would lead to a response including the full version resource along with the above content.

Enough for the JSON API overview. Next I'll present how various aspects of data fetching and modification can be achieved through the use of JSON API in the context of a CubicWeb application.


CRUD of resources is handled in a fairly standard way in JSON API, relying of HTTP protocol semantics.

For instance, creating a ticket could be done as:

POST /ticket
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

  "data": {
    "type": "ticket",
    "attributes": {
      "title": "Let's use JSON API in CubicWeb"
      "description": "Well, let's try, at least...",
    "relationships": {
      "concerns": {
        "data": { "type": "project", "id": "1095" }

Then updating it (assuming we got its id from a response to the above request):

PATCH /ticket/987654
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

  "data": {
    "type": "ticket",
    "id": "987654",
    "attributes": {
      "description": "We'll succeed, for sure!",


In JSON API, a relationship is in fact a first class resource as it is defined by a noun and an URI through a link object. In this respect, the client just receives a couple of links and can eventually operate on them using the proper HTTP verb. Fetching or updating relationships is done using the special <resource url>/relationships/<relation type> endpoint (self member of relationships items in the first example). Quite naturally, the specification relies on GET verb for fetching targets, PATCH for (re)setting a relation (i.e. replacing its targets), POST for adding targets and DELETE to drop them.

GET /ticket/987654/relationships/concerns
Accept: application/vnd.api+json

  "data": {
    "type": "project",
    "id": "1095"

PATCH /ticket/987654/relationships/done_in
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

  "data": {
    "type": "version",
    "id": "998877"

The body of request and response of this <resource url>/relationships/<relation type> endpoint consists of so-called resource identifier objects which are lightweight representation of resources usually only containing information about their "type" and "id" (enough to uniquely identify them).

Related resources

Remember the related member appearing in relationships links in the first example?

  [ ... ]
  "done_in": {
    "links": {
      "self": "",
      "related": ""
    "data": {"type": "version", "id": "998877"}
  [ ... ]

While this is not a mandatory part of the specification, it has an interesting usage for fetching relationship targets. In contrast with the .../relationships/... endpoint, this one is expected to return plain resource objects (which attributes and relationships information in particular).

GET /ticket/987654/done_in
Accept: application/vnd.api+json

  "links": {
    "self": ""
  "data": {
    "type": "version",
    "id": "998877",
    "attributes": {
        "number": 4.2
    "relationships": {
      "version_of": {
        "self": "",
        "data": { "type": "project", "id": "1095" }
  "included": [{
    "type": "project",
    "id": "1095",
    "attributes": {
        "name": "CubicWeb"
    "links": {
      "self": ""

Meta information

The JSON API specification allows to include non-standard information using a so-called meta object. This can be found in various place of the document (top-level, resource objects or relationships object). Usages of this field is completely free (and optional). For instance, we could use this field to store the workflow state of a ticket:

  "data": {
    "type": "ticket",
    "id": "987654",
    "attributes": {
      "title": "Let's use JSON API in CubicWeb"
    "meta": { "state": "open" }


Permissions are part of metadata to be exchanged during request/response cycles. As such, the best place to convey this information is probably within the headers. According to JSON API's FAQ, this is also the recommended way for a resource to advertise on supported actions.

So for instance, response to a GET request could include Allow headers, indicating which request methods are allowed on the primary resource requested:

GET /ticket/987654

An HEAD request could also be used for querying allowed actions on links (such as relationships):

HEAD /ticket/987654/relationships/comments
Allow: POST

This approach has the advantage of being standard HTTP, no particular knowledge of the permissions model is required and the response body is not cluttered with these metadata.

Another possibility would be to rely use the meta member of JSON API data.

  "data": {
    "type": "ticket",
    "id": "987654",
    "attributes": {
      "title": "Let's use JSON API in CubicWeb"
    "meta": {
      "permissions": ["read", "update"]

Clearly, this would minimize the amount client/server requests.

More Hypermedia controls

With the example implementation described above, it appears already possible to manipulate several aspects of the entity-relationship database following a CubicWeb schema: resources fetching, CRUD operations on entities, set/delete operations on relationships. All these "standard" operations are discoverable by the client simply because they are baked into the JSON API format: for instance, adding a target to some relationship is possible by POSTing to the corresponding relationship resource something that conforms to the schema.

So, implicitly, this already gives us a fairly good level of Hypermedia control so that we're not so far from having a mature REST architecture according to the Richardson Maturity Model. But beyond these "standard" discoverable actions, the JSON API specification does not address yet Hypermedia controls in a generic manner (see this interesting discussion about extending the specification for this purpose).

So the question is: would we want more? Or, in other words, do we need to define "actions" which would not map directly to a concept in the application model?

In the case of a CubicWeb application, the most obvious example (that I could think of) of where such an "action" would be needed is workflow state handling. Roughly, workflows in CubicWeb are modeled through two entity types State and TrInfo (for "transition information"), the former being handled through the latter, and a relationship in_state between the workflowable entity type at stake and its current State. It does not appear so clearly how would one model this in terms of HTTP resource. (Arguably we wouldn't want to expose the complexity of Workflow/TrInfo/State data model to the client, nor can we simply expose this in_state relationship, as a client would not be able to simply change the state of a entity by updating the relation). So what would be a custom "action" to handle the state of a workflowable resource? Back in our tracker example, how would we advertise to the client the possibility to perform "open"/"close"/"reject" actions on a ticket resource? Open question...

Request for comments

In this post, I tried to give an overview of a possible usage of JSON API to build a Web API for CubicWeb. Several aspects were discussed from simple CRUD operations, to relationships handling or non-standard actions. In many cases, there are open questions for which I'd love to receive feedback from the community. Recalling that this topic is a central part of the experiment towards building a client-side user interface to CubicWeb, the more discussion it gets, the better!

For those wanting to try and play themselves with the experiments, have a look at the code. This is a work-in-progress/experimental implementation, relying on Pyramid for content negotiation and route traversals.

What's next? Maybe an alternative experiment relying on Hydra? Or an orthogonal one playing with the schema client-side?

[cubicweb] Status of the CubicWeb python3 porting effort, February 2016

Publié le 2016-02-05 16:03:00

An effort to port CubicWeb to a dual python 2.6/2.7 and 3.3+ code base was started by Rémi Cardona in summer of 2014. The first task was to port all of CubicWeb's dependencies:

  • logilab-common 0.63
  • logilab-database 1.14
  • logilab-mtconverter 0.9
  • logilab-constraint 0.6
  • yams 0.40
  • rql 0.34

Once that was out of the way, we could start looking at CubicWeb itself. We first set out to make sure we used python3-compatible syntax in all source files, then started to go and make as much of the test suite as possible pass under both python2.7 and python3.4. As of the 3.22 release, we are almost there. The remaining pain points are:

  • cubicweb's hadn't been converted. This is fixed in the 3.23 branch as of (don't follow that link, the commit is huge)
  • the CubicWebServerTC test class uses twisted to start an http server thread, and twisted itself is not available for python3
  • the current method to serialize schema constraints into CWConstraint objects gives different results on python2 and python3, so it needs to be fixed (
  • various questions around packaging and deployment: what happens to e.g. the cubicweb-common package installing into python2's site-packages directory? What does the ${prefix}/share/cubicweb directory become? How do cubes express their dependencies? Do we need a flag day? What does that mean for applications?

[anybox] La Base de Données Economiques et Sociales : pour qui, pourquoi et comment ?

Publié le 2016-02-01 00:00:00

[anybox] La BDES au 1er janvier 2016

Publié le 2016-02-01 00:00:00

[anybox] Développement front-end en Javascript : les outils de base

Publié le 2016-02-01 00:00:00
Petit tutoriel pour comprendre rapidement les principaux outils de développement front-end en Javascript

[anybox] Configuration du serveur de mail Odoo avec Gmail

Publié le 2016-02-01 00:00:00

[anybox] Première présentation d'AnyBlok au PyConFR 2015 à Pau

Publié le 2016-02-01 00:00:00
Le 18 octobre 2015, Georges Racinet a présenté à Pau le framework AnyBlok.

[anybox] Vos applications métiers en Javascript avec Meteor

Publié le 2016-02-01 00:00:00

[sciunto] gitbackup : maintenir une copie conforme (miroir) d'un dépôt git

Publié le 2016-01-28 23:00:00


J'ai déjà argumenté à plusieurs reprises du risque que l'on prend à utiliser des systèmes que nous ne gérons pas nous même, comme github (ref). Cette problématique fonctionne aussi pour des dépôts maintenus par des personnes qui peuvent avoir envie de supprimer les données, bien que vous les trouviez intéressantes. Mêmes arguments pour des organisations comme framasoft ou FFDN. Ainsi, je ne peux qu'encourager à avoir son propre serveur git, pour les dépôts privés, mais aussi pour les miroirs. Le Logiciel Libre a la copie dans son génome, utilisons-le.


J'ai appris comment faire proprement un miroir d'un dépôt sur la doc de github.

# On clone le depot sur github
git clone --mirror

cd repository-to-mirror.git
# On ajoute comme destination
git remote set-url --push origin

# On pousse
git push --mirror

A intervalle régulier, on peut faire

git fetch -p origin
git push --mirror

L'avantage est qu'on synchronise toutes les branches et les tags, mais on ne synchronise pas les tickets.


Pour tout dire, ce billet trainait dans mon dossier en préparation. J'utilisais un petit script et suite à l'article de Carl Chenet et repris sur framablog, je me suis convaincu qu'un code propre pouvait être utile à d'autre. Comme le dit Benjamin Bayart, il faut faire !. En quelques bouts de soirées, j'ai tenté de mettre les choses au propre.

Le but est d'avoir un outil proche de git d'un point de vue syntaxique pour automatiser les commandes ci-dessus. J'ai aussi gardé le même format (configparse) pour le fichier de configuration.

# On se crée un chez soi
mkdir backup_github && cd backup_github

# On initialise
gitbackup init

# On clone deux dépôts dont on veut un miroir
gitbackup clone sametmax_0bin

gitbackup clone carl_backupchecker

# Quand les développeurs auront fait évolué le projet,
# on pourra synchroniser les changements
# sur un dépôt spécifique
gitbackup pull sametmax_0bin

# ou sur tous les dépôts
gitbackup pull

Le code est sur github (sinon, ce ne serait pas drôle) en GPLv3.

Sous peu, je vais ajouter une fonctionnalité pour ajouter un remote afin de pousser le miroir sur un autre site. Je vais pousser le code sur pipy, et faire un paquet pour archlinux.

C'est libre, ce code est aussi le votre. Commentaires et surtout pull requests sont les bienvenus.

[cubicweb] Towards building a JavaScript user interface to CubicWeb

Publié le 2016-01-27 22:15:00

This post is an introduction of a series of articles dealing with an on-going experiment on building a JavaScript user interface to CubicWeb, to ultimately replace the web component of the framework. The idea of this series is to present the main topics of the experiment, with open questions in order to eventually engage the community as much as possible. The other side of this is to experiment a blog driven development process, so getting feedback is the very point of it!

As of today, three main topics have been identified:

  • the Web API to let the client and server communicate,
  • the issue of representing the application schema client-side, and,
  • the construction of components of the web interface (client-side).

As part of the first topic, we'll probably rely on another experimental work about REST-fulness undertaken recently in pyramid-cubicweb (see this head for source code). Then, it appears quite clearly that we'll need sooner or later a representation of data on the client-side and that, quite obviously, the underlying format would be JSON. Apart from exchanging of entities (database) information, we already anticipate on the need for the HATEOAS part of REST. We already took some time to look at the existing possibilities. At a first glance, it seems that hydra is the most promising in term of capabilities. It's also built using semantic web technologies which definitely grants bonus point for CubicWeb. On the other hand, it seems a bit isolated and very experimental, while JSON API follows a more pragmatic approach (describe itself as an anti-bikeshedding tool) and appears to have more traction from various people. For this reason, we choose it for our first draft, but this topic seems so central in a new UI, and hard to hide as an implementation detail; that it definitely deserves more discussion. Other candidates could be Siren, HAL or Uber.

Concerning the schema, it seems that there is consensus around JSON-Schema so we'll certainly give it a try.

Finally, while there nothing certain as of today we'll probably start on building components of the web interface using React, which is also getting quite popular these days. Beyond that choice, the first practical task in this topic will concern the primary view system. This task being neither too simple nor too complicated will hopefully result in a clearer overview of what the project will imply. Then, the question of edition will come up at some point. In this respect, perhaps it'll be a good time to put the UX question at a central place, in order to avoid design issues that we had in the past.

Feedback welcome!

[cubicweb] Happy New Year CubicWeb !

Publié le 2016-01-25 14:30:00

This CubicWeb blog that has been asleep for some months, whereas the development was active. Let me try to summarize the recent progress.

CubicWeb 3.21

CubicWeb 3.21 was published in July 2015. The announce was sent to the mailing list and changes were listed in the documentation.

The main goal of this release was to reduce the technical debt. The code was improved, but the changes were not directly visible to users.

CubicWeb 3.22

CubicWeb 3.22 was published in January 2016. A mail was sent to the mailing list and the documentation was updated with the list of changes.

The main achievements of this release were the inclusion of a new procedure to massively import data when using a Postgresql backend, improvements of migrations and customization of generic JSON exports.

Roadmap and bi-monthly meetings

After the last-minute cancellation of the may 2015 roadmap meeting, we failed to reschedule in june, the summer arrived, then the busy-busy end of the year... and voilà, we are in 2016.

During that time, Logilab has been working on massive data import, full-js user interfaces exchanging JSON with the CubicWeb back-end, 3D in the browser, switching CubicWeb to Python3, moving its own apps to Bootstrap, using CubicWeb-Pyramid in production and improving management/supervision, etc. We will be more than happy to discuss this with the rest of the (small but strong) CubicWeb community.

So let's wish a happy new year to everyone and meet again in March for a new roadmap session !

[sciunto] Don du mois : debian

Publié le 2016-01-23 23:00:00

Ce post s'inscrit dans la série des dons pour vous donner envie de contribuer même très modestement à des logiciels libres. Les petites pierres font les grands édifices.

Le don de ce mois est pour debian. J'utilise cet OS pour mes serveurs. J'ai bien essayé pour les stations de travail, mais la lenteur de l'évolution de debian m'a fait reculer pour préférer archlinux auquel j'ai déjà consacré un don. Néanmoins, debian est une distribution de choix pour les serveurs. C'est stable, c'est documenté, la communauté est active et les mises à jours sont de qualité. Donc 15$ pour debian.

Pour faire un don à debian

[carlchenet] Le danger Github

Publié le 2016-01-21 23:00:35
Alors que le projet CPython (implémentation historique du projet Python) a annoncé son passage chez Github (avec quelques restrictions, nous reviendrons là-dessus), il est plus que jamais important de s’interroger sur les risques encourus d’utiliser un logiciel propriétaire dans notre chaîne de création du Logiciel Libre. Des voies critiques s’élèvent régulièrement contre les risques encourus… Continue reading Le danger Github

[tarek] A Pelican web editor

Publié le 2016-01-21 09:40:00

The benefit of being a father again (Freya my 3rd child, was born last week) is that while on paternity leave & between two baby bottles, I can hack on fun stuff.

A few months ago, I've built for my running club a Pelican-based website, check it out at : Nothing's special about it, except that I am not the one feeding it. The content is added by people from the club that have zero knowledge about softwares, let alone stuff like vim or command line tools.

I set up a github-based flow for them, where they would add content through the github UI and its minimal reStructuredText preview feature - and then a few of my crons would update the website on the server I host. For images and other media, they are uploading them via FTP using FireSSH in Firefox.

For the comments, I've switched from Disqus to ISSO after I got annoyed by the fact that it was impossible to display a simple Disqus UI for people to comment without having to log in.

I had to make my club friends go through a minimal reStructuredText syntax training, and things are more of less working now.

The system has a few caveats though:

  • it's dependant on Github. I'd rather have everything hosted on my server.
  • the github restTRucturedText preview will not display syntax errors and warnings and very often, articles get broken
  • the resulting reST is ugly, and it's a bit hard to force my editors to be stricter about details like empty lines, not using tabs etc.
  • adding folders or organizing articles in the Pelican content directory freaks out my editors.
  • editing the metadata tags is prone to many mistakes

So I've decided to build my own web editing tool with the following features:

  • resTructuredText cleanup
  • content browsing
  • resTructuredText web editor with live preview that shows warnings & errors
  • a little bit of wsgi glue and a few forms to create articles without having to worry about metadata syntax.

resTructuredText cleanup

The first step was to build a reStructuredText parser that would read some reStructuredText and render it back into a cleaner version.

We've imported almost 2000 articles in Pelican from the old blog, so I had a lot of samples to make my parser work well.

I first tried rst2rst but that parser was built for a very specific use case (text wrapping) and was incomplete. It was not parsing all of the reStructuredText syntax.

Inspired by it, I wrote my own little parser using docutils.

Understanding docutils is not a small task. This project is very powerfull but quite complex. One thing that cruelly misses in docutils parser tools is the ability to get the source text from any node, including its children, so you can render back the same source.

That's roughly what I had to add in my code. It's ugly but it does the job: it will parse rst files and render the same content, minus all the extraneous empty lines, spaces, tabs etc.

Content browsing

Content browsing is pretty straightforward: my admin tool let you browse the Pelican content directory and lists all articles, organized by categories.

In our case, each category has a top directory in content. The browser parses the articles using my parser and display batched lists.

I had to add a cache system for the parser, because one of the directory contains over 1000 articles -- and browsing was kind of slow :)

resTructuredText web editor

The last big bit was the live editor. I've stumbled on a neat little tool called rsted, that provides a live preview of the reStructuredText as you are typing it. And it includes warnings !

Check it out:

I've stripped it from what I needed and included it in my tool.

I am quite happy with the result so far. I need to add real tests and a bit of documentation, and I will start to train my club friends on it.

The next features I'd like to add are:

  • comments management
  • media management
  • spell checker

The project lives here:

I am not going to release it, but if someone finds it useful, I could.

It's built with Bottle & Bootstrap as well.

[AFPy Salt-fr] Compte rendu (sommaire) du meetup Salt Paris de décembre 2015

Publié le 2016-01-19 23:00:00

Le jeudi 17 decembre 2015, comme annoncé, une partie de la communauté Salt s'est réuni autour de trois présentation dans les locaux de Vivendi / Canal+.

Retour d'expérience : Un peu de sel pour être HAPI - Metin OSMAN (Canal+)

Metin Osman nous a présenté un retour d’expérience sur l'utilisation de Salt à Canal+ avec un peu de fabric, jenkins, git et des syndics Salt (mais aussi elasticsearch, logstash, redis, nginx, etc.). Prochainement ils espèrent utiliser gitfs et docker. La présentation est disponible sur slideshare.

La supervision pilotée par Salt avec carbon/graphite/grafana - Arthur Lutz (Logilab)

Salt Meetup

Arthur Lutz a présenté une démo dans des docker (lancés par docker-compose) en montant une architecture frontal+application+base de données et en supervisant le tout avec Salt. Les résultats de la supervision sont stockés dans un graphite (bases temporelles whisper) et des tableaux de bord sont construits en utilisant grafana. La démo est disponible dans un dépôt mercurial sur bitbucket (salt_graphite_grafana). Quelques slides pour introduire le tout sont aussi publiés.

Salt + Graphite + Grafana

Une interface web open-source pour Salt avec SaltPad v.2 en ReactJS - Boris Feld (tinyclues)

Boris a présenté la nouvelle version de SaltPad en ReactJS (la précédente version était en Flask)

Saltpad screenshot

Nous aurons peut-être, dans les prochaines semaines, une vidéo montée par Julien de Canal+.

Merci à Canal+ / Vivendi pour le lieu. Merci à Logilab pour les pizzas & bières.

La suite : un meetup en février ?

[sciunto] Profilage CPU de rss2email et feedparser (python)

Publié le 2016-01-16 23:00:00

J'ai déjà parlé à plusieurs reprises sur ce blog de rss2email pour récupérer les flux rss. Avec plus d'une centaine de flux, il est un peu lent et consomme tout de même pas mal de CPU. Je me suis donc lancé dans un profilage du code pour comprendre ce qui consommait le plus.

Deux objectifs à ce billet :

  • rapporter ma méthode
  • demander à la communauté ce qu'on peut faire pour optimiser ce que j'ai identifié (cf la fin de ce billet)

Mise en place du profilage

On commence par cloner le dépôt de rss2email

git clone
cd rss2email

ainsi que feedparser, la bibliothèque principale car je pressens qu'on va devoir y jeter un coup d'oeil.

git clone

Je crée un virtualenv avec pew, comme suggéré par sametmax, c'est bien plus pratique.

pew new profiler

Construction et déploiement du code avec la méthode develop. Elle fait un lien symbolique, ce qui permet de ne pas avoir à faire une installation à chaque modification de code, ce qui est très intéressant lorsqu'on avance à petit pas dans le profilage.

cd feedparser
python develop
cd ..
# rss2email
python develop

On vérifie que ça tourne comme on l'attend. J'ai importé mes fichiers de conf et de base de données du serveur de prod pour être en condition de données réelles. L'option -n permet de faire un dry-run, c'est-à-dire de ne pas envoyer le courriel.

r2e -c rss2email.cfg  run 30 -n

J'installe la bibliothèque qui permet de profiler le CPU. Voir cette référence que j'ai trouvé utile pour les différentes méthodes de profilage en python.

pip install line_profiler

Pour activer le profilage sur une fonction, il suffit d'utiliser un décorateur.

def func(var):

On lance ensuite kernprof

kernprof -l -v r2e -c rss2email.cfg  run 30 -n

De manière récursive, je descend dans le code en répérant les fonctions qui prennent le plus de temps CPU.


Le résultat est que le temps CPU est principalement utilisé par feedparser, que la moitié du temps sert à récupérer des données à travers le réseau (urllib) et l'autre moitié à faire du parsage de date. Ce parsage est traité en interne par feedparser.

Ces résultats sont rapportés dans ce ticket.

Regardons plus en détails. Il existe plusieurs formats à gérer. Pour mes flux, c'est principalement rfc822.

Le code commence par ceci

timezonenames = {
    'ut': 0, 'gmt': 0, 'z': 0,
    'adt': -3, 'ast': -4, 'at': -4,
    'edt': -4, 'est': -5, 'et': -5,
    'cdt': -5, 'cst': -6, 'ct': -6,
    'mdt': -6, 'mst': -7, 'mt': -7,
    'pdt': -7, 'pst': -8, 'pt': -8,
    'a': -1, 'n': 1,
    'm': -12, 'y': 12,
    'met': 1, 'mest': 2,

def _parse_date_rfc822(date):
    """Parse RFC 822 dates and times
    There are some formatting differences that are accounted for:
    1. Years may be two or four digits.
    2. The month and day can be swapped.
    3. Additional timezone names are supported.
    4. A default time and timezone are assumed if only a date is present.

    daynames = set(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'])
    months = {
        'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6,
        'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12,

    parts = date.lower().split()

Avec le profilage, j'ai vu que 10% du temps était utilisé à construire le set(). Les avantages de set sont de garantir l'unicité et d'avoir des opérations de type union ou intersection (comme le dit la doc). Ici, daynames n'est pas modifié, on n'a pas donc besoin de fabriquer l'unicité sur quelque chose qu'on définit. Seule une itération est faite sur cet élément. Le set() ne me semble donc pas justifié.

J'ai ouvert un pull request pour corriger le set en tuple, et passer la déclaration en variable globale.

Si vous avez des suggestions pour améliorer les performances, n'hésitez pas à les partager sur github ou par email. Merci !

Note : email.utils.parsedate_tz semble être plus lent que l'implémentation de feedparser, en terme CPU, c'est relativement comparable.

[carlchenet] Feed2tweet 0.2 : pouvoir de la ligne de commande

Publié le 2016-01-13 23:00:12
Feed2tweet a été publié en version 0.2 ! Cette application prend en entrée un flux RSS et envoie les différentes entrées sur le réseau social Twitter. Elle est auto-hébergée, codée en Python 3, sous licence GPLv3 et documentée. Feed2tweet sur Github (étoiles appréciées, ça augmente la visibilité du projet) Feed2tweet sur Readthedocs Feed2tweet est déjà… Continue reading Feed2tweet 0.2 : pouvoir de la ligne de commande

[carlchenet] Étendre votre réseau Twitter avec Retweet 0.8

Publié le 2016-01-12 23:00:21
Pour rappel si vous n’avez pas lu mes précédents billets sur le sujet,  Retweet est une application  permettant de retweeter tous ou certains statuts d’un compte du réseau social  Twitter vers un autre compte. Retweet est codé en Python 3 et sous licence GPLv3. Le Github de Retweet (étoiles appréciées ;) ) Documentation officielle de… Continue reading Étendre votre réseau Twitter avec Retweet 0.8

[carlchenet] Extend your Twitter network with Retweet

Publié le 2016-01-11 23:00:28
Retweet is self-hosted app coded in Python 3 allowing to retweet all the statuses from a given Twitter account to another one. Lots of filters can be used to retweet only tweets matching given criterias. Retweet on Github (stars appreciated if you like it/use it :) ) Retweet official documentation Retweet 0.8 is available on… Continue reading Extend your Twitter network with Retweet

[carlchenet] Feed2tweet 0.2: power of the command line sending your Feed RSS to Twitter

Publié le 2016-01-10 23:00:17
Feed2tweet is a self-hosted Python app to send you RSS feed to Twitter. A long descriptions about why and how to use it is available in my last post about it. Feed2tweet on Github (stars appreciated :) ) The official documentation of Feed2tweet on Readthedocs. Feed2tweet is in production for Le Journal du hacker, a… Continue reading Feed2tweet 0.2: power of the command line sending your Feed RSS to Twitter

[carlchenet] My Free Activities in December 2015

Publié le 2016-01-08 17:30:07
I was quite busy in December 2015, especially with my new Python projects related to automatize Twitter actions. Have a look at my Github home for more information ! 1. Personal projects db2twitter 0.5 released (Github stars appreciated :) )- new option to circle through a ring of the last N tweets in order to… Continue reading My Free Activities in December 2015

[hautefeuille] Importer des utilisateurs en masse dans un NAS Synology avec Python

Publié le 2016-01-06 22:00:23

Dans le milieu professionnel, il est courant de devoir importer en masse des utilisateurs dans un système d’exploitation.

Les NAS Synology sont des systèmes particuliers dans le sens où l’administration courante du système se réalise grâce à des d’interfaces graphiques dans un navigateur internet.

Dans la partie “Gestion des utilisateurs”, il est possible d’importer en masse des utilisateurs à partir d’un fichier tabulaire CSV (bouton “Créer”).

On nous fournit donc un fichier CSV comportant des données utilisateurs.

L’objectif est de transformer le fichier source en un fichier de destination formaté selon les critères attendus par le NAS.

Le script suivant illustrera la méthode. Le script est facilement compréhensible et trivial.

L’intérêt ici est de montrer l’utilité de Python dans des tâches courantes d’administration système.

Le fichier CSV source :

Séverine,RIGHT,,"Equipe1, commun",,Interne

Le fichier CSV de destination que l’on souhaite obtenir :

severine.right  97UW9J37XX  Equipe1, commun

On pourra remarquer que certains champs ont été coupés (adresse email), d’autres ont été abandonnés (“Interne”) et d’autres créés (mot de passe). La balise de formatage a aussi été changée pour passer d’une virgule à une tabulation. Ce formatage est attendu par le NAS Synology.

Le script de modification :

#!/usr/bin/env python3.4
# -*- coding: utf-8 -*-

__title__ = 'convert'
__version__ = '0.1'
__author__ = ''
__licence__ = 'BSD'

import csv

data = set()

def generate_temp_password(length):
    if not isinstance(length, int) or length < 8:
        raise ValueError("temp password must have positive length")

    from os import urandom
    return "".join([chars[c % len(chars)] for c in urandom(length)])

def read_csv(f):
    with open(f, 'r') as csvfile:
        inreader = csv.reader(csvfile, delimiter=',', quotechar='"')
        for row in inreader:            
            data.add((row[2].split("@")[0], generate_temp_password(10), row[3], row[2]))

def write_csv(f, d):
    with open(f, 'w') as csvfile:
        outwriter = csv.writer(csvfile, delimiter='\t', quotechar="'", quoting=csv.QUOTE_MINIMAL)
        for e in d:

if __name__ == '__main__':

    for e in data:

    write_csv('synousers.csv', data)

Le script ne présente aucune difficulté de compréhension. Néanmoins, on peut souligner l’utilisation d’un set() qui permet de supprimer les éventuels doublons dans le fichier source et l’utilisation d’une fonction de génération basique de mots de passe. Dans une utilisation plus efficace, on pourrait se passer de l’utilisation du set() data et concaténer les opérations de lecture et d’écriture dans la même fonction. L’utilisation d’une variable intermédiaire et le découpage de fonction ont davantage un objectif pédagogique.

[carlchenet] Récapitulatif 2015

Publié le 2016-01-04 23:00:21
Cette année a sans nul doute été une de mes grandes années de contributions au Logiciel Libre. Afin de me remémorer le chemin parcouru, je vais prendre les sujets dans l’ordre. 1. Le Journal du hacker J’en parle très souvent sur ce blog, je suis le fondateur du Journal du hacker, un site communautaire participatif… Continue reading Récapitulatif 2015

[carlchenet] To be alerted if no more tweet is sent from your Twitter account: Twitterwatch

Publié le 2016-01-03 23:00:07
This version 0.1 of Twitterwatch is dedicated to Ian Murdock, Debian project Founder. If you use an automated system like Feed2tweet in order to feed your Twitter account from your website of any RSS feed, it is of the utmost important to know when this system is broken, because you are losing traffic because of… Continue reading To be alerted if no more tweet is sent from your Twitter account: Twitterwatch

[sciunto] Grammalecte : avancement

Publié le 2016-01-01 23:00:00

Au printemps dernier avait été lancé une campagne de financement pour grammalecte, un correcteur grammatical et typographique dont la teneur est expliquée notamment sur linuxfr. J'ai cherché à voir l'évolution du projet puisque la campagne s'est terminée avec succès. J'ai pu trouver l'état d'avancement sur le forum.

Malheureusement, il semble que l'auteur n'utilise pas de forge. D'après mes recherches, la dernière beta est la 0.5.0b4, sous forme d'extension pour libreoffice. L'ordre prévu par la campagne fait que le développeur doit se consacrer en premier à l'extension firefox, dont une version est prévue avec la sortie de firefox 44, pour des questions de version de javascript.

Néanmoins, en ouvrant l'extension pour libreoffice, on a accès au parser écrit en python. L'extension étant installée pour moi, je retrouve ce fichier dans

python $HOME/.config/libreoffice/4/user/uno_packages/cache/uno_packages/luq78j49.tmp_/Grammalecte-v0.5.0b4-1.oxt/pythonpath/

J'en ai fait un alias qui s'appelle grammalecte. Trois commandes sont possibles :

  • grammalecte : permet de tester le texte que vous entrez à la main.
  • grammalecte -f filename : pour analyser un fichier texte (UTF-8 requis).
  • grammalecte -ff filename : pour analyser un fichier texte (UTF-8 requis) et produire un fichier résultat.

Le résultat est plutôt bon, on peut clairement aller chercher les erreurs dans un fichiers markdown ou latex. Il y aura cependant des erreurs soulevées pour les balises ainsi que, dans mon cas, les apostrophes typographiques puisque je n'utilise que des apostrophes droites. Je n'ai pas vu de moyen pour ne pas faire apparaitre certaines erreurs. Celle des apostrophes est particulièrement handicapante dans mon cas, c'est à dire la différence entre l'apostrophe et l’apostrophe.

Mais c'est là que vient l'option -tf, qui corrige la typographie avant de regarder la grammaire. Ainsi, il suffit de faire

grammalecte -tf -f filename

En somme, il y a déjà de quoi avoir un outil fonctionnel même si une version finale n'est pas encore sortie.

[carlchenet] Parse your rss feed and post to Twitter with feed2tweet

Publié le 2015-12-28 17:30:01
What’s more important today for a website than relaying your new blog posts to the social networks, especially Twitter ? Lots of unmaintained or unreliable third-parties web services offer this feature. I tried and was really disappointed using them. Feed2tweet, forked from rss2twitter, now offer a self-hosted, documented, easy-to-read Python application to automatically transfer the… Continue reading Parse your rss feed and post to Twitter with feed2tweet

[carlchenet] Liens intéressants Journal du hacker semaine #52

Publié le 2015-12-27 21:45:25
Pour la 52ème semaine de 2015, voici 5 liens intéressants que vous avez peut-être ratés, relayés la semaine précédente par le Journal du hacker, votre source d’informations pour le Logiciel Libre francophone ! Juniper : une backdoor made in NSA… récupérée par un tiers Conférence FDN et Quadrature du Net sur l’année 2015 HTTPS : OVH… Continue reading Liens intéressants Journal du hacker semaine #52

[raspberry-python] Raspberry #PiZero & MagP1

Publié le 2015-12-27 16:22:00
Royal Mail

Francois Dion

[sciunto] Déploiement de la fibre optique, quel avenir, quels enjeux pour la neutralité ?

Publié le 2015-12-26 23:00:00


En définitive, on parle assez peu du réseau internet, je veux dire, le réseau physique. Pourtant, il faut bien s'en soucier, car il est au coeur de la neutralité du réseau. Dans cet article, je me contente de résumer les propos de Benjamin Bayard qu'on ne présente plus. Benjamin donne des conférences très instructives et très profondes, elles sont longues et fouillées, c'est ce que j'apprécie. Mon but est de relayer ici un des messages qu'il porte afin de multiplier les canaux de communication.

Ce qui se passe

Echelles de temps, d'espace et de débit.

  • Les plans pour fibrer la France s'étalent sur environ 10 ans Plan du gouvernement.
  • Les politiques actuelles cherchent à fibrer en priorité les grandes villes et les centres villes.
  • Il faut savoir que le volume de données transférées est multiplié par les abonnées par 10 sous les 6 ans. C'est à dire x100 en 12 ans.

Les conséquences

En terme d'espace, le fait que les zones ayant déjà un débit élevé passent à la fibre en priorité fait que la fracture numérique se creuse et continuera à se creuser. Etant donné les volumétries prévues, les régions en ADSL deviendront inhabitables du point de vue de l'internet qu'il s'y trouvera. Pour cette raison, Benjamin parle "d'erreur d'aménagement du territoire". Il faut se rendre compte qu'internet ne sert pas qu'à regarder la télévision mais il est au coeur du développement économique.

Monopole et oligopole... les risques

Si les associations membres de la fédération FDN peuvent fournir de l'internet ADSL, c'est parce qu'ils sont en mesure d'utiliser un réseau existant (dégroupage) et de pouvoir l'utiliser même pour un petit nombre d'abonnées. Avec la fibre, la situation est très différente.

Dans les zones très denses (ex Paris), chaque opérateur déploie le réseau comme il le veut. Il y aura donc plusieurs réseaux de fibres si plusieurs opérateurs sont intéressés. Dans les zones denses, il y a un accord de co-investissement entre les opérateurs, mais la participation ne peut se faire que pour des volumes d'abonnés très importantes. En zone peu dense, c'est de l'investissement public (collectivités territoriales), là où potentiellement ça peut mieux se passer pour des associatifs, mais ça reste vraiment difficile.

Si rien n'est fait, nous allons vers un oligopole (au mieux) ou un monopole orange subventionné par les contribuables 10 milliards d'euros. Bien sûr, l'unicité (ou quasi-unicité) de l'opérateur remet en cause la neutralité du net. En effet, là où il n'y aura qu'un opérateur, la seule solution pour changer de FAI sera de déménager.

Ce que propose Benjamin Bayart est

  • d'expliquer ce qui se passe (ce que je fais ici)
  • suivre les décisions prises sur ces dossiers et dialoguer avec eux
  • commenter, critiquer, conseiller pour que des FAI associatifs puissent continuer à proposer de l'internet.


Le but est de vous motiver à voir par vous même les détails de ce que j'ai résumé.

[encolpe] When UnicodeDecodeError become irrational check $LANG

Publié le 2015-12-12 15:54:25
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 It is really simple but sometimes we ran into a […]

[sciunto] Don du mois : la quadrature

Publié le 2015-12-08 23:00:00

Ce post s'inscrit dans la série des dons pour vous donner envie de contribuer même très modestement à des logiciels libres. Les petites pierres font les grands édifices.

La quadrature du net a mis en place (comme d'autres associations april, fsf) une levée de fond pour financer leurs actions futures. Je peux vous conseiller de regarder la vidéo de Benjamin Bayart & Christopher Talib : Ce que font les exégètes amateurs dans leur garage, pour un bilan de ce qui a été réalisé.

Pour ma part, ce sont 30€ qui partent à la quadrature et je pense renouveller plus fréquemment mes dons vers cette association qui possède une action incisive et décisive.

[tarek] Managing small teams

Publié le 2015-12-02 10:00:00

In the past three years, I went from being a developer in a team, to a team lead, to a engineer manager. I find my new position is very challenging because of the size of my team, and the remote aspects (we're all remotes.)

When you manage 4/5 people, you're in that weird spot where you're not going to spend 100% of your time doing manager stuff. So for the remaining time, the obvious thing to do is to help out your team by putting back your developer hat.

But switching hats like this has a huge pitfall: you are the person giving people some work to do depending on the organization priorities and you are also helping out developing. That puts you in a position where it's easy to fall into micromanagement: you are asking someone or a group of person to be accountable for a task and you are placing yourself on both sides.

I don't have any magic bullet to fix this, besides managing a bigger team where I'd spent 100% of my time on management. And I don't know if/when this will happen because teams sizes depends on the organization priorities and on my growth as a manager.

So for now, I am trying to set a few rules for myself:

  1. when there's a development task, always delegate it to someone it the team and propose your help as a reviewer. Do not lead any development task, but try to have an impact on how things move forward, so they go into the direction you'd like them to go as a manager.
  2. Every technical help you are doing for your team should be done by working under the supervision of a member of your team. You are not a developer among other developers in your own team.
  3. If you lead a task, it should be an isolated work that does not direcly impact developers in the team. Like building a prototype etc.
  4. Never ever participate in team meetings with a developer hat on. You can give some feedback of course, but as a manager. If there are some technical points where you can help, you should tackle them through 1:1s. See #1

There. That's what I am trying to stick with going forward. If you have more tips I'll take them :)

I see this challenge as an interesting puzzle to solve, and a key for me to maximize my team's impact.

Coding was easier, damned...

[sciunto] Hackadon 2015 : des dons pour des logiciels libres le 11 décembre

Publié le 2015-11-27 23:00:00

Je reprends une annonce passée sur les canaux de l'April.

Vous voulez soutenir le LIBRE mais ne savez pas envoyer un patch ?

Pour la 3ème année consécutive, les HACKADONS permettent à chacun de contribuer financièrement : nos soutiens financiers mettent de l'argent dans un pot commun que nous distribuons aux participants pour qu'ils en fassent don à des porteurs de projets.

Inscription libre et gratuite à Orléans et Paris, 11 décembre 2015 :

Vous développez un projet LIBRE et n'osez pas demander de l'argent ?

Depuis 2013, une vingtaine de projets ont reçu en moyenne 200€ de dons grâce aux HACKADONS ! Vous avez un projet libre et souhaitez le faire connaître et recevoir des dons ? ou tout simplement des « merci » de la part de vos futurs utilisateurs ? Remplissez notre appel à projet en 2 minutes, et préparer votre présentation !

Vous êtes une entreprise et vous n'avez pas le temps de faire des dons pour tous les projets LIBRES que vous utilisez ?

Les HACKADONS ont pu démarrer grâce au soutien de l'AFUL et l'APRIL, dès 2013, ainsi que des dons de particuliers et d'entreprises.

Cette année encore, nous avons besoin du soutien des associations, des entreprises et des particuliers, tous ceux qui croient avec nous qu'il est indispensable de soutenir l'énergie de celles et de ceux qui fabriquent le libre au quotidien, modestement, qui croient aussi dans les valeurs que nous défendons dans notre manifeste :

Si vous souhaitez nous soutenir financièrement pour contribuer aux dons que les participants feront le 11 décembre prochain, merci de nous envoyer un mot !

Je veux en savoir plus !

Bravo. Pour les comptes-rendus des éditions de 2013 et 2014 :

Les vidéos des présentations faites en 2014 chez Mozilla :

La F.A.Q. pour tout savoir :

  • Contact pour le Hackadon de Paris :
  • Contact pour le Hackadon d'Orléans :
  • Contact général : ou

[afpyro] AFPYro à Nantes - Vendredi 27 novembre

Publié le 2015-11-27 00:00:00

Avant de commencer l’AFPy Camp rien de tel qu’un petit AFPYro pour faire connaissance.

Rendez-vous à 20h vendredi soir devant le 37bis quai de Versailles à Nantes pour partir manger ensemble.

[tarek] Should I use PYTHONOPTIMIZE ?

Publié le 2015-11-25 07:50:00

Yesterday, I was reviewing some code for our projects and in a PR I saw something roughly similar to this:

    assert hasattr(SomeObject, 'some_attribute')
except AssertionError:

That didn't strike me as a good idea to rely on assert because when Python is launched using the PYTHONOPTIMIZE flag, which you can activate with the eponymous environment variable or with -O or -OO, all assertions are stripped from the code.

To my surprise, a lot of people are dismissing -O and -OO saying that no one uses those flags in production and that a code containing asserts is fine.

PYTHONOPTIMIZE has three possible values: 0, 1 (-O) or 2 (-OO). 0 is the default, nothing happens.

For 1 this is what happens:

  • asserts are stripped
  • the generated bytecode files are using the .pyo extension instead of .pyc
  • sys.flags.optimize is set to 1
  • __debug__ is set to False

And for 2:

  • everything 1 does
  • doctsrings are stripped.

To my knowledge, one legacy reason to run -O was to produce a more efficient bytecode, but I was told that this is not true anymore.

Another behavior that has changed is related to pdb: you could not run some step-by-step debugging when PYTHONOPTIMIZE was activated.

Last, the pyo vs pyc thing should go away one day, according to PEP 488

So what does that leaves us ? is there any good reason to use those flags ?

Some applications leverage the __debug__ flag to offer two running modes. One with more debug information or a different behavior when an error is encoutered.

That's the case for pyglet, according to their doc.

Some companies are also using the -OO mode to slighlty reduce the memory footprint of running apps. It seems to be the case at YouTube.

And it's entirely possible that Python itself in the future, adds some new optimizations behind that flag.

So yeah, even if you don't use yourself those options, it's good practice to make sure that your python code is tested with all possible values for PYTHONOPTIMIZE.

It's easy enough, just run your tests with -O and -OO and without, and make sure your code does not depend on doctsrings or assertions.

If you have to depend on one of them, make sure your code gracefully handles the optimize modes or raises an early error explaining why you are not compatible with them.

Thanks to Brett Cannon, Michael Foord and others for their feedback on Twitter on this.

[AFPy Salt-fr] Annonce : Meetup Salt décembre 2015

Publié le 2015-11-16 23:00:00

Le meetup de décembre de Salt se déroulera le jeudi 17 décembre à partir de 19h, autour des sujets suivants :

Comme d'habitude, en fonction du public, nous pourrons faire une courte introduction de Salt en début de meetup.

Le meetup aura lieu chez Vivendi (merci à Julien de Canal+) au 6 rue Tilsitt, 75008 Paris (Metro Charles De Gaulle Etoile).

Le meetup est gratuit mais il faut s'inscrire sur

[AFPy-Nantes] Un petit barcamp en novembre 2015 ?!

Publié le 2015-11-16 23:00:00

La prochaine rencontre Python Nantes sera au format BarCamp et se déroulera le jeudi 26 novembre de 19h à 21h, à la Cantine de Nantes.

L'idée est simplement de se retrouver et de décider sur place des sujets de discussions qui vous intéressent, de les aborder ensemble en différents groupes, puis de mettre en commun ce qui s'est dit pendant les ateliers.

Comme toujours ce meetup est ouvert à tous les amoureux ou curieux du langage Python, nous apprécions particulièrement la diversité des profils qui joignent à nous !

Ceux qui ont envie pourront prolonger la soirée autour d'un verre en centre ville de Nantes.

Si vous avez des questions ou des remarques concernant nos meetups, rejoignez-nous sur le chan IRC de l'AFPy Nantes ou inscrivez vous sur la liste de diffusion . Vous pouvez aussi nous suivre sur Twitter via notre compte @PythonNantes.

À bientôt !

[hautefeuille] Recherche de fichiers doublons avec Golang

Publié le 2015-11-12 22:00:23

On utilise les fonctions de hashage pour comparer les empreintes des fichiers. Les fichiers dupliqués sont déplacés dans un répertoire spécifique.

On lance le programme :

./main -s test -m sha1 -d dupl 
  • s est le répertoire source,
  • m est la méthode de hashage,
  • d est le répertoire contenant les fichiers dupliqués.

Le code du programme est disponible sur ma page Github.

    package main

    import (

    const filechunk = 8192

    func hasher(filename string, method string) string {
        file, err := os.Open(filename)
        if err != nil {
        defer file.Close()
        info, _ := file.Stat()
        filesize := info.Size()
        blocks := uint64(math.Ceil(float64(filesize) / float64(filechunk)))
        var h hash.Hash
        switch method {
        case "sha1":
            h = sha1.New()
        case "md5":
            h = md5.New()
            h = sha1.New()
        for i := uint64(0); i < blocks; i++ {
            blocksize := int(math.Min(filechunk, float64(filesize-int64(i*filechunk))))
            buf := make([] byte, blocksize)
            io.WriteString(h, string(buf)) //append to the hash
        return fmt.Sprintf("%X", h.Sum(nil))

    func marcher(dir string, method string, dest string) map[string]string {
        hmap := make(map[string]string) // {hash:path}
        walker := fs.Walk(dir)
        for walker.Step() {
            // Start walking
            if err := walker.Err(); err != nil {
                    fmt.Fprintln(os.Stderr, err)
            // Check if it is a file
            finfo, err := os.Stat(walker.Path())
            if err != nil {
            if finfo.IsDir() {
                continue // it's a dir so pass and continue
            } else {
                // it's a file so process
                path := walker.Path()
                hash := hasher(walker.Path(), method) 
                search, ok := hmap[hash] 
                if ok {
                     _, filename := filepath.Split(path)
                    if err := os.Rename(path, filepath.Join(dest, filename)); err != nil {
                    fmt.Println("Duplicates moved =>", search)
                } else {
                    hmap[hash] = path
                    fmt.Println(hash, "=>", path)
        return hmap

    func main() {
        source := flag.String("s", "test", "Directory to scan")
        method := flag.String("m", "sha1", "Choose hashing method : md5 or sha1")
        destination := flag.String("d", "doublon", "Choose duplicates destination")
        marcher(*source, *method, *destination)

[hautefeuille] Widgets pour le framework Kivy

Publié le 2015-11-12 22:00:23

Kivy est un framework de développement orienté interface tactile.

J’ai développé quelques widgets dans le cadre de projets.

Le widget Gauge

Il est disponible sur le Kivy Garden.

Kivy Gauge

Le widget Segment

Il est disponible sur le Kivy Garden.

Kivy Segment

Le widget Pizza

Il est disponible sur le Kivy Garden.

Kivy Pizza

Tous les widgets sont disponibles sur le Kivy Garden.

[hautefeuille] Python multithreading et programmation asynchrone

Publié le 2015-11-10 14:39:23

L’objectif est de réaliser un ping vers un ensemble de machines d’un réseau local. Les deux exemples suivants utilisent d’un côté le multithreading et de l’autre la programmation asynchrone. Dans les deux cas, on utilise une file d’attente pour synchroniser les tâches.

Utilisation du multithreading

from threading import Thread
import subprocess
from queue import Queue

num_threads = 32
queue = Queue()
ips = ["", "", ""]

def pinger(i, q):
    while True:
        ip = q.get()
        ret ="ping -w 1 -c 1 %s" % ip,
            stdout=open('/dev/null', 'w'),
        if ret == 0:
            print("%s ALIVE" % ip)
            print("%s DEAD" % ip)

for i in range(num_threads):
    worker = Thread(target=pinger, args=(i, queue))

for ip in ips:


Utilisation de la programmation asynchrone

import asyncio
import shlex
from functools import wraps

q = asyncio.Queue(maxsize=0)

ips = ["", "", ""]

def ban_decorator(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
    return wrapper

def motd():
    print("\t Need a medic now !")

def producer():
    for ip in ips:
        ping_cmd = "ping -w 1 -c 1 {}".format(ip)
        p = yield from asyncio.create_subprocess_exec(* shlex.split(ping_cmd),
            stdout=open('/dev/null', 'w'),
        print("Test IP : {}".format(ip))
        _outs, _err = yield from p.communicate()
        if p.returncode:
            yield from q.put("DEAD : %s" % ip)
            yield from q.put("LIVE : %s" % ip)

def consumer():
    while not q.empty():
        elems = yield from q.get()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()

[hautefeuille] A propos

Publié le 2015-11-10 14:08:21

Ce site est un recueil d’expérimentations techniques. Il permet la mise en commun d’expériences et le partage de connaissances.

L’objectif de ce site est d’être léger, facilement consultable. Il n’est sponsorisé par aucune entité et ne contient aucun tracker.

Vous pouvez me contacter pour me signaler toute erreur technique, typographique ou orthographique.

[AFPy-Nantes] Discussions autour de PyConFR 2015 et relance du meetup

Publié le 2015-11-03 23:00:00

Nous étions une vingtaine à nous réunir pour relancer la dynamiques des meetup python à Nantes ce 28 octobre 2015 dans les locaux de Oasiswork.

Nous avons parlé de manière informelle des conférences et sujets abordés lors de la PyConFR2015, conférence autour du language de programmation python organisée par l'AFPY.

Nous avons parlé et partagé sur de nombreux sujets dont :

  • ansible, salt et le devops
  • bus d'évenements dans salt et dans fedmsg
  • scapy
  • tox, guix-tox
  • l'inevitable docker
  • sqlalchemy, l'ORM de django, pyramid, flask
  • pelican
  • elasticsearch
  • micropython et wipy

De nombreux sujets à explorer lors des prochains meetups. En attendant la publication des vidéos de PyConFR 2015, vous pouvez consulter le programme de pyconfr 2015 ou lire un compte rendu rédigé par Logilab.

Nous nous sommes mis d'accord pour nous réunir à un rhythme d'une fois par mois, alternant entre trois formats : les meetups présentation, les meetups en mode "barcamp" (non-conférence où chacun peut proposer un sujet avec constitution de petits groupes pour parler ou travailler sur ce sujet), et le format afpyro (tout simplement un apéro communautaire dans un bar). L'organisation des présentations se coordonne sur le pad.

Si vous avez des questions ou des remarques concernant nos meetups, rejoignez-nous sur le chan IRC de l'AFPy Nantes ou inscrivez vous sur la liste de diffusion . Vous pouvez aussi nous suivre sur Twitter via notre compte @PythonNantes.

[AFPy-Nantes] Discutons de PyConFR 2015 le mercredi 28 octobre 2015

Publié le 2015-10-22 22:00:00

La prochaine rencontre Python Nantes sera au format BarCamp et se déroulera le mercredi 28 octobre 2015.

Meetup de rétrospective sur PyConFR2015. Venez discuter de ce qui s'est dit à la conférence annuelle sur python qui vient de se dérouler à Pau. Inscrivez vous sur

Nous serons accueillis par Oasiswork au 11 rue du Marchix, Nantes (Arrêt Jean-Jaurès - Tramway 3 - entre Viarme et Bretagne).

Comme toujours ce meetup est ouvert à tous les amoureux ou curieux du langage Python, nous apprécions particulièrement la diversité des profils qui joignent à nous !

Si vous avez des questions ou des remarques concernant nos meetups, rejoignez-nous sur le chan IRC de l'AFPy Nantes ou inscrivez vous sur la liste de diffusion . Vous pouvez aussi nous suivre sur Twitter via notre compte @PythonNantes.

À bientôt !

View Larger Map

[afpyro] AFPYro à Pau - Mardi 20 octobre

Publié le 2015-10-20 00:00:00

Pour finir la PyConFR en beauté, retrouvez nous ce soir à La Tireuse derrière le marché bio (Place du Foirail) pour un AFPYro de clôture.

[sciunto] Don du mois : Framasoft

Publié le 2015-10-17 22:00:00

Ce post s'inscrit dans la série des dons pour vous donner envie de contribuer même très modestement à des logiciels libres. Les petites pierres font les grands édifices.

Framasoft a repris sa campagne sur le "degooglisation" d'internet. Il faut y comprendre

  • acentralisation du réseau internet
  • utilisation de logiciels libres
  • réappropriation des outils
  • respect de la vie privée
  • partage des connaissances

Même si j'utilise peu les outils de framasoft car je m'autohéberge déjà, je ne peux que soutenir leurs actions. J'étais critique envers cette association à l'époque des comptes google, la progression en un an est extraordinaire et ils prouvent qu'une transition rapide vers des outils éthiques est possible.

C'est 25 euros qui vont à l'association qui finance principalement ses salariés.

[afpyro] AFPyro à Marseille - vendredi 16 octobre

Publié le 2015-10-16 00:00:00

Le premier AFPyro à Marseille? Ne ratez pas l’occasion de dire “j’y étais!”.

Cela aura lieu au café-restaurant de la Friche belle de mai.

On pourra discuter d’EuroScipy 2015, de 3.5, et préparer la pyconFR.

[ascendances] hglib, la bibliothèque python pour s’interfacer avec Mercurial

Publié le 2015-10-14 12:55:47
Mercurial, un système de gestion de version décentralisé, propose hglib pour pouvoir agir sur un dépôt programmatiquement depuis Python. Hglib est compatible avec python 2 et 3 donc, à moins de faire de l’archéologie logicielle (python 2.3 et encore plus vieux), il n’y a pas de raison de s’en priver. L’installation est triviale puisque la […]

[AFPy Salt-fr] Compte rendu Salt Meetup Paris - octobre 2015

Publié le 2015-10-07 22:00:00

Les utilisateurs de salt parisiens ont à nouveau frappé, accueillis chez Weborama avec des pizzas offertes par Heuritech. Nous avons pu discuter tranquillement de:

  • Chiffrage au sein des fichiers salt, présenté par Ronan Amicel (Pocket Sensei): plutôt que de devoir sécuriser l'intégralité d'un dépot il est possible d'ajouter au sein des fichiers sls salt des blocs de texte chiffrés par gpg. Ces blocs sont ensuites décodés à la volée par un renderer gpg, ajouté à la suite des renderer yaml et jinja2 sur le master. Seul le master doit donc avoir accès à la clé privée.
  • L'utilisation de peer publish dans Salt par Damien Desmaret (Weborama). Le peer-publish est un des moyens qui permet à des minions de lancer des opérations ou de récupérer des informations sur d'autres minions. Cela permet par exemple de mettre à jour les listes de pairs sur un cluster lors de l'ajout d'une machine. La suite avec le réactor lors d'un prochain meetup.
Meetup Salt octobre 2015

N'hésitez pas à en parler sur la liste de discussion.

[sciunto] Autohébergement d'un gitlab

Publié le 2015-09-29 22:00:00

L'idée de ce billet est d'avoir un github chez soi. Plusieurs raisons à cela :

  • J'ai des dépôts privés. Github propose des dépôts privés payants, mais je n'ai pas confiance dans github pour respecter ma vie privée.
  • J'ai des dépôts semi-privés. Idem à ci-dessus, je veux un cercle restreint.
  • Je ne sais pas quel est l'avenir de github. Google code a fermé, gitorious de même. Je n'ai pas envie de devoir déménager un jour à la va-vite. Voir Et si github fermait.

L'outil git me permet de versionner :

  • mes codes sources (scripts d'adminsys, codes scientifiques).
  • mes articles, rédaction de projets, etc.
  • mes figures.

Je garde des dépôts séparés pour :

  • mes wiki et blog (géré par ikiwiki)
  • mes dépôts git-annex (qui me permettent de synchroniser des fichier dont la masse totale fait plusieurs gigaoctets).

Il faut donc une gestion fine des permission, ce que gitweb ne permet pas (à ma connaissance).

Deux possibilités :

  • gitlab : version entreprise + version communautaire. Des fonctionnalités ne sont pas disponibles dans la version communautaire comme le support de git-annex.
  • Gogs : communautaire, plus jeune et prometteur.

A terme, si Gogs se développe, il sera intéressant. Pour l'instant, je reste sur gitlab qui est une valeur sûre en terme de maturité.

Framasoft ne propose pas (encore) d'explications pour installer gitlab sur sa machine, comme il le fait pour d'autres de ses services sur framacloud. On va donc s'atteler à la tâche (A noter que j'écris la suite de mémoire, je n'ai pas eu de difficultés particulières à la configuration une fois que j'ai su comment j'allais m'y prendre).

On commence à suivre les instructions d'installation sur En effet, l'application est empaquetée, ce qui nous facilite grandement la vie.

Ensuite, on va dans /etc/gitlab/gitlab.rb pour faire tourner notre instance sur le port 8888

external_url ''
# Unicorn
unicorn['worker_timeout'] = 600
unicorn['port'] = 8888
# Web server
web_server['external_users'] = ['www-data']
# On desactive nginx
nginx['enable'] = false
ci_nginx['enable'] = false

On n'oublie pas l'aspect backup, à agrémenter selon l'infrastructure

# For setting up backups

J'ai une instance apache sur ce serveur, je vais donc faire en sorte que apache serve les pages qui sont sur le port 8888. En clair, on mets en place un reverse proxy. Ma configuration est basée sur cet exemple, on n'oubliera pas de modifier le port 8080 pour le 8888. Une liste de modules est présente dans l'en-tête. Ils doivent être activés avec la commande a2enmod.

On reconfigure et relance tout le monde :

sudo gitlab-ctl reconfigure
sudo service apache reload

Après ça, on a un beau gitlab qui tourne.