PROJET AUTOBLOG


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

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

⇐ retour index

Écrivez à l’arc, envoyez des sioux

mardi 3 juin 2014 à 05:21

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 ?

Photo de personnes déguisés en télétubies fumant et buvant

Youtube, Dropbox et instagram consomment toujours beaucoup

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.

Photo d'un homme assi sur un fauteuil luxueux avec un catana et une peau de tigre

Ouai c'est Google. Ouai. Ouai. Nan rien à foutre.

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.

Photo d'un chat mençant un chien avec un couteau

Par contre le chantage ne me gène pas. Si vous ne donnez pas, je posterai une photo de chaton par jour dans la rubrique cul de sam et max. Ouai, nan, c'est plus dur pour moi que pour vous en fait.

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.

Faites pêter l’oseille

Dommage, ils n’acceptent pas les bitcoins.

flattr this!

Télécharger une page du blog en PDF

lundi 2 juin 2014 à 12:14

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.

flattr this!

Le potentiel de WAMP, autobahn et crossbar.io

dimanche 1 juin 2014 à 12:09

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 :

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.

flattr this!

10 raisons égoïstes pour s’investir dans les préliminaires

vendredi 30 mai 2014 à 05:17

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.

1 – On dure plus longtemps

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 ?

2 – On chope plus

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.

3 – On est en meilleure forme

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.

4 – On a une plus belle peau

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.

5 – Plus de chance de pouvoir choisir où vous venez

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.

6 – Plus de sexe avec la même partenaire

Ç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.

7 – Plus de jeux pervers

Ç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.

8 – Plus de temps pour mettre la capote

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.

9 – Plus de temps pour voir si c’est une vraie rousse

Je me pose toujours la question.

10 – Un passport pour les quickies

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.

flattr this!

Requests fait partie de la lib standard !

jeudi 29 mai 2014 à 07:07

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.

flattr this!