Site original : Sam & Max: Python, Django, Git et du cul
Les gens derrière le développement de Python sont extraordinaires : Python existe depuis 1990, cela fait donc plus de 20 ans qu’il est là, à évoluer, sans cesse.
Et cela a un coût. Un langage qui permet à Youtube, Dropbox et Instagram de tourner, ça doit douiller non ?
Et bien, la partie fun, c’est que le plus gros du boulot est fait par des volontaires. En fait, le budget total alloué par la Python Software Fundation pour payer des dev à travailler sur le langage est moins de 30 000 euros par an. Même pas le salaire d’un ingé sénior. Parce qu’ils n’ont pas de pognon.
Alors, on peut se gargariser que, trop cool, la communauté Python contribue à mort et ça coute pas cher. Mais moi ce que je vois, ce sont des centaines de mecs qui bossent super dur, gratos, pour qu’on puisse en bénéficier d’un côté, et de l’autre, une fondation qui n’a pas de thune pour aller au bout de sa mission.
La honte, c’est bien entendu que des boites comme Google, Apple ou Sony, qui sont blindées, ne renvoient pas l’ascenseur alors qu’ils utilisent le langage en interne.
Mais ça, on peut rien y faire dans la seconde.
Ce qu’on peut faire tout de suite, en revanche, c’est filer 10€ – 100€, nous, en tant que dev, ou boite. Si vous utilisez professionnellement Python, je pense que sur toute une vie, c’est raisonnable.
Je hais la culpabilisation, donc l’idée n’est pas de vous dire “si vous ne donnez pas vous êtes des connards”. Simplement je donne régulièrement aux projets que j’utilise : VLC, Ubuntu, LibreOffice, etc. Et Le blog fait la promotion de Python, alors il est juste que je vous invite à envoyer des sous à la PSF.
Ce n’est pas une question d’être une bonne personne. C’est juste un bon moyen d’aider au dev d’un outil qu’on aime, pour en bénéficier demain.
Dommage, ils n’acceptent pas les bitcoins.
On m’a demandé de rendre possible téléchargement des articles en PDF.
Je suis paresseux, donc plutôt que d’installer un plugin, j’ai juste fait un lien vers un service externe en haut à droite de toutes les pages.
Si vous cliquez, ça vous génère le PDF pour la page complète, et ça marche pour toutes les URL, pas uniquement les articles.
De rien.
Je sais, je sais, je vous fais chier avec crossbar et autobahn.
Mais ça me tue de ne pas voir plus de monde exploiter cette techno.
Pendant que Max fait la sieste, j’ai pris mon stylo et j’ai fait la liste des besoins d’une app Web actuelle. Quels sont les composants qu’on utilise presque systématiquement, mais en agrégeant divers bouts de trucs à droite et à gauche ?
Ensuite j’ai regardé les possibilités des outils WAMP :
M’inspirant de cela, et du travail que je suis en train de faire avec l’équipe de Tavendo pour faire une API flaskesque pour autobahn, j’ai prototypé une API d’un framework Web qu’on pourrait coder au dessus de cette techno.
Voilà ce que ça donne…
Une API qui mélange flask et nodejs pour le Web
app = Application('YourProjectName') # Envoyer et recevoir des requêtes HTTP @app.http.post(r'/form') def _(req, res): res.json({'data': 'pouet'}) @app.http.get(r'/user/:id/') def _(req, res): res.render('index.html', {'data': 'pouet'}) # Servir des fichiers statiques @app.http.serve('uri', '/path/to/dir', [allow_index]) app.run()
Comme c’est asynchrone, on a de très bonnes perfs. Comme c’est basé sur Twisted, on a pas besoin d’un serveur wsgi (gunicorn, uwsgi, etc) ni d’un proxy (nginx) devant. On peut le mettre en prod tel quel.
Parti de ce principe, on peut ajouter la gestion du PUB/SUB et du RPC pour WAMP :
# Callback attendant l'événement @app.wamp.event('auth.signedin') def _(ctx, a, b, c): pass # déclenchement de l'événément app.wamp.pub('auth.signedin') # Déclaration du fonnction appelable à distance @app.wamp.remote('auth.signin') def _(ctx, a, b, c): pass # appel de la fonnction app.wamp.call('auth.signin')
On est souvent perdu quand on fait de l’asynchrone pour la première fois avec Python car on ne sait pas comment lancer du code après .run()
. On peut régler la question proposant des hooks pour les instants clés de l’app.
# Callback à lancer quand l'app est prête @app.on('app.ready') def _(ctx, args): pass # Signalement que l'app est prête (fait automatiquement en interne # pour les moments les plus importants) app.emit('app.ready')
Et tant qu’on y est, puisqu’on a une event loop, profitons en pour proposer du CRON intégré à l’app. C’est moins chiant à déployer qu’un script CRON, c’est cross plateforme, et on a accès facilement à toute sa stack.
# Lancer du code tous les x temps ou a une date précise @app.cron(every=seconds) @app.cron(every=timedelta, overlap=False) @app.cron(hour=7, minute=30, day_of_week=1) @app.cron(when=datetime) def _(ctx, args): pass
Pourquoi s’arrêter là ? Event loop + message passing + safe queues + workers = tasks queues !
# Créer une file d'attente queue = @app.queue('name', [workers], [result_backend]) # Callback appelé par un worker quand il depop ce # message dans la file @queue.task('encode.video') def _(ctx, data): pass # Envoie d'une tache dans la queu queue.append('encode.video', data)
Comme on utilise Twisted, on a accès à une chiée de protocoles, et on peut aussi créer les siens. On peut donc imaginer un système de plugins qui rajoute des protocoles supportés :
app = Application('YourProjectName') app.plug('lib.ajoutant.sms', [namespace])
Si on en a beaucoup et que le namespace nous convient :
app = Application('YourProjectName', plugins=('lib1', 'lib2', 'etc'))
Exemples de plugins possibles :
# Recevoir et envoyer des SMS (via un service type twilio, une gateway kannel ou # un modem physique) @app.sms.receive(r'LOVE \w+ \w+') def _(ctx, args): pass app.sms.send('test', [contact]) # Envoyer et recevoir des emails (via un server SMTP ou IMAP) @app.email.receive(src=r'.*@sametmax.com', dest=r'spam.*@*.') def _(ctx, args): pass app.email.send('test', [contact, title, attachments]) # techniquement n'importe quel service de message pour lequel on peut écrire # un backend @app.tweet.receive(r'Chat') @app.fb.receive(r'Like') @app.instagram.receive(r'Bouffe') @app.irc.message(r'dtc') def _(ctx, args): pass
Le problème des apps centrées sur un objet, c’est qu’elles ont souvent un design monolithique. Ce n’est pas un problème du concept d’app, c’est juste que les auteurs ont pensé “point d’entrée”, et pas “élément composable”.
Si besoin, on doit pouvoir composer une app via plusieurs sous-app :
app = Application() app.embed('autre.app')
ou
app = Application(embed=['app1', 'app2', 'app3'])
Il faut des hooks pour overrider la configuration, mais vous avez compris le principe.
Un autre problème avec les plateformes comme NodeJS, c’est qu’il est difficile d’utiliser plusieurs coeurs. C’est une des raisons du succès de Go.
Or, Crossbar encourage la division en plusieurs process qui communiquent entre eux (un peu comme les channels). Créons aussi une API pour ça :
p1 = app.process() p2 = app.process() # Déclarer et appeler une procédure dans process 1 @p1.wamp.remote('auth.signin') def _(ctx, args): pass # Déclarer et appeler une procédure dans process 2 @p2.wamp.event('auth.signedin') def _(ctx, args): pass
Ainsi on profite enfin de plusieurs CPU. La même chose en plus facile à changer:
# Déclarer et appeler une procédure @app.wamp.remote('auth.signin') def _(ctx, args): pass # Déclarer et appeler une procédure @app.wamp.event('auth.signedin') def _(ctx, args): pass app.processes({ 1: ['wamp.remote:auth.signin'] 2: ['wamp.event:auth.signedin'] })
En bonus, on fait la nique au GIL.
Mieux, on peut bouger ses process sur plusieurs machines :
Machine 1 (routeur):
router = Application(endpoint="0.0.0.0:8080") router.run()
Machine 2 (authentification):
# IP du router auth = Application('auth', connect_to="182.64.1.15:8080") # Nommage automatique en fonction du nom de la fonction # et de l'app, avec possibilité d'annuler ou overrider le prefix. # Ici du coup la fonction s'appellera en RPC via 'auth.signin' @auth.wamp.remote() def signin(ctx, args): pass auth.run()
Machine 3 (API REST):
web = Application('site', connect_to="182.64.1.15:8080") @web.http.post(r'api/auth/') def _(req, res): user = yield res.wamp.call('auth.signin', req.POST['username'], req.POST['password'])* if user user = yield res.wamp.pub('auth.signedin', user.userid) res.json({'token': user.token}) else: res.json({'error': 'nope'}) @web.http.get(r'api/stuff/') def _(req, res): res.json(get_stuff()) @web.http.serve('uri', '/path/to/dir', [allow_index]) web.run()
Et vous savez le plus beau dans tout ça ? En Python on a plein de libs qui sont encore bloquantes. En théorie on ne peut pas les utiliser dans les apps asynchrones. Quand on a toute sa logique métiers dans des classes d’ORM, c’est balot. Mais pas ici ! On met un process avec tous ces appels bloquants, et on les appelle depuis des process non bloquant en RPC de manière asynchrone. Pif, paf, pouf, problème isolé.
Après, libre à son imagination de rajouter des fonctionnalités de confort…
Callback qui sera appelé seulement x fois :
# Déclarer et appeler une procédure @p1.wamp.event('auth.signedin', options={'limit_calls': x} ) def _(ctx, args): pass
Raccourcis pour les opérations courantes :
# Recevoir et envoyer un événement @app.sub('auth.signin') def _(ctx, *args): # ctx.pub @app.pub('auth.signedin') # Déclarer et appeler une procédure @app.proc('auth.signedin') def _(ctx, args): # ctx.call app.rpc()
Comme je vous l’avais expliqué, crossbar peut gérer le cycle de vie de services externes à votre application au démarrage. Autant exposer cette API programativement :
@app.service(['/urs/bin/nodejs', 'script.js'], [user], [group])
.run()
, c’est cool, mais si on veut changer des options via la ligne de commande, faut se taper tout le boulot alors que ça pourrait très bien se générer automatiquement :
@app.cmd_run()
Et si vous faites : python sites.py --debug=true --endpoint=0.0.0.0:5252
, ça le prend automatiquement en compte. Y a pas de raison de se faire chier.
En parlant de générer automatiquement des trucs, le fichiers de configs pour les services externes sur lesquels on peut avoir envie de brancher notre app, c’est toujours galère. Autant fournir un exemple de base qui est sûr de toujours marcher, généré avec les paramètres de notre app :
python site.py template centos:nginx
python site.py template ubuntu:upstart
python site.py template bsd:systemd # :D
On peut partir très loin dans le délire “battery included”. Typiquement, on peut fournir des services externes nous même puisque crossbar nous le propose, et coder des versions moins bien, mais compatibles (et suffisantes pour les petits sites), de projets toujours utilses :
changement de valeur
On plug tout ça a une admin Web.
J’espère que je vous ai donné maintenant l’envie de vous plonger un peu plus dans cette techno, et peut être coder quelque chose avec.
Il n’y a plus d’excuses pour ne pas avoir de framework web next gen, ultime de la mort qui tue en Python. A part le fait qu’on soit des feignasses.
Ah, merde, on est foutus.
Je pense que vous vous êtes TOUS fait rabâcher que les préliminaires c’est important qu’il faut s’occuper de l’autre, le sexe c’est du partage, et gna gna gna, bla bla bla, trolololo lololo lololo.
Le truc c’est que ce genre d’arguments ne va parler qu’à des gens qui sont DÉJÀ de nature à en faire. Prêcher des convertis, c’est bon pour l’égo mais ça construit pas grand chose. C’est pas bon comme Lego. Ok, je => [].
Bref, voici 10 bonnes raisons de faire des préliminaires, parce que ça vous rapporte quelque chose à vous.
Passé une certaine durée d’érection, le corps s’adapte pour passer en mode endurance. Du coup plus de préliminaires = plus de temps à bander sans trop monter en sauce = plus de temps à bander tout court.
Donc plus de baise. Des performances plus flatteuses. Le temps d’essayer plus de positions.
Et puis plus d’opportunité de la faire jouir, puisque ça prend souvent plus de temps que pour nous. Et franchement, qu’est-ce qu’il y a de plus sexy qu’une meuf à qui on est en train de faire prendre son pied ?
Les meufs parlent entre elles. Si vous êtes un bon coup, ça se saura : un secret, c’est quelque chose que l’on ne répète qu’à une personne à la fois. Du coup, rapidement, les copines des copines (un degré de séparation est nécessaire le plus souvent pour en bénéficier) vous voient comme un partenaire sexuel valable.
Les préliminaire impliquent souvent de tenir des positions qui font travailler vos muscles, votre cœur, votre respiration. Plus il y a de préliminaires, plus votre séance de gym en l’air devient complète.
Franchement, garder la forme en niquant, y a pire.
Plus de transpiration et plus de caresses, plus de frictions donc un scrubing + hammam gratos. Votre peau va s’en trouver toute renouvelée.
Or, scoop, la peau est un facteur hyper important de sex appeal. En fait, avoir une belle peau compense beaucoup d’autres problèmes. Par exemple, si vous êtes en surpoids avec un superbe épiderme, vous paraissez en bonne santé et donc baisable, si vous avez une super silhouette et une peau de merde, vous donnez autant d’envie qu’un lépreux.
Désolé pour les gens à peau difficile, je connais ça, j’ai été victime de tout un tas de trucs dermatologiquement inesthétiques.
Si vous aimez lui gicler dans la bouche, le cul ou sur les nichons, avoir bien fait monter en température la demoiselle aide beaucoup. Dans le feu de l’action, on accepte beaucoup plus de choses. La sodomie, douloureuse à froid, peut devenir super excitante quand elle est à fond. Avaler se fera parfois naturellement si vous lui avez léché copieusement la touffe avant. Etc.
Ça stimule la libido. Et donc si vous avez une fille qui à pas souvent envie, tout d’un coup, paf, elle répond vachement mieux aux sollicitations. Merci capitaine obvious, tu peux refermer ta braguette maintenant.
Ça va avec les deux précédents, mais si vous faites frémir votre partenaire, elle sera forcément plus en confiance pour mettre un costume d’écolière et s’empaler sur une tentacule en plastique tout en criant “Yameru, Kyōju-Sama !”.
Il faut dire que le cul, ça peut facilement tourner en rond. Du coup, faire des trucs funs et osés en entrée, ça amène un peu de renouveau.
Franchement, c’est toujours mieux d’enfiler le bout de plastique quand on a sa langue dans sa chatte que de s’arrêter pour ça.
Je me pose toujours la question.
Envie de venir en deux minutes ? Vous pouvez, vous lui avez révolutionné la Sardaigne hier ! C’est le moment de la monter directement sur la table basse, lui baisser le fut de 20 cm, venir, et repartir comme si de rien n’était.
Enfin, presque :)
J’étais en train d’installer en vieux requirements.txt quand soudain :
File " /usr/lib/python2.7/dist-packages/pip/download.py", line 23, in <module> from requests.adapters import BaseAdapter ImportError: No module named adapters
Wut ?
Mon erreur était due à une version de requests trop ancienne. J’upgrade et ça remarche. Mais ça m’a intrigué :
⟩ grin "requests" /usr/lib/python2.7/dist-packages/pip/ /usr/lib/python2.7/dist-packages/pip/download.py: 22 : import requests, six 23 : from requests.adapters import BaseAdapter 24 : from requests.auth import AuthBase, HTTPBasicAuth 25 : from requests.compat import IncompleteRead 26 : from requests.exceptions import InvalidURL, ChunkedEncodingError 27 : from requests.models import Response 28 : from requests.structures import CaseInsensitiveDict 120 : # Store the new username and password to use for future requests 182 : # We only work for requests with a host of localhost 211 : class PipSession(requests.Session): 548 : except requests.HTTPError as exc: /usr/lib/python2.7/dist-packages/pip/index.py: 17 : import html5lib, requests, pkg_resources 18 : from requests.exceptions import SSLError 80 : # The Session we'll use to make requests 689 : except requests.HTTPError as exc: 692 : except requests.ConnectionError as exc: 697 : except requests.Timeout:
Je regarde le code source github, qui diffère de celui de mon système :
from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth from pip._vendor.requests.compat import IncompleteRead from pip._vendor.requests.exceptions import ChunkedEncodingError
Mais ça reste bien requests.
Donc, si vous avez la 3.4, vous avez pip installé, qui maintenant embed requests. Donc Python 3.4 vient forcément avec requests \o/
Si vous supportez uniquement cette plateforme, vous pouvez faire :
try: import requests except ImportError: import pip._vendor.requests as requests
Et bénéficier de requests sans rien installer.
Tout ça pour ça.