PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Quel niveau peut-on exiger à l’embauche en Python ?

mardi 8 juillet 2014 à 09:19

J’ai répondu récemment à une question sur le niveau qu’on pouvait attendre d’un professionnel en Python. La réponse était en anglais, alors je me fends d’une petite traduction ici.

Cela dépend beaucoup du niveau Python dont la boîte a besoin, et s’ils accordent plus d’importance à celui-ci ou à ta capacité générale à résoudre des problèmes et tes connaissances générales informatiques.

Pour répondre à ta question, je vais ignorer ces deux points puisqu’ils ne sont pas particulièrement liés à Python. Il y a plein d’articles de blog sur ces sujets sur le Web.

En Python, un dev standard devrait :

  • être capable d’écrire du code Pythonique (style idiomatique, syntaxe PEP8…).
  • connaître la stdlib et l’écosystème de Python. Par exemple n’avoir aucun problème avec pip, virtualenv, datetime, os.path, hashlib, uuid, csv, pdb, json, et toutes les collections y compris celles qu’il y a dans le module éponyme ou les sets.
  • connaître les bases de la POO. L’héritage, l’overloading, les propriétés, la composition, etc.

Un développeur Python avancé devrait être capable d’écrire des libs agréables, et donc :

  • être à l’aise avec les générateurs et yield.
  • pouvoir écrire son propre décorateur.
  • pouvoir écrire son propre context manager.
  • pouvoir écrire son propre descripteur.
  • savoir gérer l’héritage multiple, l’injection de dépendance, etc.
  • pouvoir faire une lib de A à Z, incluant un API flexible et extensible, le packaging et la documentation.
  • savoir mettre en place des tests unitaires.

Un expert Python devrait :

  • connaître la plupart des libs tierces parties et frameworks pour la plupart des tâches, leurs qualités et leurs défauts.
  • pouvoir créer sa propre métaclasse.
  • être à l’aise avec l’introspection.
  • savoir contourner la plupart des faiblesses de Python : le GIL, les API bloquantes, créer soit-même un code non bloquant, créer un code compatible V2 et V3, etc.
  • être capable de prendre un projet sans doc, lire le code source, trouver son problème, et le résoudre via monkey patch tout en proposant un patch propre upstream.
  • savoir utiliser des modules difficiles ou peu connus comme heapq, inspect, nmap, etc.

Selon ton domaine, tu auras également peut-être besoin d’être capable :

  • de gérer plusieurs implémentations de Python.
  • d’écrire une extension C.
  • de maîtriser des libs spécifiques comme numpy, scrapy, opencv, etc.

Vous noterez que je n’ai pas mis des évidences comme savoir faire une fonction ou une boucle for. Évidemment qu’il faut savoir écrire du code basique sinon on ne sert à rien.

Gardez aussi en tête que chaque entreprise est différente, et donc peut avoir des besoins qui se situent n’importe où dans l’échelle ci-dessus.

Mais surtout, les connaissances dans un langage, bien qu’importantes, ne sont pas la priorité. Un langage, ça s’apprend. Être une personne qui résout les problèmes, communique, travaille bien en équipe et satisfait les clients, c’est plus difficile à former.

Par ailleurs, il n’existe pas de job avec juste du Python. Un dev backend devrait sans doute aussi travailler avec une base de données, faire du sys admin ou du réseau. Un dev d’UI devra gérer des problématiques multi-plateformes, de l’ergonomie, du packaging avancé. Un dev scientifique devra peut-être écrire du C ou du Fortran. Un dev Web est tenu de gérer le JS/CSS/HTML. Et il y a aussi les outils autour : bug tracking, versioning, peer review, etc.

Bref, dire seulement “je suis bon en Python” ne suffit pas à convaincre quelqu’un qu’il doit vous embaucher.

flattr this!

Oblitérer un fichier de l’historique Git

lundi 7 juillet 2014 à 15:24

Si vous avez commité votre clé privée dans un repo, il existe un moyen de la dégommer de l’historique.

ATTENTION, CECI EST UNE OPERATION DANGEREUSE.

git filter-branch -f --index-filter "git rm -r --cached --ignore-unmatch chemin/vers/fichier" --prune-empty --tag-name-filter cat -- --all

Si le fichier a été dans plusieurs endroits durant sa vie, il faut relancer la commande pour chaque chemin.

Puis :

git push remote branch --force

Cette seconde commande est à lancer pour chaque branche et chaque tag concerné.

Comme tout ce qui change l’historique, il faut que tous les autres repos appliquent aussi cette opération.

Tous vos collègues devront, soit appliquer eux aussi “filter-branch”, soit faire un rebase de toutes les branches basées sur celles modifiées par vous.

La commande peut prendre pas mal de temps à tourner. L’opération totale avec syncro entre collègues, je ne vous en parle pas. Et si vous foirez une étape, je vous garantis que ça va être la merde.

Mais c’est possible, en cas d’extrême urgence.

Pour se simplifier la tâche, il existe un petit soft spécialisé pour ça qui est bien plus rapide et avec moins de risques.

Mais ça reste dangereux, donc mollo.

flattr this!

Little things

samedi 5 juillet 2014 à 06:04
Terminer un stylo bic ou une gomme.

Éclater du papier bulle. Dont quelqu’un a besoin.

Passer une porte en train de se fermer sans la toucher.

Connecter un périphérique USB dans le bon sens d’un seul coup.

Vider une bouteille et remplir pile poil un verre avec. Ni trop, ni pas assez.

Marcher uniquement sur les bandes blanches du passage clouté sans changer de rythme.

Avoir un ascenseur qui s’ouvre et se ferme exactement calqué sur la basse du morceau qu’on écoute.

Coder un snippet d’une traite, sans regarder la doc, et l’exécuter pour voir que ça marche exactement comme prévu.

Mais dans notre métier, rien ne bat le sentiment jubilatoire de gagner 5 secondes en utilisant les raccourcis de son éditeur.

flattr this!

Le merci du jour

jeudi 3 juillet 2014 à 03:30

Régulièrement je relis les commentaires et je me dis qu’on a quand même un public en or.

Alors, je me repète, mais merci.

On a peu de trolls ou de connards.

On a des relecteurs qu’ils sont cool.

Et je note aussi la patience que vous avez à me corriger quand j’écris des conneries. Avec beaucoup de politesse et de cordialité. Et pas juste pour les typos.

J’apprécie également les débats qu’on peut avoir, qui peuvent monter en sauce sans jamais exploser.

Merci, donc.

flattr this!

WAMP et les outils de dev Web Python existants

mercredi 2 juillet 2014 à 07:21

Même si on peut créer un site Web en utilisant uniquement des libs WAMP, tout comme on peut le faire en utilisant uniquement flask ou tornado, il arrive immanquablement le moment où on veut mélanger, intégrer, faire cohabiter les techos ensemble. D’abord parce qu’il y a des sites existants, et qu’on va pas les jeter à la poubelle. Ensuite parce que ce sont des techos qu’on connaît et pour lesquelles ils y a beaucoup d’outils, que l’on veut mettre à profit.

C’est tout naturellement qu’on a fini par me poser (par mail), ze question :

Subject: crossabar et autobahn

Message Body:
Ha bah oui vous l’avez cherché à nous parler de truc comme ça: ça interroge !

Je m’interroge donc sur la manière d’intégrer WAMP à django. Pour la perstistence des données (l’ORM qui simplifie la création des tables tout de même), pour l’authentication et l’authorization, pour la robustesse et versatilité apportée par django…

J’ai pour habitude de mettre pas mal de logique dans mes modèles et je me demandais si il n’y aurait pas moyen de pluguer WAMP dans ceux ci… exposer une méthode update en RPC avec passage de JSON ? Avec namespace automatique à partir de la classe ?

En fait remplacer: Angular+tastypie+django+nginx par Angular+WAMP+django+crossbar avec du coup le bonus de WAMP pour le pubsub que n’a pas AJAX.

Comment vous verriez un (petit – après relecture ça parait dur) mixin WAMP pour des modèles django auto-détectés, auto-exposés en RPC ?

J’ai du mal à voir quelle tactique utiliser. J’ai peur que ça finisse en refaire tout le travail que fait déjà (très bien) tastypie/RESTframework.

Comment modulariser (là ou est sensé briller WAMP) les services déjà offerts par django: celery, authentication, etc ?
Ces services sont tous très dépendant du système de persistance des données (check des users, permissions, query) et donc l’approche plus monolithique de django n’est pas mauvaise car tout est lié et donc facilement manipulable au même endroit… où est le gain de WAMP dans ce cas, pour une application assez classique en fait ?

Merci encore pour cette découverte dans tous les cas. C’est top.

Du coup je me relis et ça part un peu dans tous les sens… désolé.

Comme je me suis fendu d’une réponse bien longue, je vais la paster verbatim :

Bonjour,

C’est une très bonne question.

D’abord, il faut savoir qu’on ne peut pas faire tourner un routeur WAMP dans un process Django (ou tout autre app WSGI) car Django est synchrone. En plus, l’ORM de django est bloquant, donc même sans utiliser django, utiliser son ORM au sein de WAMP va bloquer la boucle d’événements et on perdra tout l’interêt d’avoir une techno temps réel.

(Note a posteriori : y a surement un truc à faire avec gevent ou des threads ici, mais je sais pas encore quoi)

Ici on a donc 3 problèmes à résoudre :

- comment faire communiquer django et son app WAMP ?
- comment utiliser un ORM bloquant avec WAMP ?
- comment auto générer une API WAMP ?

Ces 3 questions n’ont pas encore de réponse définitive puisque, comme je l’ai précisé, WAMP est une techno jeune, et donc il y a beaucoup à faire. Mes articles sont précisément là pour tenter de générer un enthousiasme et pousser les gens à améliorer les outils autour de WAMP.

Prenons les problèmes un par un :

Comment faire communiquer django et son app WAMP ?
====================================

C’est le problème le plus facile à mon sens. Il faut coder une app WAMP qui fasse le bridge entre HTTP et WAMP. Quand on register côté app HTTP, on fait un post sur l’app WAMP (qui écoute aussi sur HTTP du coup) en fournissant une URL de callback. L’app WAMP fait le register, et quand on l’appelle, elle fait l’appel à l’app HTTP via l’url de callback, et retourne le résultat. On peut faire ça pour register, subscribe, call et publish, c’est le même principe.

(Note a posteriori : en me relisant moi-même je m’aperçois à quel point c’est pas clair. De toute façon il faudra que je le code un jour où l’autre, et avec un bon tuto pratique, la pilule passera mieux).

Ce faisant, on pourra appeler du WAMP côté app HTTP, et taper dans l’app HTTP côté client WAMP.

Une amorce de travail a été fait pour coder un tel bridge. Pour le moment il n’y a que le publish :

https://groups.google.com/forum/#!searchin/autobahnws/http/autobahnws/SbobAnoWVlQ/FnGhdYXj9aIJ

Ce n’est pas très dur à coder, c’est juste un boulot chiant à faire.

Cela dit, ça ne résout pas le problème de l’authentification, qu’il faudra à un moment on un autre, se poser. Je pense qu’on va se diriger vers une authentification hybride, qui va utiliser le session ID en cookie, mais l’envoyer via un token. Encore un truc à travailler.

De même, on voudra sûrement créer quelques facilités pour intégrer ça dans les frameworks les plus connus en proposant une app prêt à plugger. Rien d’insurmontable donc, mais pas mal de taff.

Par contre, pour ce qui est des tasks queues, à mon avis une solution de task queue WAMP sera bien plus intéressante qu’une solution type celery car on peut envoyer des messages WAMP depuis les tâches et donc avertir en temps réel de l’avancement du process. Je voterais donc pour coder soi-même une alternative.

Comment utiliser un ORM bloquant avec WAMP ?
===============================

Idéalement, il faudrait avoir des ORM non bloquant, mais on Python, on en a pas. On a quelques drivers non bloquant, notamment pour PostGres et Mongo, mais pas d’ORM, et ils demandent une forme de compilation d’extension C.

C’est là qu’on voit qu’on se traine la culture de l’API synchrone en Python, car côté NodeJS, ils commencent à avoir pas mal de solutions.

En l’occurrence, on a 3 solutions :

- utiliser le bridge dont je viens de parler pour garder les appels dans l’app HTTP. Ca veut dire que quand on veut faire un appel à de la base de données, ça fait WAMP => HTTP => connexion à la base, aller-retour. C’est pas idéal.
- créer une app WAMP pour héberger les appels bloquants et taper dedans en RPC. Une bonne solution à mon avis. Mais assez peu intuitive.
- faire tous les appels dans un threads à part. Le plus simple. Un peu verbeux par contre.

Dans les deux derniers cas, on à quand même le problème des querysets qui sont lazy, notamment au niveau des foreign keys. Il faudra faire particulièrement attention à ne pas accidentellement faire des appels bloquant, par exemple dans le rendu du template. Une solution viable est de créer un wrapper qui fait le rendu du template dans un threads.

Bref, encore pas mal d’outils à developper.

On peut aussi se lancer dans l’écriture d’un ORM non bloquant. Une bonne année de travail avant d’avoir quelque chose qui soit compétitif.

Comment auto générer une API WAMP ?
==========================

Là tu m’en poses une bonne.

C’est la suite logique, évidement, mais je n’avais jamais réfléchi aussi loin. C’est un taff énorme, surtout que ça dépend de l’outil derrière. La solution la plus simple c’est encore de faire un mapper dans le bridge HTTP-WAMP qui va traduire directement un appel WAMP en un appel JSON vers l’API générée par django-rest-framework ou autre.

Mais bon, je suis pas certains de la valeur ajoutée.

Je pense qu’il est difficile pour moi de répondre à cette question pour le moment car :

- je ne suis pas certain que WAMP soit un bon remplacement pour les API REST. Je pense plutôt que c’est un complément.
- il y a toute la question de l’authentification. Encore et toujours.
- il va falloir pas mal d’essais avec plusieurs architectures en prod (séparées, mixtes, mono culture…) pour pouvoir déterminer ce qui rend le mieux.

Mon intuition est qu’on utilise généralement 10% de l’API générée par les frameworks, et que la partie dont on a besoin à peut très bien se faire à la main. La raison pour laquelle les trucs comme django-rest-framework sont si pratiques, c’est qu’ils gèrent des problématiques comme l’authentification, la sérialisation et la pagination.

Je serais plutôt d’avis de s’attaquer à ça pour WAMP, et je pense qu’on s’apercevra que finalement, pour ses propres besoins, un API complète est overkill. Par contre, pour exposer une API au monde, c’est une autre histoire. J’ai eu récemment une discussion à propos de faire des APIs WAMP :) Il y a des possibilités fascinantes. Mais c’est peut être encore un peu loin tout ça.

Je pense que je vais publier cette réponse sur le blog, car tu soulèves des points très importants.

flattr this!