Vieux Perls et nouveau code

Sébastien Aperghis-Tramoni, sebastien@aperghis.net

Problématique

  • Pourquoi se soucier des vieux Perl ?

    • Parce qu'il y a énormément de code Perl caché qui tourne sur de vieux systèmes

    • Parce qu'on peut donc s'y trouver confronté

  • Pourquoi ne pas mettre Perl à jour ?

    • À cause de l'impact potentiel (souvent dur à estimer)

    • Parce que ce n'est pas toujours possible

Problématique

  • Missionné sur un poste d'expert Perl

  • But : maintenir du vieux code, développer du nouveau

  • Plate-forme : plus de 2000 serveurs

    • Certains ont été installés il y a plusieurs années

    • Vieux systèmes, vieux Perls

Une brève histoire de Perl

  • Perl 5.000 .. octobre 1994

  • Perl 5.001 .. mars 1995

  • Perl 5.002 .. février 1996

  • Perl 5.003 .. juin 1996

  • Perl 5.004 .. mai 1997

  • Perl 5.005 .. juillet 1998

Une brève histoire de Perl

  • Perl 5.6 .. mars 2000

  • Perl 5.8 .. juillet 2002

  • Perl 5.10 .. décembre 2007

  • Perl 5.12 .. JBDNF?

Terminologie

core

  • la distribution standard de Perl, l'ensemble des modules préinstallés avec Perl

pragma

  • module avec un nom en minuscule, souvent lexical, par exemple strict et warnings

Les problèmes de compatibilité

  • Au niveau du langage

    • version

    • syntaxe

    • fonctionnalités

  • Au niveau des modules

    • version

    • fonctionnalités

    • disponibilité

Version du langage

  • use 5.8 ou require 5.8

  • Origine de ce prérequis

    • ajouté par un outil de génération de code, par exemple h2xs

    • ajouté intentionnellement par l'auteur

Version du langage

  • Exemple

      maddingue@gwaihir:Speech-eSpeak-0.21 $ perl Makefile.PL && make all test
      Perl v5.8.8 required--this is only v5.8.7, stopped at Makefile.PL line 1.
      BEGIN failed--compilation aborted at Makefile.PL line 1.
  • "ZOMG!1! UR PERL IZ NOT TEH LATEST U L4M3RZ LOLOLOL!!1! BAI"

  • Yeah, sure

Version du langage

  • Pertinence

    • estimer la pertinence de ce prérequis

    • si non pertinent, on peut le retirer

    • on gagne

      • peut-être

    • si pertinent, il faut continuer à chercher

Version du langage

  • Exemple

    • use 5.008006 est typique de h2xs

    • Peut donc généralement être supprimé

Syntaxe

  • Mots-clés et opérateurs

  • Fonctions

  • Chaînes

  • Regexps

Syntaxe

  • Mots-clés et opérateurs

    • our (Perl 5.6)

    • state (Perl 5.10)

    • // (Perl 5.10 ou 5.8+DOR)

Syntaxe - Mots-clés

  • our

    • Généralement utilisé à tort et à travers

    • Utilise deux fois plus de mémoire que les lexicales ou les globales simples

    • Variables privées d'un package peuvent être déclarées avec my

          package TwoFlowers;
          my $config;

Syntaxe - Mots-clés

  • our

    • Variables globales à usage unique peuvent être placées dans un bloc no strict

          {   no strict 'vars';
              $VERSION = '1.23';
              @ISA = qw(Flower);
          }
    • Variables publiques déclarées avec vars

          use vars qw($DEBUG);

Syntaxe

  • Fonctions

    • open()

    • binmode()

Syntaxe - Fonctions

  • open()

    • Syntaxe très protéiforme avec plusieurs modes différents

    • Mais déjà en place depuis longtemps

    • Toutefois, deux ajouts récents sont couramment utilisés

Syntaxe - Fonctions - open()

  • open my $fh, $path

    • Fonctionalité introduite avec Perl 5.6

    • Syntaxe valide dans les anciens Perls

    • Mais $fh pas automatiquement affecté

Syntaxe - Fonctions - open()

  • open my $fh, $path

    • Solution : créer manuellement le descripteur de fichier

          use FileHandle;
      
          open my $fh = new FileHandle, $path or die $!;
    • ou, plus lisible

          use FileHandle;
      
          my $fh = new FileHandle;
          open $fh, $path;

Syntaxe - Fonctions - open()

  • open() avec 3 arguments

    • open($fh, '<', $path);

  • Syntaxe introduite avec Perl 5.6

  • Offre une protection contre la magie

Syntaxe - Fonctions - open()

  • open() avec 3 arguments

    • Malheureusement, le code suivant ne marche pas :-(

          my @args = ('>', $path);
          open($fh, @args);
    • Solution simple : retransformer en open() à 2 arguments

          open($fh, '<'.$path);
    • Problème : perte de la protection supplémentaire

Syntaxe - Fonctions - open()

  • open() avec 3 arguments

    • Seconde solution : FileHandle (depuis 5.002), ou IO::File (depuis 5.003_07)

          use FileHandle;
          my $fh = FileHandle->new($path, $mode);
          # where $mode is ANSI mode: r, w, r+, etc

Syntaxe - Fonctions

  • binmode()

    • À l'origine pour passer le descripteur de fichier en mode binaire

    • Peut maintenant être utilisé pour sélectionner la couche PerlIO (ajouté dans 5.6)

    • binmode(FILEHANDLE, LAYER)

    • Ne produit qu'un avertissement

Syntaxe

  • Chaînes

    • v-strings

    • qr//

Syntaxe - Chaînes

  • v-strings

    • Syntaxe introduite avec Perl 5.6

    • Tout usage autre que pour définir un numéro de version est à proscrire

Syntaxe - Chaînes - v-strings

  • Comparer des versions

    • Solution : revenir à de simples chaînes de caractères ou à des flottants

      • Avantage : technique éprouvée, utilisée depuis des années

      • Inconvénient : moins fiable

      • Exemple avec des flottants:

            if ($version > 2.034) {
                ...
            }

Syntaxe - Chaînes - v-strings

  • Comparer des versions

    • Solution : utiliser version (fonctionne à partir de 5.005_04, inclus dans le core 5.10)

      • Avantage : interface objet avec surcharge d'opérateurs

      • Inconvénient : ne fournit pas beaucoup véritablement de compatibilité en deçà de 5.6

      • Exemple:

            use version;
        
            my $vers1 = version->new("1.2.3");
            my $vers2 = version->new("1.2.5");
        
            if ($vers1 < $vers2) {
                ...
            }

Syntaxe - Chaînes - v-strings

  • Comparer des versions

    • Solution : use Perl::Version (pur Perl, fonctionne avec au moins 5.004)

      • Avantage : module objet classique, bien adapté pour manipuler des versions

      • Inconvénient : il faut numifier les objets pour les comparer

      • Example:

            use Perl::Version;
        
            my $vers1 = Perl::Version->new("1.2.3");
            my $vers2 = Perl::Version->new("1.2.5");
        
            if ($vers1->numify < $vers2->numify) {
                ...
            }

Syntaxe - Chaînes - v-strings

  • Afficher la version de Perl

    •   printf "version is v%vd\n", $^V;
    • peut être changé en

    •   if ($] > 5.006) {
            printf "version is v%vd\n", $^V;
        } else {
            printf "version is v%s\n", $];
        }

Syntaxe - Chaînes

  • qr//

    • Syntaxe introduite avec Perl 5.005

    • Souvent rencontrée dans les suites de test

      • Typiquement dans like( $result, qr/regexp/ )

      • Peut donc être remplacée par une chaîne simple sans risque fonctionnel

    • Utilisation dans du code fonctionnel

      • Si utilisé pour une bonne raison, ne peut être remplacé

      • Perte de lisibilité du code trop importante

      • Impact sur la maintenance

      • Voir Test::Harness 3.0, Regexp::Assemble, Regexp::Common

Syntaxe

  • Regexps

    • Assertions

    • Classes

    • Motifs étendus

Syntaxe - Regexps

  • Assertions

    • \z (Perl 5.005)

      • \Z est vraie en fin de chaîne ou avant un saut de ligne

      • \z est vraie en fin de chaîne

    • Sera vu comme "z" par Perl 5.004

    • Donc nécessité de protéger l'utilisation de cette assertion

          my $end = $] >= 5.005 ? "\\z" : "\\Z";
      
          substr($path, 0, -$ignore) =~ s/X(?=X*$end)/$CHARS[int(rand($#CHARS))]/ge;

Syntaxe - Regexps

  • Classes

    • Classes POSIX [:class:] (Perl 5.6)

      • Solution : remplacer par leur équivalent explicite, cf. perlre

    • Classes Unicode (Perl 5.6)

       \x{263a}  wide hex character
       \N{nom}   named character
       \pP  Match P, named property. Use \p{Prop} for longer names.
       \X   Match eXtended Unicode "combining character sequence",
            equivalent to (?:\PM\pM*)
       \C   Match a single C char (octet) even under Unicode.
      • Abandonnez tout espoir

Syntaxe - Regexps

  • Motifs étendus

    • (?#text) (Perl 5.003)

    • (?imsx-imsx) (Perl 5.003)

    • (?:pattern) (Perl 5.003)

    • (?=pattern) (Perl 5.003)

    • (?!pattern) (Perl 5.003)

Syntaxe - Regexps

  • Motifs étendus

    • (?<=pattern) (Perl 5.005)

    • (?<!pattern) (Perl 5.005)

    • (?>pattern) (Perl 5.005, experimental)

    • (?(condition)yes-pattern|no-pattern) (Perl 5.005, experimental)

    • (?{ code }) (Perl 5.005, experimental)

    • (??{ code }) (Perl 5.6, experimental)

Dépendance de modules

  • Dépendances possibles :

    • pragmas

    • modules du core

Pragmas

  • warnings

    • Introduit dans Perl 5.6

    • Active et désactive les avertissements de manière lexicale

          use warnings;
      
          sub init {
              no warnings 'redefine';
              *DEBUG = \&debug if $debug;
          }
    • Catégories d'avertissements personnalisées avec warnings::register

Pragmas

  • warnings

    • C'est la première cause d'incompatibilité avec les anciens Perl

    • Pourtant $^W est disponible depuis Perl 5.0

    • ...

Pragmas

  • warnings

    • C'était la première cause d'incompatibilité avec les anciens Perl

    • JFDI: warnings-compat

    • $CPAN/S/SA/SAPER/warnings-compat-0.04.tar.gz

          $ perl5.00405 -e 'use warnings; warnings::warnif "hello"'
          hello at -e line 1
          $ perl5.00405 -e 'no warnings;  warnings::warnif "hello"'
          $ 

Pragmas

  • warnings-compat

    • Quelques limitations, mais ça marche !

          use warnings::register;
      
          {
              no warnings 'redefine';
              ...
          }
      
          warnings::warnif "...";
    • Limitations :

      • Non lexicale

      • Catégories non utilisées

Pragmas

  • warnings

    • Autre solution : utiliser $^W directement

    • Exemple:

          package My::Module;
          local $^W = 1;

Pragmas

  • base

    • Introduit dans Perl 5.005

    • Établit une relation de dérivation de classe

    • Mais relativement complexe pour son but

    • Et en conséquence fragile

    • Déconseillé par plusieurs porters

Pragmas

  • base

    • Solution : remplacer par le code équivalent

          use base qw(Parse::Syslog);
    • devient

          use Parse::Syslog;
      
          {   no strict;
              push @ISA, qw(Parse::Syslog);
          }

Pragmas

  • base

    • Solution : use parent.pm

      • A été discuté et approuvé sur P5P

      • Simple, pur Perl, compatible avec 5.004

    • Exemple

          package LolPerl;
          sub exclaim { "I CAN HAS PERL" }
      
          package Cheezburger;
          use parent "LolPerl";

Pragmas

  • constant

    • Introduit dans Perl 5.004

    • Mais les fonctionnalités ont évolué

    • En particulier, les déclarations groupées, apparues dans Perl 5.8.0

Pragmas

  • constant

    • Du code comme

          use constant {
              PROTOCOL_NAME   => "http", 
              PROTOCOL_PORT   => 80, 
              PROTOCOL_TYPE   => "stream", 
          };
    • doit être réécrit :

          use constant PROTOCOL_NAME   => "http";
          use constant PROTOCOL_PORT   => 80;
          use constant PROTOCOL_TYPE   => "stream";

Pragmas

  • constant

    • Autre solution : mettre à jour le module

    • CPANifié après que Nicholas Clark a vu cette présentation à YAPC::Europe 2007

    • $CPAN/S/SA/SAPER/constant-1.15.tar.gz

    • Fonctionne sur Perl 5.005

Modules du core

  • Important : le core évolue !

  • Vérifier les dates et versions d'introduction

  • Module::CoreList, corelist

        $ corelist Data::Dumper
        Data::Dumper  was first released with perl 5.005

Modules du core

  • Vérifier aussi la disponibilité sur le CPAN

    • Un nombre croissant de modules sont à double vie

    • Des modules du CPAN sont régulièrement intégrés au core

    • Des modules du core sont régulièrement mis à disposition indépendamment sur le CPAN

Modules du core

  • Modules corifiés :

    • 5.004 : CGI, CPAN, IO (IO::File, IO::Socket, ...)

    • 5.005 : Test

    • 5.6 : File::Temp, Pod::Parser, Term::ANSIColor

    • 5.8 : Encode, Filter::Simple, Locale::Maketext, Scalar::Util, List::Util, Storable, Test::More

    • 5.10 : Module::Build, CPANPLUS and its numerous dependencies (Archive::Tar, IO::Zlib, Module::Pluggable, ...)

Modules du core

  • Modules cpanifiés :

    • 2001 : Test::Harness

    • 2002 : Safe, ExtUtils::MakeMaker, version

    • 2003 : Data::Dumper

    • 2004 : XSLoader

    • 2005 : Sys::Syslog

    • 2006 : ExtUtils::Install, threads, threads::shared, Exporter

    • 2007 : B::Lint, File::Path, constant, Pod::Html

Évolution

  • Modules du core évoluent aussi

    • Ajouts de fonctionnalités

    • Corrections de bugs

  • Mises à jour indépendantes de Perl

  • Alors faites-le !

Questions ?

Merci