PROJET AUTOBLOG


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

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

⇐ retour index

Les graves problèmes de la vie en Thaïlande

jeudi 12 juin 2014 à 13:46

Quelques morceaux choisis de ces dernières semaines. Les > sont des monologues, les – sont des dialogues.

> ce soir une amie de ma cop veut tourner une scène meuf/meuf pour son pigeon, je suis chargé de trouver une meuf partante, j’ai un budget de 1500 TBH payé par elle et j’ai le droit de me faire sucer par la meuf
> puis je dois filmer
> elle se fait 20 000 TBH en vendant sa scène lesbienne
> faut que je trouve une bouffeuse de minou
> je me demande si c’est pas mieux de passer un coup de fil à requin
> elle a fait ses preuves
> t’as son num?


- Hi, I need to change my mirror glass and wheel
- Ok, 600 BHT (14€).
- I’ll be back tomorrow ?
- No. Ready. 20 minutes.


> les thais dans les restos, c’est toujours leur premier jour. Ils peuvent avoir travaillé 10 ans, ça change rien. Ils vont se coucher, le lendemain, tu as un thai tout neuf.


- Bon alors tu vas tout droit, vers la colline, et quand tu atteins un scouter rouge garé, tu prends à gauche.
- …


> Bon les photos du DJ qui mixe avec sa bite, on les mets dans “musique” ou dans “amateur” ?


- Non, ma nouvelle meuf m’a racheté à l’ancienne.
- T’a racheté ?
- Oui, elle lui a donné un paquet de drogue pour ne plus me voir.
- Ta nouvelle meuf c’est pas celle en taule pour trafique de drogue et du coup tu dois garder ses clebs ?
- Yep. Son avocat me demande 350 000 BHT pour la faire sortir ou elle reste 10 ans.


> Donc la grosse, elle se pointe dès qu’elle voit les photos du bbq sur facebook, elle bouffe tout, elle se met la race avec notre vodka, et une fois qu’elle voit que sa copine se fait chauffer dans la piscine, elle l’embarque et se casse.
> Et on la connaissais même pas.


- Le mec de ce resto, il fait de la haute cuisine française.
- Mais comment il s’en sort ?
- L’astuce, c’est qu’il a deux cartes. Une pour les connards de touristes. Dès qu’il voit un australien ou un russe, il lui sort le menu frite, hamburger et bière. Et quand les compatriotes arrivent, il nous file le coq au vin et la pana-cota maison.


17h > Bon parlons des choses importantes : ou-est-ce qu’on bouffe ? Fuji ? Batcave ? 101 ? BBQ ? Marché ? Da Moreno ? Buffet sushi ? Samsha ? Suédois ? Coffee mania ? Halal ?
20h> Bon, ça fait 3 heures qu’on débat, je tranche. Batcave. Ou peut être 101. Quoique je me ferais bien une pizza. C’est con que le mec des buritos ait fermé. L’indien c’est trop lourd, et au 101 ils ont une choucroute…


> Ils ont ouvert un Hooters en centre ville ? C’est idiot, c’est comme tenir un stand de pêche aux canards en face de Disneyland.

flattr this!

Comment trouve-t-on des clients en freelance ?

mercredi 11 juin 2014 à 04:16

Ça faisait longtemps que je n’avais pas publié le courrier des lecteurs. Voici un mail que j’ai reçu et la réponse envoyée (avec une ou deux corrections).

Comment trouve-t-on des clients en freelance ? Vous avez surfé sur le réseau du job précédent, ou la demande est telle qu’il suffit de s’inscrire en tant que dev python dans les pages jaunes ?

Hello,

Je depop les emails datant de mars en juin :)

Pour trouver des clients en tant que freelance :

- utiliser le carnet d’adresse de sa boîte précédente. Ça suppose un travail régulier à garder le contact avec les interlocuteurs qu’on a avec les clients externes, demander les numéros/emails directes, les postes, etc. L’important est de bien être transparent et de ne pas essayer de choper des contrats qui auraient été donnés à son ancienne boîte. Il faut être un complément sur les projets en cours, ou prendre les projets que les autres ne prennent pas. Ça ne peut pas se faire a posteriori, ça se prépare pendant son séjour dans la boîte, et ça suppose une démarche semi-commerciale une fois qu’on est dehors.

- aller aux événements/lieux sociaux tech. Tout ce qui est conférences (type la cantine, NUMA), salons (type PyConFR, Djangocong), soirées à thème (on peut les organiser soi-même), petit dej entrepreneur (type open coffee), associations (type hackerspace, fablab). Faire sa review, découvrir de nouvelles technos et les partager avec les autres (proposer une présentation, un tuto, un workshop, etc), ça créé des liens.

- créer un site, avec son CV, un tweeter, et scanner régulièrement la recherche tweeter pour des demandes. Il y en a une chiée.

- faire toutes les semaines un tour des annonces sur les dizaines de sites d’offre d’emploi. Même si une offre ne demande pas un freelance, proposer quand même. Même si l’offre est à l’étranger (proposer de bouger, ou télétravail). On finit toujours pas trouver un truc, mais même quand on en trouve pas, ça fait des contacts. Google est ton ami. Les RSS aussi. Tu es dev, dev un script qui ratisse large et récupère pour toi toutes ces infos et en fait la synthèse.

- prétendre qu’on est freelance depuis longtemps. Demander à des amis de mentir pour soit en ce faisant passé pour un référent dans un boîte et donner son numéro. Je suis très sérieux.

- ton compte github peut être un bon CV, le mettre en avant.

- on est pas obligé de quitter sa boîte pour être freelance. Le status auto entrepreneur est cumulable avec un travail à plein temps (prendre des missions courtes et travailler le we) ou mi-temps (pour se lancer). On est pas obliger de faire le grand saut d’un coup.

@+

Sam

On entend souvent dire “construisez votre réseau”, “faites marcher votre réseau”, mais la réalité c’est que la plupart des gens n’en ont pas et ne savent pas comment en construire un. C’est pour ça qu’il faut attaquer sur deux tableaux : les annonces en ligne (assez facile, contrats moins sympas, mais permet de ternir à court terme et boucher les trous) et les événements sociaux (plus dur, mais meilleurs résultats sur le long terme).

Si vous arrivez à travailler pour une niche (informatique pour pharmaceutique, gestion de projet industriel automobile, booking, etc), il est beaucoup plus facile de démarcher ensuite dans cette niche. N’hésitez donc pas au début à vous spécialiser dedans.

Ça fait écho à l’article Où trouver un Job ou une mission Python / Django ?, même si il faudrait que je fasse un dossier complet là dessus un jour avec :

Comme d’hab, tout ceci est prévu pour après 2038, un jeudi, vers 16h47.

Mais ça serait intéressant, surtout que sur le Web ont dit à tout le monde de jouer franc jeu, d’être un bon garçon, etc, alors que clairement c’est un jeu de dupe et vous serez gagnant en trichant. Pas en bossant pas, attention, faut fournir le taff pour lequel on est payé, mais “soyez-vous même” est le conseil le plus débile que le cinéma et votre maman vous ait donné quand il s’agit de maximiser ses chances de plaire (dans le job et dans d’autres domaines).

flattr this!

La protection CSRF de Django et les requêtes Ajax

mardi 10 juin 2014 à 06:18

La protection contre les attaques CSRF est dans le top 10 des erreurs les plus chiantes en Django, main dans la main avec les fichiers statiques qui ne marchent pas, les URL qui ne matchent pas et les CBV qui nheuuuu, juste pas.

Une fois qu’on a compris le principe, ça va pour la prog normal, mais un jour on a besoin de faire un POST en Ajax, et on se retrouve avec une erreur invisible. Après avoir dégainé Firebug, on comprend qu’on a une 403 forbidden, et votre cerveau finit (la durée galérienne est plus ou moins longue selon les profiles, les heures de sommeil et les phases de la lune) par réaliser qu’on n’a pas envoyé le token CSRF. Merde.

C’est là que généralement les gens sortent du @csrf_exempt, ou carrément, en finissent avec cette solution radicale :

MIDDLEWARE_CLASSES = (
    'django.middleware.gzip.GZipMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
)

Mais c’est quand même dommage d’en arriver là alors qu’on peut le faire proprement.

D’abord, le token est sauvegardé dans un cookie. Il faut donc le récupérer.

// ue petite fonction pour récupérer la valeur d'un cookie,
// puisque bien entendu, comme toutes les APIS javascripts,
// les choses qu'on fait le plus souvent ne sont pas incluses
// en natif. Oui je suis aigri.
function getCookie(name) {
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                return decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
}

Sinon, pour les fainéants, il y a y a un plugin jquery qui permet de faire $.cookie('nom').

Ensuite, on attache cette valeur en header avec toutes les requêtes POST. Comme ça, plus besoin de l’inclure manuellement, on peut faire ses requêtes sans y penser.

Avec jQuery :

$.ajaxSetup({
    // fonction appelée avant d'envoyer une requête AJAX
    beforeSend: function(xhr, settings) {
         // on ajoute le header que si la requête est pour le site en cours
         // (URL relative) et est de type POST
         if (!/^https?:.*/.test(settings.url)  && settings.type == "POST") {
             // attachement du token dans le header
             xhr.setRequestHeader("X-CSRFToken",  getCookie('csrftoken'));
         }
     }
});

Avec AngularJs :

// interception de la configuration du provider HTTP
// qui possède un mécanisme déjà tout prêt pour ça
votre_app.config(function($httpProvider) {
    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});

Attention, si l’app n’est pas servie par Django (template statique avec uniquement des appels Ajax), il faut faire au moins un GET avant de faire son premier POST afin d’obtenir le cookie.

flattr this!

assert “а” == “a” # lol

lundi 9 juin 2014 à 10:45

Python 3, le bonheur d’avoir UTF8 comme encoding par défaut !

En plus, ça ajoute un petit potentiel de lulz.

Par exemple, ceci marche très bien :

def test():
    print('Youpi')
    print('Youpi')

Et ceci…

def test():
    print('Arg')
    print('Arg')

… provoque une syntaxe error !

  File "<stdin>", line 2
       print('Arg')
    ^
SyntaxError: invalid character in identifier

La raison est que la première ligne print(‘Arg’) contient le caractère unicode U+0020, qui est un espace inimprimable, mais pas le même que l’ascii :)

Bon, vous allez-me dire, on pouvait déjà mélanger les tabs et les espaces, s’amuser avec les espaces insécables, ou simplement déclarer manuellement l’encoding et faire pareil…

Allons plus loin. Saviez-vous qu’on pouvait utiliser des caractères non-ASCII dans les identifiants en Python 3 ?

Ceci est donc parfaitement valide :

éôà = 1

Ce qui invite bien entendu a des choses tout à fait amusantes comme :

def аttention():
    print('!')
 
>>> аttention()
!
>>> attention()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'attention' is not defined

En effet, j’ai utilisé le caractère “а” cyrilique comme première lettre, et non le “a” ASCII. Ils n’ont pas le même code :

>>> ord("а"), ord("a")
(1072, 97)
>>> "а" == "a"
False

Quand j’étais au lycée, une bonne blague qu’on faisait aux profs était de faire une capture d’écran du bureau de leur ordi, la mettre en fond d’écran, virer les icônes et régler la barrer des tâches pour se cacher automatiquement. Ils pensaient que leur machine était freezée, et un reboot ne changeait rien. Des heures à s’arracher les cheveux.

Avec les identifiants unicodes je pense qu’on peut retrouver cette merveilleuse créativité avec ses collègues.

flattr this!

Deferred, Future et Promise : le pourquoi, le comment, et quand est-ce qu’on mange

mercredi 4 juin 2014 à 15:19

Si vous avez plongé dans le monde de la programmation asynchrone non bloquante, vous avez du vous heurter aux callbacks. Si ce n’est pas le cas, aller lire l’article, et faites vos armes sur jQuery, je vais m’en servir en exemple.

Signalement de rigueur que l’article est long :

Un callback, ça va.

Deux callbacks, pour un seul appel, ça commence à être chiant, mais c’est compréhensible.

Quand les callbacks appellent eux aussi des callbacks, ça donne des codes imbitables :

$(function(){
  $.post('/auth/token', function(token){
    saveToken(token);
    $.get('/sessions/last', function(session){
      if (session.device != currentDevice){
        $.get('/session/ ' + session.id + '/context', function(context){
          loadContext(function(){
            startApp(function(){
              initUi()
            })
          })}
        )}
      else {
        startApp(function(){
          initUi()
        })
      }}
    )
  })
});

Il y a pire que de lire ce code : le modifier ! Retirez un bloc, pour voir. Oh, et histoire de vous faire partager l’expérience complète, j’ai volontairement déplacé l’indentation d’une parenthèse et de deux brackets.

Or les codes asynchrones ont besoin de callback afin d’enchainer certaines opérations dans le bon ordre, sinon on ne peut pas récupérer le résultat d’une fonction et l’utiliser dans une autre, puisqu’on ne sait pas quand l’opération se termine.

Dans notre exemple, $.post et $.get font des requêtes POST et GET, et comme on ne sait pas quand le serveur va répondre, il faut mettre un callback pour gérer la réponse quand elle arrive. C’est plus performant que de bloquer jusqu’à ce que la première requête soit terminée car pendant ce temps, notre programme peut faire autre chose. Mais c’est aussi super relou à écrire et comprendre.

Entre en jeu les promesses (promises). Ou les deferred. Ou les futures.

Typiquement, on retrouve des deferreds dans Twisted, des promises pour l’AJAX avec jQuery, des futures pour asyncio… Mais il y en a un peu partout de nos jours, et une lib peut utiliser plusieurs de ces concepts.

En fait c’est la même chose, un nom différent donné au même concept, par des gens qui l’ont réinventé dans leur coin. Les puristes vous diront qu’il y a des différences dans l’implémentation, ou alors que la promesse est l’interface tandis que le deferred est l’objet retourné, bla, bla, bla.

Fuck it, on va considérer que c’est tout pareil.

Les promesses sont une des manières de rendre un code asynchrone plus facile à gérer. On dit : ce groupe de fonctions doit s’exécuter dans un ordre car elles sont dépendantes les unes des autres.

Il y a d’autres moyens de gérer le problème de l’asynchrone: des événements, des queues, etc. L’avantage des promesses c’est que c’est assez simple, et ça marche là où on utilisait des callbacks avant, donc on a pu les rajouter aux libs qui étaient blindés de callbacks.

Le principe

La promesse est un moyen de dire que certaines fonctions, bien que non bloquantes et asynchrones, sont liées entre elles, et doivent s’exécuter les unes à la suite des autres. Cela permet de donner un ordre d’exécution à un groupe de fonctions, et surtout, que chaque fonction puisse accéder au résultat de la fonction précédente. Tout ceci sans bloquer le reste du système asynchrone.

En résumé, cela donne un gout de programmation synchrone, à quelque chose qui ne l’est pas.

Cela se passe ainsi :

Voilà un exemple :

// $.get est asynchrone. On a pas le résultat tout de suite, mais en attendant
// on a une promesse tout de suite.
var $promesse = $.get('/truc/machin');
 
// premier callback. Il sera appelé quand $.get aura récupéré son
// résultat
$promesse.then(function(resultat){
  // faire un truc avec le résultat
  // puis on retourne le nouveau résultat
  return nouveau_resultat;
});
 
// deuxième callback. Il sera appelé quand le premier callback
// aura retourné son résultat.
$promesse.then(function(nouveau_resultat){
  // faire un truc
});

Notez bien que c’est TRES différent de ça (en Python):

resultat = request.get('/truc/marchin')
 
def function(resultat):
  # faire un truc
  return nouveau_resultat
nouveau_resultat = function(resultat)
 
def autre_function(nouveau_resultat):
  # faire un truc
autre_function(nouveau_resultat)

En Python, le code est bloquant par défaut. Ça va marcher, mais pendant que le code attend la réponse du serveur, votre ordinateur est en pause et ne travaille pas.

Un plus beau code

On se retrouve avec un code asynchrone, mais qui s’exécute dans l’ordre de lecture. Et comme on peut chainer les then() et donc ne pas réécrire $promesse à chaque fois, on obtient quelque chose de beaucoup plus lisible :

$.get('/truc/machin')
.then(function(resultat){
  // faire un truc
  return nouveau_resultat;
})
.then(function(nouveau_resultat){
  // faire un truc
});

Si on reprend notre premier exemple, ça donne ça :

$(function(){
 
// create new token
$.post('/auth/token')
 
// then save token and get last session
.then(function(token){
  saveToken(token);
  return $.get('/sessions/last');
})
 
// then init session
.then(function(session){
  if (session.device != currentDevice){
 
    $.get('/session/ ' + session.id + '/context')
    .then(function(context){
      loadContext(function(){
        startApp(function(){
          initUi()
        })
      })
    })
 
  }
  else {
    startApp(function(){
      initUi()
    })
  }}
})
 
});

Tout ça s’exécute de manière non bloquante (d’autres fonctions ailleurs dans le programme peuvent s’exécuter pendant qu’on attend la réponse du serveur), mais dans l’ordre de lecture, donc on comprend bien ce qui se passe. Si on veut retirer un bloc, c’est beaucoup plus facile.

Comment ça marche à l’intérieur ?

Histoire d’avoir une idée de comment une promise marche, on va faire une implémentation, simpliste et naïve, mais compréhensible, d’une promesse en Python. Pour rendre l’API un peu sympa,je vais utiliser les décorateurs.

class Promise:
 
    # La promesse contient une liste de callbacks, donc une liste de fonctions.
    # Pas le résultat des fonctions, mais bien les fonctions elles mêmes,
    # puisque les fonctions sont manipulables en Python.
    def __init__(self):
        self.callbacks = []
 
    # Point d'entrée pour ajouter un callback à la promesse
    def then(self, callback):
        self.callbacks.append(callback)
 
    # Cette méthode est celle qui sera appelée par le code asynchrone
    # quand il reçoit son résultat.
    def resolve(self, resultat):
 
        # Ici, on obtient le résultat du code asycnhrone, donc on boucle
        # sur les callbacks pour les appeler
        while self.callbacks:
            # On retire le premier callback de la liste, et on l'appelle
            # avec le résultat
            resultat = self.callbacks.pop(0)(resultat)
 
            # Si le resultat est une promesse, on dit à cette nouvelle promesse
            # de nous rappeler quand a elle a reçu ses résultats à elle avant
            # d'aller le reste de nos callbacks a nous : on fusionne les deux
            # promesses :
            # Promesse 1
            #  - callback1
            #  - callback2
            #  - Promesse 2
            #      * callback 1
            #      * callback 2
            #  - callback 3
            if isinstance(resultat, Promise):
                resultat.then(self.resolve)
                break

Maintenant, créons un code asynchrone:

from threading import Timer
 
def func1(v1):
    # On dit complètement artificiellement d'afficher le résultat
    # de la fonction dans 3 secondes, sans bloquer histoire d'avoir
    # un peu de nonbloquitude dans notre code et justifier l'asynchrone.
    def callback1():
        print(v1)
    t = Timer(3, callback1)
    t.start()
 
def func2(v2):
    # Le même, mais pour 2 secondes
    def callback2():
        print(v2)
    t = Timer(2, callback2)
    t.start()
 
# Deux fonctions normales
def func3(v3):
    print(v3)
 
def func4(v4):
    print(v4)
 
# Et si on les enchaines...
print('Je commence')
func1(1)
print('Juste après')
func2(2)
func3(3)
func4(4)
 
# ... le résultat est bien désordonné :
 
## Je commence
## Juste après
## 3
## 4
## 2
## 1

Parfois c’est ce que l’on veut, que les choses s’exécutent dans le désordre, sans bloquer.

Mais quand on a des fonctions qui dépendent les unes des autres, au milieu d’un code asynchrone, on veut qu’elles se transmettent le résultat les unes aux autres au bon moment. Pour cela, utilisons notre promesse :

from threading import Timer
 
 
# La mise en place de promesses suppose que le code 
# écrit en fasse explicitement usage. Notre code est
# définitivement lié à cette manière de faire.
 
def func1(v1):
    # Notre fonction doit créer la promesse et la retourner
    p = Promise()
    def callback1():
        print(v1)
        # Dans le callback, elle doit dire quand la promesse est tenue
        p.resolve(v1)
    t = Timer(3, callback1)
    t.start()
    return p
 
# On lance la première fonction.
print('Je commence')
promise = func1(1)
print('Juste après')
 
# On ajoute des callbacks à notre promesse.
 
@promise.then
def func2(v2):
    p = Promise()
    def callback2():
        # Pour justifier l’enchainement des fonctions, on fait en sorte que
        # chaque fonction attend le résultat de la précédente, et
        # l'incrémente de 1.
        print(v2 + 1)
        p.resolve(v2 + 1)
    t = Timer(2, callback2)
    t.start()
    # Ce callback retourne lui-même une promesse, qui sera fusionnée
    return p
 
# Ces callbacks ne retournent pas de promesses, et seront chainés
# normalement
@promise.then
def func3(v3):
    print(v3 + 1)
    return v3 + 1
 
@promise.then
def func4(v4):
    print(v4 + 1)
 
# Nos fonctions s'exécutent dans le bon ordre, mais bien de manière
# asynchrone par rapport au reste du programme.
 
## Je commence
## Juste après
## 1
## 2
## 3
## 4

Notez bien :

Évidement, n’utilisez pas cette implémentation de promise à la maison, c’est pédagogique. Ça ne gère pas les erreurs, ni le cas où le callback est enregistré après l’arrivée du résultat, et tout un tas d’autres cas tordus.

Syntaxe alternative

En Python, beaucoup de frameworks ont une approche plus agréable pour gérer les promesses à grand coup de yield. Twisted fait ça avec son @inlineCallback, asyncio avec @coroutine. C’est juste du sucre syntaxique pour vous rendre la vie plus facile.

Il s’agit de transformer une fonction en générateur, et à chaque fois qu’on appelle yield sur une promesse, elle est fusionnée avec la précédente. Ça donne presque l’impression d’écrire un code bloquant normal :

# un appel de fonction asyncrone typique de twisted
@inlineCallback
def une_fonction(data):
  data = yield func1(data)
  data = yield func2(data)
  data = yield func3(data)
 
une_fonction(truc)

Les fonctions 1, 2 et 3 vont ainsi être appelées de manière asynchrone par rapport au reste du programme, mais bien s’enchainer les unes à la suite des autres.

Ouai, tout ce bordel parce que l’asynchrone, c’est dur, donc on essaye de le faire ressembler à du code synchrone, qui lui est facile.

flattr this!