Journal Exit Pyth(on|ran)2

Posté par (page perso) . Licence CC by-sa.
41
10
jan.
2020

Demat'iNal,

parmi les vœux de 2020, il y en a un qui ne devrait pas passer inaperçu tellement il est attendu depuis longtemps. Je veux bien évidement parler de l'abandon officiel du support de Python2 au profit de Python3.

Les plus abasourdis pourront lire le python 3 statement. Et tout particulièrement cette phrase :

We will then be able to simplify our code and take advantage of the many new
features in the current version of the Python language and standard library.

On nous promet une grande simplification… qu'en est-il ? Je vous propose une petite étude de cas à travers un exemple qui, à défaut d'être représentatif, est cher à mon cœur. Je veux bien évidement parler du compilateur pythran.

La version 0.9.5, sortie il y a peu, était la dernière à être compatible avec Python 2.7 et Python 3. Depuis j'ai commis (du verbe commettre, et pas commiter hein) 97ea22f7, sobrement intitulé

Remove all reference to py2 code and behavior from pythran

Cette phrase anodine cache pas mal de choses, notamment

678 changed files with 2,376 additions and 3,555 deletions

Et

code and behavior

Car oui, Pythran devait, dans une représentation commune, modéliser le code python2 et python3, d'où pas mal de changement et simplifications à la clef.

Les simplifications mises en œuvre ont donc été :

  1. Suppression de tout le code du style if sys.version_info.major == 2:, et de son équivalent C++ #if defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION < 3. Exit les from __future__ import itou.

  2. Renommage de __builtin__ en builtins.

  3. Suppression de fonctions et types non-supportées en Python3 comme cmp, xreadlines, StandardError, itertools.i(map|zip|filter) etc. Déplacement de certaines fonctions, notamment __builtins__.reduce dans functools.reduce.

  4. Modification de la sémantique de certaines fonctions : dict.(items|values|keys), range, zip etc. Heureusement, la version C++ de ces fonctions existaient déjà pour implémenter dict.iter(items|values|keys), xrange, itertools.izip, respectivement.

  5. Modification de certaines optimisations, comme la transformation de listes en générateur, vu que la sémantique de certaines fonctions a changé (cf. point 4.)

  6. Diminution notable du temps de validation, vu qu'on valide maintenant uniquement sur python 3 :-)

Alors, certes, l'impact sur les utilisateurs n'est pas négligeable, certains d'entre eux étaient peut-être encore scotché à Python2, et enlever le scotch, bah ça fait mal à la pilosité. Mais comme numpy me précède de un an, et ne fournit plus de mise à jour pour Python 2 ce n'est pas bien grave !

  • # Question

    Posté par . Évalué à 6.

    On nous promet une grande simplification… qu'en est-il ?

    Tu ne répond pas clairement à cette question. Tu explique que c'est plus simple de gérer python3 que python2+python3, mais à part cela ? Est-ce qu'il y avait des comportement complexes à implémenter avec python2 et qui sont grandement facilité dans python3 ?

    • [^] # Re: Question

      Posté par (page perso) . Évalué à 6.

      Tu explique que c'est plus simple de gérer python3 que python2+python3

      Oui ! Content que l'idée soit passée

      Est-ce qu'il y avait des comportement complexes à implémenter avec python2 et qui sont grandement facilité dans python3 ?

      Bien que cette question soit (à mon sens) vaine (Python3 est là, Python2 est mort, à quoi bon remuer le couteau dans la plaie), je peux donner un avis non éclairé. Pas de grosse simplification, dans la cadre de Pythran, plutôt de l'harmonisation et du cosmétique, à part la différenciation str/bytes qui ajoute de la complexité apparente à Python3. Je dis bien apparente car elle force au final à raisonner de manière plus précise sur nos données, et une fois ce travail fait, on est bien dans les clous, alors qu'en Python2 ça pouvait exploser sur le tard.

      Ensuite Pythran a au final un usage assez simple de Python, donc n'est pas forcément représentatif.

      Ah si, s'il fallait ralouiller un peu, je trouve les stacktrace Python2 plus lisibles que leur version Python3.

      • [^] # Re: Question

        Posté par (page perso) . Évalué à 2.

        […] à part la différenciation str/bytes qui ajoute de la complexité apparente à Python3. Je dis bien apparente car elle force au final à raisonner de manière plus précise sur nos données […]

        Il y a quelque temps, j'ai développé des fonctions pour faire transiter des chaînes de caractères via des sockets. Bien que je n'avais quasiment jamais fait de Python, j'ai trouvé la version pour Python 2 assez facile à écrire. Par contre, pour la version Python 3, ça a quand même été un petit peu plus compliqué.

        Voilà les deux versions ; on ne sait jamais, ça servira peut-être à quelqu'un…

        (Oui, je sais, je n'aurais pas du utiliser le camelCase, mais je débutais en Python, et n'avais pas connaissance des règles en la matière…)

        La version pour Python 2 :

        def writeByte(socket, byte):
            socket.send(chr(byte))
        
        def writeSize(socket, size):
            result = chr(size & 0x7f)
            size >>= 7
        
            while size != 0:
                result = chr((size & 0x7f) | 0x80) + result
                size >>= 7
        
            socket.send(result)
        
        def writeString(socket, string):
            writeSize(socket, len(string))
            socket.send(string)
        
        def writeStringNUL(socket, string):
            socket.send(string + "\0")
        
        def getByte(socket):
            return ord(socket.recv(1))
        
        def getSize(socket):
            byte = getByte(socket)
            size = byte & 0x7f
        
            while byte & 0x80:
                byte = getByte(socket)
                size = (size << 7) + (byte & 0x7f)
        
            return size
        
        def getString(socket):
            size = getSize(socket)
        
            if size:
                return socket.recv(size)
            else:
                return ""

        et la version pour Python 3 :

        def writeByte(socket, byte):
            socket.send(bytes([byte]))
        
        def writeSize(socket, size):
            result = bytes([size & 0x7f])
            size >>= 7
        
            while size != 0:
                result = bytes([(size & 0x7f) | 0x80]) + result
                size >>= 7
        
            socket.send(result)
        
        def writeString(socket, string):
            bString = bytes(string, "utf-8")
            writeSize(socket, len(bString))
            socket.send(bString)
        
        def writeStringNUL(socket, string):
            socket.send(bytes(string + "\0", "utf-8"))
        
        def getByte(socket):
            return ord(socket.recv(1))
        
        def getSize(socket):
            byte = getByte(socket)
            size = byte & 0x7f
        
            while byte & 0x80:
                byte = getByte(socket)
                size = (size << 7) + (byte & 0x7f)
        
            return size
        
        def getString(socket):
            size = getSize(socket)
        
            if size:
                return socket.recv(size).decode("utf-8")
            else:
                return ""

        (Code source extrait respectivement de https://github.com/epeios-q37/atlas-python/blob/be77cf9d5c8921877fecc1ed1bfa3819fc7e7546/atlastk/XDHqDEMO2.py et https://github.com/epeios-q37/atlas-python/blob/be77cf9d5c8921877fecc1ed1bfa3819fc7e7546/atlastk/XDHqDEMO3.py.)

        Toolkit Atlas : un moyen simple et rapide pour ajouter une interface graphique à vos programmes Java, Node.js, Python… (voir page perso) !

        • [^] # Re: Question

          Posté par (page perso) . Évalué à 10.

          La différence dans Meld pour les curieux plus fainéants que moi :
          Titre de l'image

          Adhérer à l'April, ça vous tente ?

          • [^] # Re: Question

            Posté par (page perso) . Évalué à 8. Dernière modification le 10/01/20 à 14:40.

            Bonne idée, mais c'est un peu trop petit…

            Je tente avec du Markdown :

             def writeByte(socket, byte):
            -       socket.send(chr(byte))
            +       socket.send(bytes([byte]))
            
             def writeSize(socket, size):
            -       result = chr(size & 0x7f)
            +       result = bytes([size & 0x7f])
                    size >>= 7
            
                    while size != 0:
            -               result = chr((size & 0x7f) | 0x80) + result
            +               result = bytes([(size & 0x7f) | 0x80]) + result
                            size >>= 7
            
                    socket.send(result)
            
             def writeString(socket, string):
            -       writeSize(socket, len(string))
            -       socket.send(string)
            +       bString = bytes(string, "utf-8")
            +       writeSize(socket, len(bString))
            +       socket.send(bString)
            
             def writeStringNUL(socket, string):
            -       socket.send(string + "\0")
            +       socket.send(bytes(string + "\0", "utf-8"))
            
             def getByte(socket):
                    return ord(socket.recv(1))
            @@ -65,6 +66,8 @@
                    size = getSize(socket)
            
                    if size:
            -               return socket.recv(size)
            +               return socket.recv(size).decode("utf-8")
                    else:
                            return ""

            Toolkit Atlas : un moyen simple et rapide pour ajouter une interface graphique à vos programmes Java, Node.js, Python… (voir page perso) !

            • [^] # Re: Question

              Posté par (page perso) . Évalué à -1. Dernière modification le 10/01/20 à 17:26.

              Bonne idée, mais c'est un peu trop petit…

              Change de CSS. ;-)

              Adhérer à l'April, ça vous tente ?

            • [^] # Re: Question

              Posté par (page perso) . Évalué à 3.

              pourquoi passer de str/chr à bytes ? ça j'ai pas suivi…

              pourquoi ajouter decode("utf-8") alors qu'un use utf-8 suffirait en en-tête ? (bientôt obsolète j'espère)

              • [^] # Re: Question

                Posté par (page perso) . Évalué à 2. Dernière modification le 11/01/20 à 10:45.

                pourquoi passer de str/chr à bytes ? ça j'ai pas suivi…

                Certaines fonctions qui prenaient un string en version 2 n'acceptent plus qu'un objet bytes en version 3, comme la méthode send de l'objet socket (v2, v3).

                pourquoi ajouter decode("utf-8") alors qu'un use utf-8 suffirait en en-tête ? (bientôt obsolète j'espère)

                "utf-8" est inutile, étant la valeur par défaut du paramètre correspondant, mais le decode() est nécessaire pour convertir l'objet bytes retourné par socket.recv(…) en string.

                J'ai voulu essayer use …, mais je n'ai pas trouvé de documentation à ce sujet…

                Toolkit Atlas : un moyen simple et rapide pour ajouter une interface graphique à vos programmes Java, Node.js, Python… (voir page perso) !

              • [^] # Re: Question

                Posté par (page perso) . Évalué à 6.

                pourquoi ajouter decode("utf-8") alors qu'un use utf-8 suffirait en en-tête ?

                Parce que c'est du Python et pas du Perl ?

              • [^] # Re: Question

                Posté par (page perso) . Évalué à 8. Dernière modification le 11/01/20 à 14:37.

                decode("utf-8")

                Car l'éventuelle directive d'encodage au début du fichier (le # coding: utf8) indique comment sont codées les chaînes de caractère dans le fichier source Python, elle ne spécifie rien sur les données qui entrent/sortent par ailleurs (fichiers, console, SGBDR, flux réseau, etc). En Python3 il faut explicitement se poser la question (donc avoir la connaissance de l'encodage… ou se rendre compte qu'il manque une information dans le cahier des charges).

                Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

                • [^] # Re: Question

                  Posté par . Évalué à 2.

                  En Python3 il faut explicitement se poser la question (donc avoir la connaissance de l'encodage… ou se rendre compte qu'il manque une information dans le cahier des charges)

                  Il y a des langages ou l'encoding se fait tout seul? :)

        • [^] # Re: Question

          Posté par (page perso) . Évalué à 3.

          Mais ton code Python 2 fonctionne-t-il avec du texte contenant des accents ou autres caractères bizarres ? À vue de nez, je dirais que non.

          • [^] # Re: Question

            Posté par (page perso) . Évalué à 3.

            Je viens d'essayer avec Python 2.7.15+ avec les caractères ⯈⯈⯈ éàôö…, et ça fonctionne sans problème…

            Toolkit Atlas : un moyen simple et rapide pour ajouter une interface graphique à vos programmes Java, Node.js, Python… (voir page perso) !

            • [^] # Re: Question

              Posté par (page perso) . Évalué à 5.

              Entre un Python 2.7 sous Windows configuré pour la console en cp1512 et un autre sous Linux configuré pour la console en utf8, ça fonctionne aussi ?

              Car c'est bien le problème avec Python2.x et les chaînes, c'est au petit bonheur la chance, ça fonctionne… jusqu'à ce que ça crash avec une erreur d'encodage/décodage (sauf à avoir été hyper clean et à avoir fait systématiquement de la conversion str de/vers unicode… comme dans Python3 bytes de/vers str finalement).

              Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

              • [^] # Re: Question

                Posté par (page perso) . Évalué à 1.

                En fait, les chaînes de caractères qui transitent par ces fonctions sont uniquement récupérées de formulaires HTML et affichées dans des pages HTML, tous encodés en utf8 ; elles ne sont jamais lues à partir de la console, ou écrites dans la console. C'est peut-être pour cela que je n'ai jamais rencontré de problèmes…

                Toolkit Atlas : un moyen simple et rapide pour ajouter une interface graphique à vos programmes Java, Node.js, Python… (voir page perso) !

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.