PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Les articles les plus importants sur Python 6

vendredi 2 janvier 2015 à 07:54

J’ai beau régulièrement pointer vers la section Cours et tutos, je sens bien que les gens n’en bénéficient pas autant qu’ils le devraient.

Une des raisons est la quantité d’info à lire.

Ok, voici donc une sélection d’articles, qui ne sont pas forcément orientés débutants. Ce sont les trucs à maîtriser. Pas tout de suite. Pas tout d’un coup. Mais au final, un programmeur Python doit savoir ça.

La notation de version SemVer 7

jeudi 1 janvier 2015 à 07:52

Il y a des tas de manières d’indiquer la version d’un logiciel.

Ubuntu utilise l’année et le mois, à l’envers : la 14.04 indique une version sortie en avril 2014.

Avant la 1.2, angular utilisait les versions paires pour signifier la stabilité, et impaire pour l’instabilité.

Quelques projets utilisent des numéros de branche Git ou Svn.

Certains utilisent des noms du genre “kangourou-cosmique” pour marquer le coup.

Et d’autres mélangent plusieurs techniques.

En Python, la méthode recommandée est nommée SemVer, pour Semantic Versioning.

Explications

Tout est basé sur la notion d’API publique. Si vous ne vous souvenez pas de ce qu’est une API publique, c’est par ici.

Une bibliothèque versionnée avec SemVer doit clairement documenter toute son API publique pour dire : voici les bouts de codes que vous pouvez utiliser sans risque.

Beaucoup de devs ne font pas, tout comme on fait souvent du REST non pur, on fait souvent du SemVer non pur, mais je vous le dis sinon des puristes viendront jouer de la moustache en commentaires.

Le principe est simple : votre versioning est noté x.y.z, x, y et z étant des entiers positifs. A moins que le nombre soit 0, aucun nombre ne doit commencer par 0.

Le x représente une version majeure, qui introduit au moins un changement incompatible dans l’API publique.

Le y représente une version mineure, qui introduit au moins une nouvelle fonctionnalité dans l’API publique.

Le z représente une correction de bug ou un changement dans l’API privée.

Et c’est tout.

En pratique

Si vous réparez un bug, z augmente de 1.

Ex : 1.3.4 devient 1.3.5

Si vous introduisez un nouveau paramètre optionnel, une fonction outil indépendante, un nouvel attribut public, y augmente de 1, et z est remis à 0 :

Ex : 1.3.4 devient 1.4.0

Si une fonction retourne maintenant ne serait-ce qu’un tuple au lieu de précédemment une liste, x augmente de 1, y et z sont remis à 0 :

Ex : 1.3.4 devient 2.0.0

FAQ

Le numéro de version va monter très vite non ?

On ne change le numéro de version que pour une version publiée. Si vous faites plein de petits changements mais que vous ne déclarez pas ce nouveau code comme la dernière version officielle, pas besoin de changer de version : vous êtes encore en développement.

N’oubliez pas aussi que c’est VOTRE job de vous assurez que votre programme reste compatible. Faites des adapters, des alias, introduisez les fonctionnalités de manière optionnelle, depréciation à l’avance… Il y a des moyens de faire ça proprement.

C’est quand même over chiant pour une lib qui vient de sortir et que personne n’utilise, non ?

Jusqu’à la version 1.0.0, le SemVer ne s’applique pas. En 0.y.z, on peut faire du freestyle.

Mais si je suis en beta ?

On peut signaler des informations supplémentaires en mettant un tiret après son versioning. Le style est libre mais la convention est généralement x.y.z-status. Ex : 1.3.3-alpha, 1.3.3-beta, 1.3.3-rc1, etc.

Attend, je fais une release parfaitement compatible, mais avec une fonction publique qui ne retourne plus 1 mais True, je vais pas bumper pour ça quand même ?

Si. Si vous ne voulez pas le faire, mettez le dans une branche à part, et attendez une release plus grosse pour introduire ce changement.

J’ai une correction de bug qui introduit une changement incompatible dans l’API publique, je fais quoi ?

On bump. Ça n’arrive pas si souvent que ça qu’un bug change l’API publique, faut pas charrier.

Ouais, genre tu le fais…

Non, je ne fais pas du SemVer pur. Mais sur une lib qui est très utilisée, c’est un système indispensable. Si j’avais la responsabilité de pondre Django, ouais, je ferais du SemVer pur.

Ok, je l’ai fais mais j’ai pas l’impression d’y gagner quoi que ce soit.

Le bénéfice n’est pas pour l’auteur, mais pour les utilisateurs. Si un autre utilisateur à votre lib comme dépendance, il peut déclarer dans son fichier setup.py:

requires=['votrelib>=1.3.1<2.0.0']

Ainsi il est certain que ça ne pétera pas si vous faites votre boulot correctement car la lib installée par pip sera toujours dans une version entre la 1.3.1 qu’il sait compatible, jusqu’à la 2.0.0 qui sera incompatible.

IndexError : plateforme de Questions/Réponses pour Python en français 23

mercredi 31 décembre 2014 à 16:34

Quand des gens demandent de l’aide en comments, on les invite à aller plutôt sur des forums. Les commentaires sont pas pratiques pour ça.

La nouvelle qu’elle est cool

Aujourd’hui Max a installé un petit outil (PHP, mais bon, on va pas faire la fine bouche) genre mini-stackoverflow en beaucoup plus simple :

IndexError

Vous pouvez maintenant poser vos questions techniques dessus, ou répondre à celles des autres. Le contenu est placé sous la même licence que le blog, en creative 3.0 unported.

Je vais traîner un peu dessus, et faire ce que je ne pouvais pas faire auparavant, donner un coup de main. Mais ne prenez pas ça pour une ligne directe en SAV gratuit avec Sam. On a plutôt envie qu’une petite communauté s’entraide, SamOS n’est pas scalable pour ce genre de charge.

La clause en tout petit en bas du contrat

L’espace de discussions est uniquement fait pour parler de l’écosystème Python en français. Pas de cul, de Javascript ou autres trucs déviants ;)

Ça laisse quand même de la marge : Blender, Raspberry, Django, Pygame, Twisted, OpenCV, Scipy, Virtualenv, OpenStack, WAMP, ArcGIS… Y a du Python partout !

Le site est lent – et la raison est simple – on est hébergé sur la même petite instance que le blog pour des raisons de budget. Si il commence à y avoir un peu de monde, on migrera le truc sur quelque chose de plus gros. En attendant, on va croiser les doigts et serrer les fesses, espérant que ça nous explose pas à la tronche dès qu’il y a 3 clampins dessus.

Néanmoins, si ça arrive, ça va coûter des thunes. Contrairement au blog qui ne verra jamais un morceau d’AdSense par principe, il faudra bien trouver un moyen de payer ces frais. Donc en gros, si on commence à avoir du traf, y aura sûrement de la pub quelque part. J’annonce avant qu’on se fasse tomater la gueule sur scène… Ce truc n’est pas comme 0bin, ça fait des requêtes en masse, ça envoie de mails, ça consomme quoi.

On sépare donc bien la partie blog, qui reste un espace de liberté où on peut parler de sodomie sans se taper une pop under, et IndexError, quelque chose de plus classique.

Qu’est-ce que les websockets et à quoi ça sert ? 8   Recently updated !

mardi 30 décembre 2014 à 05:51

Le protocole WebSocket vise à développer un canal de communication full-duplex sur un socket TCP.

LOL. C’est clair non ?

Vous inquiétez pas, tonton Sam est là.

Le Web a évolué. On est passé de Gopher a HTTP 1 puis 1.1. Et on a eu AJAX pour rafraîchir la page sans tout recharger.

Et maintenant on a des apps complètes qui font des centaines de requêtes au serveur alors même que l’utilisateur ne change pas de page. D’ailleurs, je parie que plein de gens ne savent même plus ce qu’est une page…

Le problème c’est qu’AJAX, c’est toujours HTTP, et HTTP est sans état (stateless) : il ne garde aucune information en mémoire d’une requête à l’autre. Ça a des avantages, mais cela implique qu’à chaque requête, il faut ouvrir une connexion et la refermer. Ce qui bouffe quelques ms à chaque fois, et d’autant plus si on utilise SSL.

Une autre limite, c’est que le serveur ne peut pas envoyer de données au client (ici le navigateur) si le client ne fait pas une requête au préalable. Du coup, pour savoir si il y a quelque chose de nouveau, le navigateur doit régulièrement faire des requêtes au serveur ou utiliser des gros hacks comme le long polling.

Les websockets (c’est un abus de langage, on devrait parler du protocole Websocket) ont été créés pour répondre à ces besoins : elles permettent d’ouvrir une connexion permanente entre le navigateur et le serveur. Ainsi, chaque requête est plus rapide, et plus légère. En prime, le serveur peut envoyer des requêtes au navigateur pour le prévenir qu’il y a du nouveau.

Ceci permet de faire tout ce que permettait de faire AJAX mais en plus rapide, et en plus léger. Et également d’envoyer des notifications (ce contenu a changé, un message est arrivé, l’autre joueur a fait cette action…) au navigateur au moment où l’événement se produit.

En gros, de faire des apps Web quasi temps réel.

Il existe d’autre technos pour faire cela : applets Java, flash, comet, server sent events…

Mais aucune n’ont décollé. Websocket est donc aujourd’hui la solution de facto.

Caractéristiques

Le protocole Websocket utilise l’abréviation ws et wss si SSL, les URLs vers des endpoints websocket ressemblent donc à : ws://domaine.tld/chemin/vers/truc/.

Intelligemment, il utilise un handshake compatible avec celui de HTTP, permettant à un serveur de gérer les deux sur les mêmes ports. Donc on peut faire du Websocket sur le port 80 et 443. Néanmoins, certains proxy se gourent quand ils voient du websocket non chiffré et gauffrent votre connexion en la traitant comme du HTTP. Donc si vous voulez une app solide, investissez dans un certif SSL.

Tout ça fonctionne à partir de IE10. Notez comme IE est devenu le standard de ce qui ce fait de moins bien à tel point que je n’ai même pas besoin de vous parler des autres, vous savez que ça marche. Il existe en plus des plugins flash pour simuler des websockets sur les navigateurs anciens, c’est à dire les encore plus vieux IE.

Par défaut, les websockets permettent de faire de requêtes crossdomain, contrairement à AJAX. Avec les nouvelles apps qui utilisent NodeJS en local (comme popcorntime) on peut imaginer une nouvelle type d’attaque : une page web qui se connecte à un serveur websocket local de votre machine. Comme les websockets sont souvent utilisées pour du RPC, il y a du potentiel.

Bon, ta gueule, et montre le code

Vous noterez que ce qui prend du temps dans l’exemple c’est la connexion, qu’on ne fait qu’une fois. Ensuite l’échange de données est super rapide.

Ceci est un exemple Javascript, mais un client websocket n’est pas forcément un navigateur. En fait, c’est très précisément le cas avec WAMP, dont les clients peuvent être des programmes Python, Objective C, Java, C++, etc. L’avantage de WAMP, c’est qu’il automatise toute la machinerie pour découper la logique de son programme en divers fonctions et services, plutôt que d’avoir à tout faire à la main avec send() et onmessage().

Dans tous les cas, il vous faudra un serveur qui supporte les Websockets pour l’utiliser. En Python, c’est Tornado ou Twisted (sur lequel est basé le serveur WAMP crossbar). En Javascript, c’est NodeJS. Quoi qu’il en soit, il vous faut un logiciel qui gère l’IO de manière non bloquante, car il y a de nombreuses connexions ouvertes en simultanées, si on veut que ça soit performant.

Bridge HTTP/WAMP 5

lundi 29 décembre 2014 à 11:07

Il y a 4 gros freins à l’adoption de WAMP :

  1. L’incompréhension de la techno. Les gens ne voient pas à quoi ça sert, ce qu’ils peuvent faire avec, et ont peur. Logique. Je vais pas les blâmer, c’est pareil avec toute nouvelle techo. S’investir dans un nouveau truc a un coût, surtout un truc jeune.
  2. La doc. je vais travailler sur la question, mais c’est un travail de fond.
  3. L’API : ça va avec les deux points plus haut. Il faut quelque chose de plus haut niveau. L’API flaskesque est un bon début, il faut maintenant continuer dans ce sens.
  4. L’intégration avec les anciennes technos. Par exemple, un site Django. Personne n’a envie de mettre toute son ancienne stack à la poubelle.

Toute les libs qui introduisent une nouvelle façon de travailler rencontrent ce problème. Quand les ORM sont sortis, c’était pareil. Quand les Django et Rails, et Symfony sont sortis, c’était pareil.

Mais puisqu’on le sait, on peut agir.

Les 3 premiers points ont déjà un début de solution, ce qui nous intéresse c’est donc le 4ème point.

Il y a de nombreuses choses à faire pour l’intégration : authentification, actions bloquantes, communications…

On ne peut pas tout résoudre d’un coup, mais une solution qui ratisse large serait de créer un bridge HTTP/WAMP.

Le principe : faire un client WAMP qui soit aussi client HTTP avec une API REST.

Fonctionnement

En envoyant des requêtes HTTP avec un webtoken pour s’authentifier, on peut faire un register/subscribe/call/publish sur le bridge en spécifiant une URL de callback. Le bridge transmet tout ça à routeur WAMP. Quand un événement arrive sur le bridge qui concerne une des URLs de callback, il fait une requête sur l’URL avec les infos arrivées via WAMP.

API :

POST /register/

{
    // token d'authentification
    token: "fdjsklfqsdjm",
    // On enregistre des urls de callbacks pour chaque "function" exposées en RPC
    // Quand le bridge reçoit un appel RPC via WAMP, il fera une requête POST
    // sur la bonne URL. Votre app récupère les données via POST, et retourne
    // du JSON que le bridge va transmettre via WAMP.
    endpoints: {
        "nom_fonction_1":  "http://localhost:8080/wamp/rpc/nom_fonction_1/",
        "nom_fonction_2":  "http://localhost:8080/wamp/rpc/nom_fonction_2/"
    }
}

POST /subscribe/

{
    token: "fdjsklfqsdjm",
    // On enregistre des urls de callbacks chaque abonnement à un topic.
    // Quand le bridge reçoit un message PUB via WAMP, il fera une requête POST
    // sur la bonne URL. Votre app récupère les données via POST.
    endpoints: {
        "nom_fonction_1":  "http://localhost:8080/wamp/pub/nom_fonction_1/",
        "nom_fonction_2":  "http://localhost:8080/wamp/pub/nom_fonction_2/"
    }
}

POST /call/nom_fonction/

{
    token: "fdjsklfqsdjm",
    // Le bridge fera l'appel RPC, récupère la valeur de retour, et la renvoie
    // à cette URL via POST ou une erreur 500 en cas d'exception.
    callback: "http://localhost:8080/wamp/callback/nom_fonction",
    // Les params à passer à l'appel RPC
    params: ['param1', 'param2']
}

POST /publish/nom_sujet/

{
    token: "fdjsklfqsdjm",
    // Le bridge fera la publication WAMP
    // Les params à passer lors de la publication
    params: ['param1', 'param2']
}

On peut rajouter des fioritures : recharger le fichier de config qui contient les API keys, se désabonner, désinscrire un callback RPC, etc.

Bien entendu, du fait d’avoir un intermédiaire, c’est peu performant, mais suffisant pour permettre une communication entre des apps existantes et vos apps WAMP et mettre un pied dedans.

Intégration poussée

Ceci permet une intégration générique, de telle sorte que tout le monde puisse intégrer son app facilement avec quelques requêtes HTTP. Néanmoins, avoir un plugin pour son framework plug and play faciliterait la vie de beaucoup de gens.

Donc la partie 2, c’est de faire des apps pour les frameworks les plus courants qui wrappent tout ça.

Par exemple, pour Django, ça permettrait de faire :

settings.py

WAMP_BRIDGE_URL = "http://localhost:8181/"

urls.py

from wamp_bridge.adapters.django import dispatcher
 
urlpatterns += ('',
    url('/wamp/, dispatcher),
    ...
)

views.py

from wamp_bridge.adapters.django import publish, call, rpc, sub
 
@rpc()
def nom_fonction_1(val1, val2):
    # faire un truc
 
@sub()
def nom_topic_1(val1, val2):
    # faire un truc
 
def vue_normale(request):
    publish('sujet', ['arg1'])
    resultat = call('function', ['arg1'])

Ca répond pas à des questions du genre : “comment je fais pour garder mon authentification Django” mais c’est déjà super glucose.

Implémentation

On peut créer un bridge dans plein de langages, mais je pense que Python est le plus adapté.

Les clients JS utiliseraientt de toute façon nodeJS, qui n’a pas besoin de bridge, puisque déjà asynchrone. Un routeur en C demanderait de la compilation. PHP est trop moche. Java, c’est tout un bordel à setuper à chaque fois. C#, si il faut se taper Mono sous Linux…

En prime, le routeur crossbar est déjà en Python, donc a déjà tout sous la main pour le faire. On peut même penser à l’intégrer à crossbar plus tard histoire que ce soit batteries included.

Il faut une implémentation Python 2 et Python 3, donc une avec Twisted, et une avec asyncio.

Pour le client twisted, on peut fusionner le client WAMP ordinaire avec treq.

Pour asyncio, il y a aiohttp qui fait client et serveur.

On met les clés API dans un fichier de conf ou une variable d’env, une auth via token, et yala.

C’est le genre de projet intéressant car pas trop gros, mais suffisamment complexe pour être un challenge surtout qu’il faudra des tests unitaires partout, de la doc, bref un truc propre.

Je laisse les specs là, des fois qu’il y ait quelqu’un qui ait des envies de code cet hiver et cherche un projet open source dans lequel se lancer.

Si on se sent un peut foufou, on peut même transformer ça en bridget HTTP <=> anything, avec des backends pour ce qu’on veut : IRC, XMPP, Redis, Trigger Happy…