PROJET AUTOBLOG


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

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

⇐ retour index

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!

É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!