Catégorie : développement

Tout ce qui concerne le développement en général, que ce soit des choses générales, ou des choses bien précises. Cela va de la gestion de projet à la recherche du fonctionnement de pointeurs en C.

Unicode : le minimum absolu que tout développeur doit savoir

Nb : j’ai toujours été très admiratif de Joël Spolsky (créateur de Trello qu’il a revendu $425M) et de ses articles excellents. Il l’a retiré de son wiki mais il est toujours disponible ici, sur Webarchive.

Je me permets de le copier coller, avec de très légères modifications, car il est excellent – cela n’est que mon opinion bien sûr.

Le minimum absolu que tout développeur doit absolument, positivement savoir sur Unicode et les jeux de caractères (aucune excuse !)

Par Joël Spolsky

Vous ne vous êtes jamais posé de questions sur cette mystérieuse balise Content-Type ? Vous savez, celle que vous êtes supposé mettre en HTML, et dont vous n’avez absolument jamais su ce qu’elle veut dire ?

Vous n’avez jamais reçu un email de vos amis en Bulgarie avec pour objet  » ???? ?????? ??? ????  » ?

J’ai été atterré de découvrir combien de développeurs ne sont absolument pas au jus à propos du monde mystérieux des jeux de caractères, encodages, Unicode et tous ces trucs-là. Il y a quelques années, un beta-testeur se demandait si le programme pourrait traiter des messages emails en japonais. En japonais ? Ils ont des emails, en japonais ? J’étais même pas au courant. Quand j’ai regardé de près au contrôle ActiveX que nous avions acheté pour parser les messages email au format MIME, nous avons découvert qu’il faisait très exactement ce qu’il ne fallait pas faire avec les jeux de caractères, et nous avons dû écrire du code vraiment héroïque pour défaire la mauvaise conversion qu’il effectuait et la refaire correctement. J’ai alors évalué une autre librairie du commerce, et elle avait elle aussi une implémentation complètement naze des codes de caractères. J’ai contacté le développeur de ce package et il pensait en gros qu’ « ils ne pouvaient rien y faire ». Comme beaucoup de programmeurs, il aurait bien voulu que ce sujet disparaisse de lui même, d’une manière ou d’une autre.

Mais il ne disparaîtra pas. Quand j’ai découvert que l’outil de développement web le plus populaire, PHP, avait une ignorance à peu près totale de ces questions d’encodage de caractères, qu’il utilisait d’une manière insouciante 8 bits pour les caractères, rendant de fait presque impossible le développement d’applications web internationales facilement, je me suis dit « trop, c’est trop ».

J’ai donc une annonce à vous faire : si vous êtes un programmeur, et que vous ne possédez pas les bases des caractères, des jeux de caractères, de l’encodage et d’Unicode, et que je vous attrape, je vais vous punir en vous faisant éplucher des oignons pendant 6 mois dans un sous-marin. Je vous jure que je le ferai.

Et encore une chose:

CE N’EST PAS SI DIFFICILE.

Dans cet article je vais vous donner exactement ce que tout programmeur en activité devrait savoir. Tout ce mélange « texte à plat = ascii = tout caractère est sur 8 bits » n’est pas seulement faux, c’est sans espoir, et si vous êtes encore en train de programmer de cette façon, vous ne valez pas mieux qu’un docteur qui ne croit pas aux microbes. Faites moi le plaisir de ne pas écrire une ligne de code de plus jusqu’à ce que vous ayez fini de lire cet article.

Avant de commencer, je devrais vous prévenir que si vous êtes l’une de ces rares personnes qui savent tout de l’internationalisation, vous allez trouver mon exposé un petit peu trop simplifié. Je veux vraiment juste donner ici un minimum afin que tout le monde puisse comprendre ce qui se passe et puisse écrire du code qui ait un espoir de fonctionner avec du texte dans n’importe quelle autre langue qu’une version d’anglais n’incluant aucun mot accentué. J’attire également votre attention sur le fait que la manipulation des caractères est seulement une minuscule partie de tout ce qu’il faut pour créer des logiciels qui fonctionnent dans toutes les langues, malheureusement je ne peux écrire que sur une chose à la fois, alors aujourd’hui ce sont les jeux de caractères.

Un point de vue historique

La façon la plus facile de comprendre tout ça est d’avancer chronologiquement.

Vous vous dites probablement que là je vais parler de ces très vieux jeux de caractères comme EBCDIC. Hé bien non. EBCDIC n’est pas vital pour vous. Nous n’avons pas à remonter si loin dans le temps.

La table ASCII

Il y a de cela presque longtemps, alors qu’Unix était inventé et que K&R écrivaient Le langage C, tout était très simple. EBCDIC était en train de disparaître. Les seuls caractères qui comptaient étaient ces bonnes vieilles lettres anglaises non accentuées, et nous avions un code pour elles appelé ASCII qui pouvait représenter chaque caractère à l’aide d’un nombre entre 32 et 127. L’espace était le 32, la lettre « A » la 65, etc. On pouvait facilement stocker ça sur 7 bits. Beaucoup d’ordinateurs, à cette époque, utilisaient des mots de 8 bits, ce qui fait que non seulement vous pouviez stocker n’importe quel caractère ASCII, mais vous aviez un bit entier à dépenser, que, si vous étiez vicieux, vous pouviez utiliser pour vos propres besoins déviants: les mous du bulbe de chez WordStar ont utilisé ce bit de poids fort pour indiquer la dernière lettre d’un mot, réellement, condamnant ainsi WordStar aux textes anglais uniquement. Les codes en dessous de 32 étaient appelés non imprimables et étaient utilisés pour les jurons. Je plaisante. Ils étaient utilisés pour les caractères de contrôle, comme le 7 qui faisait biper l’ordinateur et le 12 qui provoquait l’expulsion d’une feuille hors de l’imprimante, et l’aspiration d’une nouvelle.

Et tout cela était bel et bon, tant que vous êtes anglophone.

Parce que ces mots mémoire avaient de la place pour 8 bits maximum, beaucoup de gens se sont mis à penser « Bon sang, on peut utiliser les codes 128-255 pour nos propres besoins ». Le problème, c’est que beaucoup de gens ont eu cette idée au même moment, et qu’ils avaient chacun leur propre petite idée de quoi devait aller où dans cet espace de 128 à 255. L’IBM-PC avait quelque chose qui allait devenir connu comme le jeu de caractères OEM qui fournissait des caractères accentués pour les langues européennes et une brassée de caractères semi-graphiques… Des barres horizontales, des barres verticales, des barres horizontales avec des petites babioles suspendues à droite, etc., et vous pouviez utiliser ces caractères représentant des lignes pour fabriquer d’élégantes boîtes de dialogue et des lignes sur l’écran, que vous pouvez peut être même voir encore tourner sur un très vieux PC dans le pressing du coin. En fait dès que des gens ont commencé à acheter des PC en dehors d’Amérique toutes sortes de jeux de caractères OEM différents ont été imaginés, qui utilisaient tous les 128 caractères du haut pour leurs propres besoins. Par exemple sur certains PC le caractère 130 affichait un é, mais sur les ordinateurs vendus en Israël c’était la lettre hébreu Gimel (ג), ce qui fait que quand des américains envoyaient leurs C.V. en Israël ils étaient reçus comme des « csums ». Dans de nombreux cas, comme le russe, il y avaient beaucoup d’idées différentes de ce qu’ils fallait faire avec les 128 caractères du haut, ce qui fait que vous ne pouviez même pas interchanger de manière fiable des documents russes entre eux.

Cet OEM ouvert à tous finit par être codifié par le standard ANSI. Dans le standard ANSI, on était d’accord sur ce qu’il fallait faire en dessous de 128, qui était vraiment très proche de l’ASCII, mais on gérait les caractères à partir de 128 de plein de façons différentes, selon où on vivait. Ces différents systèmes furent appelés des pages de codes. Par exemple en Israël DOS utilisait une page de codes appelée 862, alors que les utilisateurs grecs utilisaient la 737. C’était la même chose en dessous de 128, mais différent au dessus, là où les lettres rigolotes habitaient. Les versions nationales de MS-DOS avaient des douzaines de ces pages de codes, capables de prendre en compte n’importe quoi de l’anglais à l’islandais , et il y avait même quelques pages de code « multi-langues » qui pouvaient faire de l’espéranto et du galicien sur le même ordinateur ! Houah ! Mais avoir, disons, de l’hébreu et du grec sur le même ordinateur était une impossibilité totale à moins d’écrire son propre programme capable d’afficher n’importe quoi à l’aide de bitmaps graphiques, parce que l’hébreu et le grec réclamaient des pages de codes ayant des interprétations différentes des nombres du haut.

Pendant ce temps, en Asie, des choses encore plus dingues étaient faites pour prendre en compte le fait que les alphabets asiatiques avaient des milliers de lettres, qui ne pourraient jamais tenir sur 8 bits. En fait, la solution trouvée fut un système bordélique appelé DBCS, « double byte character set », le jeu de caractères à double octet, dans lequel certaines lettres étaient stockées sur un octet, et d’autres sur deux. Il était facile de se déplacer vers l’avant à l’intérieur d’une chaîne, mais quasi impossible de reculer. Les programmeurs étaient encouragés à ne pas utiliser s++ et s-- pour se déplacer en avant et en arrière, mais plutôt d’appeler des fonctions, comme les AnsiNext et AnsiPrev sous Windows, qui savaient comment se débrouiller avec tout ce foutoir.

Mais même alors, beaucoup de gens se contentaient de prétendre qu’un octet était un caractère et qu’un caractère c’était 8 bits, et que du moment que vous ne déplaciez jamais une chaîne d’un ordinateur vers un autre, ou que vous ne parliez pas plus d’une langue, en gros ça marchait toujours. Mais bien sûr, dès qu’Internet apparut, déplacer une chaîne d’un ordinateur vers un autre devint un lieu commun, et tout ce merdier se cassa la figure. Heureusement, Unicode avait été inventé.

Unicode

Unicode fut un effort courageux pour créer un jeu de caractères unique qui inclurait tout les systèmes d’écriture raisonnables de la planète et quelques autres fictifs comme le Klingon, également. Certaines personnes sont persuadées à tort qu’Unicode est simplement un code 16-bits où chaque caractère tient sur 16 bits et qu’il y a donc 65536 caractères possibles. En fait, ce n’est pas correct. C’est le mythe le plus commun à propos d’Unicode, donc si vous pensiez ça, ne vous en faites pas.

En fait, Unicode possède une façon différente d’appréhender les caractères, et vous devez comprendre cette façon qu’Unicode a d’appréhender les choses, ou rien ne vous semblera clair.

Jusqu’à maintenant, nous sommes partis du principe qu’à une lettre correspondaient des bits qu’on pouvait stocker sur disque ou en mémoire.

A -> 0100 0001

En Unicode, une lettre correspond à quelque chose appelé un point de code (ou numéro de caractère, ou valeur scalaire Unicode) qui n’est qu’un concept théorique. Comment ce point de code est représenté en mémoire ou sur disque est une tout autre histoire.

En Unicode, la lettre A est un idéal platonicien. Elle flotte dans les cieux :

« A »

Ce A platonicien est différent de B, et différent de a, mais le même que A et A, et A. L’idée que A dans la police Times New Roman est le même caractère que le A dans la police Helvetica, mais différent de « a » en minuscule, ne semble pas tellement sujet à discussion, mais dans certaines langues, se demander simplement ce qu’une lettre est peut entraîner des controverses. Est-ce que la lettre allemande ß est une véritable lettre, ou juste une façon tordue d’écrire ss ? Si la forme d’une lettre change à la fin d’un mot, est-ce une lettre différente ? La langue hébreue répond oui, l’arabe non. Enfin bon, quoi qu’il en soit, les brillants membres du consortium Unicode ont réfléchi à tout cela durant la dernière décennie ou presque, à grand renfort de débats hautement politiques. Vous n’avez pas donc plus à vous en préoccuper. Ils ont déjà pensé à tout.

Toute les lettres platoniciennes de tous les alphabets se sont vues attribuer un nombre magique par le consortium Unicode qui s’écrit comme ceci: U+0645. Ce nombre magique est appelé un point de code. Le U+ signifie « Unicode », et les nombres derrière sont en héxadécimal. U+FEC9 est la lettre arabe Ain. La lettre anglaise A est la U+0041. Vous les trouverez toutes à l’aide de l’utilitaire charmap sous Windows, ou en visitant le site web d’Unicode.

Il n’y a pas de véritable limite au nombre de lettres qu’Unicode peut définir et ils sont réellement allés au delà de 65536, ce qui fait que toutes les lettres Unicode ne peuvent pas tenir sur deux octets, mais de toute façon c’était un mythe, alors…

OK, alors disons que nous avons une chaîne :

Hello

qui, en Unicode, correspond aux cinq points de code :

U+0048 U+0065 U+006C U+006C U+006F.

Juste une poignée de points de code. Des nombres, en fait. Jusque là nous n’avons rien dit sur comment stocker ou représenter ça en mémoire ou dans un message email.

Encodages

Et c’est là que les encodages interviennent.

La première idée pour l’encodage d’Unicode, celle qui a abouti au mythe des deux octets, fut: Hé, on n’a qu’à stocker ces nombres sur deux octets chacun! Hello devient donc:

00 48 00 65 00 6C 00 6C 00 6F

D’accord ? Pas si vite ! Est-ce que ça ne pourrait pas être :

48 00 65 00 6C 00 6C 00 6F 00

Hé bien, techniquement, oui, je pense bien que ça pourrait, et, en fait, les premiers « implémenteurs » voulaient pouvoir stocker leurs points de code Unicode en mode « poids fort derrière » [high-endian] ou « poids fort devant » [low-endian], selon que leur CPU était plus rapide ou plus lent dans l’un ou l’autre, et c’était bonnet blanc et blanc bonnet, et cela faisait déjà deux façons de stocker de l’Unicode. Alors les gens furent obligés d’en arriver à la convention bizarre de placer les caractères FE FF au début de toutes les chaînes Unicode ; on appela ça une marque d’ordre des octets Unicode (Unicode Byte Order Mark) et si vous intervertissiez vos octets de poids faible et fort ça donnait FF FE, et les gens qui lisaient votre chaîne savaient qu’ils devraient intervertir tous les autres octets. Pfff. Et toutes les chaînes Unicode en liberté dans la nature ne possèdent pas cette marque d’ordre des octets au début.

Pendant un moment il semblait que tout cela aurait pu suffire, mais les programmeurs se plaignaient toujours. « Regarde moi tous ces zéros! », disaient-ils, parce qu’ils étaient américains et qu’ils regardaient des textes en anglais qui utilisaient rarement des points de code au dessus de U+00FF. Et puis c’étaient des hippies libéraux en Californie qui voulaient préserver (sarcasme). Si ç’avait été des Texans, peu leur aurait importé de bâfrer deux fois la quantité d’octets. Mais ces mauviettes de Californiens ne pouvaient pas supporter l’idée de doubler la quantité de stockage nécessaire pour des chaînes, et puis, de toute façon, il y avait ces foutus documents, dehors, qui utilisaient tous ces jeux de caractères ANSI et DBCS, et qui allait convertir tout ça ? Moi?  [NdT: En français dans le texte]. Pour cette seule raison, un tas de gens décidèrent d’ignorer Unicode pendant plusieurs années et pendant ce temps les choses empirèrent.

Aussi fut inventé le brillant concept UTF-8. UTF-8 était un autre système pour stocker en mémoire vos chaînes de points de code Unicode, ces nombres U+ magiques, en utilisant des mots de 8 bits. en UTF-8, chaque point de code de 0 à 127 est stocké sur un seul octet. Seuls les points de code à partir de 128 et au delà sont stockés en utilisant 2, 3, en fait jusqu’à 6 octets.

Tout ceci a pour effet de bord très net qu’un texte anglais en UTF-8 ressemble exactement à sa version en ASCII, alors pour les américains il n’y a pas le moindre problème. Seul le reste du monde doit endurer le gage. Pour reprendre notre exemple, Hello, qui était U+0048 U+0065 U+006C U+006C U+006F, sera stocké comme 48 65 6C 6C 6F, ce qui est, le croiriez-vous, exactement comme si c’était stocké en ASCII, et en ANSI, et en n’importe quel jeu de caractères OEM de la planète. Maintenant, si vous êtes assez intrépide pour utiliser des lettres accentuées, ou des lettres grecques, ou des lettres Klingon, vous devrez utiliser plusieurs octets pour stocker un seul point de code, mais les américains ne s’en apercevront jamais. (UTF-8 a aussi cette agréable propriété qu’un vieux code de traitement de chaînes voulant utiliser un octet 0 en tant que null-terminateur ne tronquera pas les chaînes).

Je vous ai déjà indiqué trois façons d’encoder de l’Unicode. Les méthodes traditionnelles « stockez-moi-ça-sur-deux-octets » sont appelées UCS-2 (à cause des deux octets) ou UTF-16 (à cause des 16 bits), et vous devez toujours vous demander si c’est de l’UCS-2 à poids fort derrière [high-endian] ou de l’UCS-2 à poids fort devant [low-endian]. Et puis il y a une autre norme norme UTF-8 qui possède en plus l’agréable propriété d’être respectueuse si vous avez l’heureuse coïncidence de posséder à la fois des textes anglais et des programmes lobotomisés qui ne sont absolument pas au courant qu’il existe autre chose que l’ASCII.

Il y a en fait une poignée d’autres manières d’encoder Unicode. Il y a quelque chose qui s’appelle UTF-7, qui ressemble beaucoup à UTF-8 mais qui garantit que le bit le plus haut est toujours à 0, de manière à ce que si vous devez passer Unicode à travers une sorte de système email totalitaire qui pense que 7 bits c’est bien assez, merci, il pourrait encore se faufiler sain et sauf. Il y a UCS-4, qui stocke chaque point de code en 4 octets, qui a l’agréable propriété que tous les points de code sont stockés sur le même nombre d’octets, mais, bon sang, même un Texan ne serait pas assez intrépide au point de gaspiller autant de mémoire.

Et en fait, à partir du moment où vous vous figurez les choses en terme de lettres platoniciennes idéales représentées par des points de code Unicode, ces points de code Unicode peuvent être encodés dans n’importe quel schéma d’encodage de la vieille école ! Par exemple, vous pourriez encoder la chaîne Unicode pour Hello (U+0048 U+0065 U+006C U+006C U+006F) en ASCII, ou avec le vieil encodage OEM grec, ou avec l’encodage ANSI hébreu, ou avec n’importe lequel des centaines d’encodages déjà inventés, à un détail près : certaines lettres pourraient bien ne pas s’afficher ! S’il n’y a pas d’équivalent pour le point de code Unicode que vous essayer de représenter avec l’encodage que vous utilisez, vous obtenez généralement un petit point d’interrogation: ? ou, si vous êtes vraiment bon, une boîte. Alors, vous obtenez quoi? -> �

© Joël Spolsky

Commentaires fermés sur Unicode : le minimum absolu que tout développeur doit savoir Publié dans Mots-clé

Go langage

Mémo go

Uniquement des notes sur ce qui est « original » dans le langage « go », par rapport aux autres langages.

Variables

Déclaration

Par rapport à tous les autres langages, il faut le faire à l’envers :
var x int
Et pour un tableau : var [nom] [[longueur]][type], exemple :
var s [3]int

Initialisation avec des valeurs

Pas d’explication, un exemple direct suffit : arr := [2]int{2, 4}

Inférence de type

Une déclaration complète d’une variable serait var i int = 12
Une déclaration ultra simplifiée acceptable est i := 12 et dans ce cas, Go va tenter de deviner le type (« type inference »).

Comparaison de tableaux

La déclaration de variables se fait toujours en précisant la longueur, et au moment de comparer, il faut que les tableaux aient exactement la même longueur, sinon on aura une erreur. C’est pour cela que les tableaux sont moins utilisés que le type slice.

Comparaison = avoir des types identiques

Go n’essaie jamais de transformer les variables en booléens pour pouvoir les comparer, c’est à nous transformer une variable ou l’autre de manière à ce qu’elles aient toujours le même type.

Création d’une variable dans un « if »

Exemple direct : if b:= a / 2; b > 5 {...} else {...}
La variable b n’existera que dans le bloc if / else.

Boucle for / range

s := "Bonjour"
for k, v := range s {
    fmt.Println(k, v, string(v))
}

k correspond à l’index, et n’est pas toujours incrémenté que de 1, selon l’encodage. C’est la méthode à utiliser pour parcourir une chaîne en utf8. Car via un classique for, Go parcourra la chaîne « à la » C, c’est à dire que un caractère = un octet (ce qui n’est pas le cas en utf8).

Switch / case

Par défaut, les « break » sont implicites, pas besoin de les mettre. Si on veut exécuter l’instruction du « case » suivant (= comme en C ou JavaScript), il faut le dire explicitement via « fallthrough« .

Il est possible de créer une variable qui ne vivra que le temps du switch / case, exemple :

switch l := len(word); word {
    ...blabla...
}

Contrairement aux autres langages, il ne faut pas mettre plusieurs case d’affilée pour gérer plusieurs cas au même endroit, il suffit de les séparer par une virgule, exemple pour continuer le précédent :

switch l := len(word); word {
case 1 < l && l < 10, word == "ok":
    fmt.Println("Soit mot : 1 < longueur < 10, soit il vaut 'ok'")
}

Fonctions

Appels

Tous les appels de fonction sont par valeur. Jamais par référence. Nombre, booléen, ainsi que les tableaux.

Retours

Le type résultat de retour est après les parenthèses des paramètres et avant l’accolade ouvrante :
func mult(a, b) int {
    return a + b
}

Vous pouvez renvoyer plusieurs valeurs ! Les résultats de retour sont entre parenthèse après les parenthèses des paramètres et avant l’accolade ouvrante :
func divAndRemainder(a, b) (int, int) {
    return a / b, a % b
}
d, r := divAndRemainder(2, 3)
d, _ := divAndRemainder(2, 3)
_, d := divAndRemainder(2, 3)

Vous pouvez nommer les paramètres de retour et les « remplir » dans la fonction. Il faut simplement ajouter return sans rien pour qu’il renvoie le retour sans y toucher :
func divAndRemainder(a, b) (div, remainder int) {
    div = a / b
    remainder = a % b
    return
}
d, r := divAndRemainder(2, 3)
d, _ := divAndRemainder(2, 3)
_, d := divAndRemainder(2, 3)

Fonctions en tant que variable

Exemple direct :
func myFunc() {
    f := func(a int) int {
        return a + 1
    }
    fmt.Println(f(1))
}

Fonctions en tant que paramètres

Exemple direct d’un paramètre qui doit être « une fonction qui prend un entier en tant que paramètre et renvoie un entier » :
func myFunc(a int, f func(int) int) {
    fmt.Println(f(a))
}

Tableaux

Base : var a [2]int

fmt.Println("Array a", a, len(a))

Mieux : slice var a []int

La différence entre slice et array ? On ne précise pas le nombre d’éléments.

Pointeurs

Très proche du C. Un exemple direct suffit :
var p *int
i := 4 // int declaration
p = &i // assign the address of the variable to the pointer
*p = 45 // change the value of the variable i
fmt.Println(p, *p) // prints address then value

Switch / case

C’est l’inverse du C : les break sont implicites, et si on veut vraiment continuer sur le case suivant, il faut utiliser le mot-clé fallthrough :

switch i := myfunc(); {
case i < 0:
    fmt.Println("negative")
    fallthrough
case i == 0:
    fmt.Println("zero")
default:
    fmt.Println("positive")
}

Conversions

Bizzareries

Pointeurs et structures

Vous pouvez déclarer une structure, et la créer dynamiquement, puis créer un pointeur vers elle. En C, quand on utilise les pointeurs, l’indirection pour accéder aux membres est "->", donc on ne peut pas se tromper. Ici, on utilise la même syntaxe, que ce soit avec une structure, directement, ou avec un pointeur, donc indirectement. Exemple concret :

type People struct {
    name string
    height int
}
var value People
value.name = "Olivier"
value.height = 190
ptr := &value
ptr.name = "Thibault"
ptr.height = 185

Donc on écrit value.name et ptr.name alors que l’un est une struct et l’autre un pointeur. Je n’aime pas du tout cela, cela prête clairement à confusion.

Astuces classiques

Site officiel

Packages

Serveur asynchrone TCP Python. Et le client C# Unity !

Deux exemples très courts pour vous mettre sur les rails, qui envoient et reçoivent du binaire « pur » = très peu de bande passante, avec une connexion persistante.

Je vous donne deux envois-réception qui devraient vous permettre de faire tous vos envois binaires :

  1. C# : le client envoie un octet, qui correspond à un booléen, pour dire s’il est en big ou little endian ;
  2. C# : le client envoie un message encodé en UTF-8 (oui j’ai trouve la solution qui fonctionne !) ;
  3. Python : le serveur lit ce booléen ;
  4. Python : le serveur lit le message et le dit à voix haute (sous Windows) ;
  5. Python : le serveur envoie un entier non signé, puis deux float ;
  6. C# : le client lit l’entier non signé puis deux floats.

Avec ça, vous avez de quoi comprendre et faire tous les échanges que vous voulez !

Serveur asynchrone TCP Python

import asyncio
import struct
from asyncio import StreamWriter, StreamReader
import pythoncom
import win32com.client as win32_client
HOST = '192.168.1.31'
PORT = 9696
async def handle(reader: StreamReader, writer: StreamWriter):
    is_little_endian = False
    buffer = bytearray(100)
    addr = writer.get_extra_info('peername')
    print(f"Connected with {addr!r}")
    is_little_endian, = struct.unpack_from(
        '?', await reader.read(struct.calcsize('c'))
    )
    print(f'{is_little_endian=}')
    data = await reader.read(4096)
    message = data.decode('utf8')
    pythoncom.CoInitialize()
    speak = win32_client.Dispatch('SAPI.SpVoice')
    speak.Speak(message)
    print(f"Received {message!r} from {addr!r}")
    print(f"Send: {message!r}")
    float1 = 1.1
    float2 = 2.2
    struct.pack_into(
        # =: native order, std. size & alignment
        # H: unsigned short
        # f: float
        "=Hff",
        buffer, 0, 1, float1, float2)
    writer.write(buffer)
    await writer.drain()
    print("Close the connection")
    writer.close()
async def main():
    server = await asyncio.start_server(handle, HOST, PORT)
    print(f'Serving on {server.sockets[0].getsockname()}')
    async with server:
        await server.serve_forever()
asyncio.run(main())

Client C# Unity

using System;
using System.IO;
using System.Net.Sockets;
using UnityEngine;
public class Connexion : MonoBehaviour
{
    public string server;
    public string message;
    public ushort port;
    private void Start()
    {
        // working sample to send text:
        byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
        byte isLittleEndian = BitConverter.IsLittleEndian ? (byte)1 : (byte)0;
        TcpClient client = new TcpClient(server, port);
        NetworkStream stream = client.GetStream();
        // Send the message to the connected TcpServer.
        stream.WriteByte(isLittleEndian);
        stream.Write(data, 0, data.Length);
        Debug.Log($"Sent: {message}");
        // read sample
        BinaryReader reader = new BinaryReader(stream);
        uint len = reader.ReadUInt16();
        var x = reader.ReadSingle();
        var y = reader.ReadSingle();
        Debug.Log("len=" + len);
        Debug.Log($"x={x}, y={y}");
    }
}

Pour la note, ces deux exemples paraissent simples, mais ils m’ont pris un temps fou, et je n’ai eu aucune réponse au bout de 3 semaines sur stackoverflow…

Python : EAFP vs LBYL

Très souvent vous pouvez avoir deux styles de codes différents qui font la même chose en Python :


import os

if os.path.exists("fichier.txt"):
    os.unlink("fichier.txt")

import os
try:
    os.unlink("fichier.txt")
except OSError:  # levé si le fichier n'existe pas
    pass

Alors, lequel choisir ?

Personnellement, j’ai toujours préféré le premier choix, et pourtant… dans la documentation officielle, ils le déconseillent !

Pourquoi cela ? Explication : l’opposé de EAFP, c’est LBYL.

EAFP : Easier to ask for forgiveness than permission

Plus facile de demander pardon que la permission. Ce style de codage très utilisé en Python suppose l’existence de clés ou d’attributs valides et intercepte les exceptions si l’hypothèse s’avère fausse. Ce style propre et rapide se caractérise par la présence de nombreuses déclarations try and except. La technique contraste avec le style LBYL commun à de nombreux autres langages tels que C.

LBYL : Look before you leap

Réfléchir avant d’agir.

Ce style de codage teste explicitement les conditions préalables avant d’effectuer des appels ou des recherches. Ce style contraste avec l’approche EAFP et se caractérise par la présence de nombreuses déclarations if.

Dans un environnement multi-thread, l’approche LBYL peut risquer d’introduire une condition de concurrence entre « la vérification » et « la validation ». Par exemple, le code : if key in mapping: return mapping [key] peut échouer si un autre thread supprime la clé du mappage après le test, mais avant la recherche. Ce problème peut être résolu avec des verrous ou en utilisant l’approche EAFP.

Django et git : bonnes pratiques / idées pour faire du CI

Conseil d’un ami :

  • gitlab n’est pas 100% opensource, ils proposent une édition communautaire limité et la totalité des fonctionnalités est dispo avec la version entreprise, leur modèle économique c’est de brider la version CEE (community) dans pas mal de coin pour te pousser à prendre une licence, perso je trouve que c’est plus un freeware qu’autre chose
  • gitlab est pas mal mais honnêtement pour l’avoir beaucoup (vraiment beaucoup) utilisé dans le passé ce n’est pas la meilleur alternative.

Si tu cherche à mettre en place un truc je te conseille fortement de jeter un œil à :

Cette stack est un peu plus compliquée à mettre qu’un gitlab, mais c’est très puissant, surtout la partie gerrit qui transcende la manière de faire des revues de code.

Si le code Django / Python n’est pas correct, il y a des méthodes pour améliorer les choses notamment :

  • https://nvie.com/posts/a-successful-git-branching-model/
  • https://semver.org/

Il est également important de mettre en place un système de « core developer » même au sein d’un projet privé en entreprise, afin de garantir la cohérence des données et l’intégrité de l’historique. En effet, si tout le monde à les droits d’écriture sur tout, ça finira toujours à un moment donné à partir dans tous les sens… L’idée, c’est de donner les droits d’écriture à seulement 2-3 personnes de confiance qui ont un certain niveau et à tous les autres imposer de faire des forks dans leur namespace gitlab et de proposer des merge request (l’équivalent des pull requests avec github). Ainsi, la revue de code est obligatoire et il n’est plus possible de merger du mauvais code… et les développeurs « standard » (sans droits donc) ont juste 2-3 commandes de base à connaître pour bosser avec les autres. Sans ce genre de système cela ne peut pas réellement fonctionner car git à été developpé pour ce mode de fonctionnement (au même titre que svn et d’autres gestionnaire de version) et donc son workflow est idéal dans ce cadre.

Fonctionner comme ça m’a permis de :

  1. coder un petit robot qui réagissait au merge requests proposé et donc qui checkait tout un tas de choses automatiquement et qui proposait des commandes si les choses n’allaient pas
  2. responsabiliser les gens de l’équipe en les rendant responsables de leur propre forks
  3. faire monter en compétence l’équipe qui était ultra frileuse à faire du versionning puis au final à pris goût au truc…

J’ai fais des petites formations au début pour leur expliquer l’intérêt et expliquer la sémantique des changement et qu’il est important de penser ses commits de manière propre. Cela permet :

  • dans certains cas de retirer (revert) des bugs complet d’un seul coup,
  • de faire du debug de manière automatique sans rien toucher et de trouver les changements responsable d’un bug (git bisect),
  • de faire un gain énorme de qualité au final
  • d’avoir des historiques clairs avec des messages de commit ayant une réelle plus value (exemple)
  • d’automatiser tout un tas d’actions qui vont augmenter la qualité globale du projet et vont réduire les taches inutiles et donc laisser plus de temps pour les trucs fun à faire.

C’est gagnant gagnant, mais au début les gens râlent… il faut juste s’y préparer et avec le temps ils changent d’avis !

Utiliser Chrome pour imprimer / générer un PDF

Google Chrome a plein d’options cachées en ligne de commande, et parmi celles-ci, une option qui permet d’imprimer en PDF.
De plus, si, comme moi, vous ne voulez pas les numéros de pages / nom de fichier en haut et en bas, il vous faut ajouter l’option --print-to-pdf-no-header

google-chrome --headless --disable-gpu --run-all-compositor-stages-before-draw --print-to-pdf-no-header --print-to-pdf=Bureau/test.pdf file://Bureau/git_book.html

Et vous aurez votre fichier PDF qui sera généré ;

[0730/112224.164336:INFO:headless_shell.cc(615)] Written to file Bureau/test.pdf

JavaScript : comment surcharger console.log()

J’avais besoin de décaler mes log pour voir rapidement si j’étais, ou pas, à l’intérieur d’une fonction.

Eh oui, après plusieurs années, même si on peut faire des points d’arrêt, JavaScript est là pour répondre à des événements utilisateurs et autres… donc il ne faut souvent pas arrêter pendant l’exécution ou la visualisation d’une page Web… on n’a toujours pas d’autre choix que de faire le bon console.log() des familles qui fait que le développement est extrêmement ralenti…

Je me suis donc crée les fonctions log_in(), log() et log_out().
Ainsi, quand j’entre dans une fonction, j’appelle log_in(), et tous mes log() dans cette fonction seront décalés, ce qui fait que je vois immédiatement où sont appelés mes logs ! Ensuite, ne pas oublier de faire un log_out().

let logLevel = 0;
let log = function () {
    let args = Array.from(arguments);
    if (logLevel > 0) {
        args = [Array(logLevel).fill(' ').join('')].concat(args);
    }
    console.log.apply(null, args);
};
let log_in = function () {
    log.apply(null, arguments);
    logLevel++;
}, log_out = function (s) {
    if (logLevel>0) {
        logLevel--;
    }
    log.apply(null, arguments);
};

Django : comment vérifier si on est en mode debug dans le template

Vous trouverez l’information dans beaucoup de sites, et cela semble très simple : dans votre template, il suffit de faire :

{% if not debug %}Je suis en debug !{% endif %}

En réalité cela ne suffit pas.

Il faut dans votre fichier settings.py, configurer correctement les adresses IP’s qui précisent qu’on est / ou pas / en mode « développement » :

INTERNAL_IPS = ['127.0.0.1', ]

(Notez que ce code peut être largement optimisé et dépendant de l’environnement, par exemple j’ai fait un settings.py qui prend cela en compte)

curl

Curl hints / aide

curl -v -H "Host:www.djangoproject.com" https://www.olivierpons.fr/