PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

D’une base à l’autre en Python

mercredi 25 juin 2014 à 15:19

Je vous avais parlé des bases dans l’article sur le binaire. En informatique, on utilise essentiellement la base 10, évidement, mais aussi 2, 8, 16 et 64. Des puissances de 2, quoi.

Cependant, il arrive parfois qu’on ait besoin d’une base personalisée. En effet, une base n’étant qu’une notation, on peut en créer une de toutes pièces. C’est parfois très utile :

Mais la plupart des outils ne permettent pas de choisir la base de représentation des données qu’ils génèrent. Pour cette raison, il peut être pratique d’implémenter une petit convertisseur d’une base à l’autre.

Voici un objet Python qui permet de convertir une string d’une base custom en entier en base 10, et inversement.

# Il est possible de passer de n'importe quelle base à
# une autre directement, mais utiliser la base 10,
# comme pivot, est ce qu'il y a de plus facile 
# à coder
class BaseConverter:
 
   def __init__(self, symboles):
      self.symboles = symboles
      self.sym2val = {l: i for i, l in enumerate(symboles)}
      self.val2sym = dict(enumerate(symboles))
 
   def to_base_10(self, string):
       i = 0
       base = len(self.sym2val)
       # On part de la gauche vers la droite,
       # donc on commence avec les valeurs les plus
       # grosses.
       # Pour chaque symbole, on ajoute la valeur
       # de celui-ci (donnée par la table) et
       # avec facteur lié à sa position.
       for c in string:
           i *= base
           i += self.sym2val[c]
       return i
 
   def from_base_10(self, number):
       """ Convert from a base 10 to the custom base"""
       array = []
       base = len(self.val2sym)
       # Division euclidienne en boucle jusqu'à ce que le
       # reste soit égal à zero.
       while number:
           number, value = divmod(number, base)
           # Le résultat est l'index du symbole.
           # On le place le plus à gauche, chaque
           # symbole ayant une valeur plus grande
           # que le précédent.
           array.insert(0, self.val2sym[value])
 
       # Ne pas oublier le zéro
       return ''.join(array) or self.symboles[0]

Maintenant imaginez la génération d’un UUID qui doit seulement contenir certains caractères ou avoir une certaine taille. Par exemple, votre client vous demande des ID en base 32 de Crockford.

uuid = uuid.uuid4()
print(uid)
## 50ab7fa9-9643-4aad-be84-548405abea08
CROCKFORD_B32 = BaseConverter("0123456789abcdefghjkmnpqrstvwxyz")
print(CROCKFORD_B32.from_base_10(uid.int))
## 2gndztk5j39apvx12mgg2tqtg8

Bon, évidément, pour toutes les bases ordinaires, int(string, base) fait ça très bien.

On peut aller plus loin et faire une seule partie de l’ID dans une base, et l’autre dans une autre base, pour des raisons de lisibilité. Le cas d’école étant les plaques d’immatriculation ou les numéros de vol.

Une fois, on m’a demandé de faire une URL de type :

/ressource/id/

Mais l’id devait être facile à recopier à la main. Le client a opté pour une notation de type “AAA111″, 3 lettres, puis 3 chiffres.

Vu qu’il fallait l’implémenter en Lua embed dans nginx, la solution la plus simple a été adoptée :

location ~ "^/([0-9A-Fa-f]{3}[0-9]{3})$" {
   set $short_id $1;
   content_by_lua '
       local redirect_url = "http://site.com/ressource/" .. string.sub(ngx.var.short_id, -3) + tonumber(string.sub(ngx.var.short_id,0,3), 16 ) * 1000
       return ngx.redirect(redirect_url.."/")
   ';
}

En gros, on prend les 3 premiers caractères, on les traite comme de l’hexa, puis on multiplie par 999, et on additionne les 3 derniers chiffres. Cela convertit le truc en base 10.

L’URL est très lisible, et facile à dicter ou recopier à la main :

http://site.com/ressource/FAC101

Bien entendu, on peut se retrouver avec des chiffres au début puisque c’est de l’hexa, et avoir une URL de type 345823, mais ça a été accepté.

Au maximum on ne peut gérer que 4 millions d’ID. Or le site n’en avait pas accumulé plus de 350 000 en 7 ans, et à moins que le trafic explose soudainement (auquel cas il aura le budget pour me payer pour changer l’algo:)), ça devrait tenir 80 ans.

Je vais sans doute ajouter ce code à Batbelt du coup.


Télécharger le code de l’article

flattr this!

Pourquoi sametmax.com utilise WordPress ?

mardi 24 juin 2014 à 08:17

WordPress est le pire des blog engines, à l’exception de tous les autres

Churchill

Il y a des solutions en Python, pourquoi vous ne les utilisez pas ?

Parce que c’est trop de taff. Si on veut une feature, il faudra la coder puisque ces blogs n’ont pas de communauté de plugins qui approche 1/1000eme de celle de WordPress.

Par exemple, depuis l’ouverture du blog, on a installé 3 plugins anti-spam, un plugin pour adapter Varnish, un pour avoir des stats sans Google analytics, un formulaire de contact, un live preview pour les commentaires, un outil de notes collaboratives, etc. On a aussi changé 10 fois de thèmes, et custo celui-là à mort (je sais, il est moche :)).

Je ne connais aucune solution de blog Python ou non qui permette de faire tout ça out of the box.

Or, durant ces 2 dernières années, on a publié plus de 742 articles. Pas un article par jour, mais pas loin. On a une vie assez remplie également derrière. Et même si on a la chance de pouvoir gérer notre emploi du temps, ce dont on use et abuse, les journées n’ont que 37h.

Je connais un super générateur de blog statique qui…

Pas de commentaires indexable par Google. Pas de formulaire de contact. Besoin de connaître un VCS pour les contributeurs. Pas de gestion de droits… J’ai failli rajouter pas de moteur de recherche mais c’est vrai que le notre est pourri.

Pourquoi pas une plateforme en ligne alors ?

On veut avoir le contrôle de nos données pour éviter de se faire fermer violemment en cas de problème.

Ok, mais pourquoi pas Dotclear, Ghost, etc ?

J’en ai essayé par mal. Aucun n’a le nombre de plugins et thèmes de WordPress. Aucun n’a autant de features. Aucun n’a autant de tutos. Et rien ne prouve qu’on aura moins de problèmes avec.

Tout va toujours bien jusqu’à ce qu’on dépasse 10 visiteurs par jour.

Alors arrêtez de vous plaindre !

Si tu ne veux pas nous entendre nous plaindre, ne nous lis pas. Il y a un petit bouton en forme de croix en haut à droite de ton tab, tu vas voir, c’est magique.

Sinon, pas envie de coder votre solution ?

Très envie. Mais contrairement à la plupart des gens, je ne pense pas que créer un moteur de blog ce soit “3 jours de boulot”.

C’est un taff énorme, et là, tout de suite, c’est mort.

Et il existe 20000 personnes qui en ont fait un, donc pour avoir une raison de coder quelque chose, il faudrait faire quelque chose de vraiment innovant :

Ça, ça serait un truc qui vaudrait le coup de se faire chier à migrer dessus.

3 jours de dev ? Mouarf. Comptez plutôt 3 ans, pendant tous ces jours de libre. Autant dire que ça n’arrivera pas.

Bref, je vous laisse vous référer à l’image d’illustration de l’article pour résumer la situation des bloggers sur la toile actuellement.

flattr this!

Prostitution masculine

lundi 23 juin 2014 à 04:11

Un de mes coloc est un ancien gogo dancer, et je le soupçonne de bien plus. Mais dans les apparences, c’est un garçon très propre sur lui, et depuis quand je rencontre de nouvelles personnes, je les imagine toujours dans des situations coquines.

Il se trouve que quand on parle de pute, on pense généralement à des nanas, pas à des gigolos, car il y en a beaucoup moins. Enfin en France, parce que quand je travaillais en Afrique, j’en ai vu des blondes venir se payer leur bois d’ébène…

Cependant, il existe une prostitution bien moins reconnue : la prostitution sociale. Coucher pour réussir, pour bénéficier du train de vie d’une personne, pour éviter des ennuis, pour ne pas perdre quelque chose, en échange d’un service, d’un bien ou d’un avantage…

Ces putes là sont très nombreuses, parfaitement socialement acceptées, absolument pas assumées, et les mecs sont beaucoup plus représentés.

Non pas qu’il n’y a pas de nanas putes sociales, elles sont légions, mais je trouve qu’on a tendance à omettre trop souvent les deux côtés de la pièce. L’homme pute est là, et pas juste métaphoriquement.

Comme un de mes potes avocat qui a fisté son batonier car c’était bon pour sa carrière.

Comme un des potes de Max qui a couché régulièrement avec un thon ce mois-ci car il avait besoin de sa voiture et qu’elle garde les chiens de sa copine (sic) pendant quelques mois.

Et qui n’a pas dans son entourage ce type qui se force à se taper sa gonzesse dont il ne veut plus pour éviter qu’elle l’emmerde, qu’elle divorce, qu’elle prenne les enfants/l’argent, etc ?

On a tous des tas de gens autour de nous, hommes ou femmes, qui utilisent leur cul pour obtenir quelque chose.

Mais ça ne se limite pas à ça.

Cette semaine, en boîte, une meuf a proposé de l’argent à Max pour qu’il rentre avec elle.

Je suis un peu jaloux, ça ne m’est jamais arrivé.

flattr this!

Un petit tour des frameworks Web Python

dimanche 22 juin 2014 à 05:28

Bottle : le plus petit disponible (tient dans un fichier). Génial pour du code jetable ou des petits sites, etc. Fantastique outil d’enseignement et d’apprentissage. En plus, ça tient plutôt bien la charge compte tenu de la taille.

django : le framework Python le plus connu. On peut tout faire avec, l’écosystème est fantastique (il y a des apps djangos tierces partout pour tout et n’importe quoi, c’est un truc de fou). Mais le maîtriser prend du temps. Efficace pour construire un site web avec beaucoup de logique personnalisée. Au final, si on veut être sérieux dans en programmation Web avec Python, on finit toujours par passer par Django.

flask : sa taille est entre django et bottle, et avec tout ce qu’il faut pour faire un site de taille moyenne. Il commence à avoir pas mal de plugins disponibles un peu partout sur la toile, et est une alternative très sympa quand on n’a pas besoin de charger les 3 tonnes de Django.

wep2py : se positionne en alternative à Django et Flask, mais avec une philosophie très différente. Pas mal de magie (à la rails), des interfaces graphiques pour l’admin, composants très couplés mais très intégrés… Je ne suis pas fan, mais on m’en a dit du bien.

cherrypy : un framework WSGI pure Python qui a de très bonnes perfs sans rien rajouter, même en prod. Mais depuis qu’ils ont rendu disponible sa partie serveur et qu’on peut l’utiliser pour Django/flask/bottle, ça ne vaut plus le coup d’utiliser sa partie framework qui n’a rien de fantastique.

pyramid : le plus gros compétiteur de Django en termes de fonctionnalités. Beaucoup moins monolithique, et bien plus flexible, donnant pas mal de contrôle. Mais l’intégration des composants de django le rend beaucoup plus facile à maîtriser, et son écosystème est 100 fois meilleur (j’ai toujours été en froid avec l’écosystème Zope).

twisted un framework internet asynchrone en pur Python. J’ai dit INTERNET, pas WEB. On peut faire HTTP, mais aussi SSH, IMAP, FTP, et à peu près n’importe quoi. Surpuissant, et ses performances sont incroyables. Son API est aussi la plus merdique de tout l’univers Python. Apprendre et utiliser Twisted, c’est comme se branler avec des gants de boxe en récitant l’alphabet à l’envers.

tornado : framework Web asynchrone. Techniquement le seul compétiteur de nodejs en pur Python (pas de gevent, d’extension C, etc) si on exclue crossbar encore trop immature. L’API n’est pas incroyable, mais pas trop dur, et les perfs sont bonnes. Les Websocket marchent clé en main.

cyclone : tornado qui tourne dans l’event loop de twisted. Pour pouvoir utiliser l’API de tornado pour le Web, mais les outils de twisted pour le reste. Si vous êtes capable d’utiliser ça à son plein potentiel, qu’est-ce que vous foutez à lire cet article ?

webpy : un ancêtre. Un fantôme du passé. Do not use.

Il existe ensuite tout un tas d’expérimentations de framework avec asyncio, gevent, etc. C’est plus du bricolage qu’autre chose pour le moment, alors je ne recommande pas de base un gros projet dessus à moins d’être déjà très à l’aise avec la prog web python.

flattr this!

Attributs privés en Python

samedi 21 juin 2014 à 03:15

Tout est accessible en Python. Il n’y a pas de variables privées.

Quand on veut une variable à usage interne, la convention est de la nommer avec un underscore devant :

class Monique:
    def __init__(self):
        self._private = "lessons"

Ça n’empêche rien, mais les devs savent qu’il faut éviter d’utiliser cette variable car elle ne fait pas partie de l’API publique de la classe, et l’implémentation pourrait changer plus tard.

Comme self, c’est une convention forte puisque la plupart des libs de completion du code la prennent en compte.

Il existe une fonctionalité moins connue, qui fait que quand on utilise deux underscores, accéder directement à la variable lève un AttributeError:

class Monique:
    def __init__(self):
        self.__private = "lessons"
 
m = Monique()
print(m.__private)
AttributeError: 'Monique' object has no attribute '__private'

Du coup, quelques rares personnes ont utilisé cette feature pour émuler des attributs privés, ignorant le fait qu’on vous dit partout sur la toile que tout est accessible en Python.

C’est une mauvaise idée.

C’est une mauvaise idée car si votre objectif est la sécurité, ça ne sert à rien puisque votre variable est accessible de tas de manières détournées différentes. Par exemple :

print(m.__dict__['_Monique__private'])
# lessons
m.__dict__['_Monique__private'] = None
print(m.__dict__['_Monique__private'])
# None

C’est une mauvaise idée car si votre objectif est de créer une API publique, un seul underscore suffit.

Enfin c’est une mauvaise idée car si vous fournissez une lib qui ne correspond pas à un besoin, quelqu’un peut toujours contourner le problème en monkey patchant votre code le temps que vous trouviez une solution plus propre. En rendant la variable “privée”, vous rendez ceci plus difficile, sans le rendre impossible. Tout le monde y perd.

flattr this!