Vieux Perl 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 ?

    • Parce qu'il y a souvent des contraintes et 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 1000 serveurs

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

    • Vieux systèmes, vieux Perls

Un peu d'histoire

  • 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

  • Perl 5.6 : mars 2000

  • Perl 5.8 : juillet 2002

  • Perl 5.10 : Noël 2006 ?

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

  • Pertinence

    • si non pertinent, on peut retirer ce prérequis et gagner

      • 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

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

          package TwoFlowers;
          my $config;
    • Variables globales à usage unique peuvent être placées dans un bloc no strict

          {   no strict;
              $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

  • open(FILEHANDLE, $path) vs open my $fh, $path

    • Fonctionalité introduite avec Perl 5.6

    • Syntaxe valide dans les anciens Perls

    • Mais $fh pas automatiquement affecté

    • Solution : créer manuellement le descripteur de fichier

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

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

Syntaxe

  • open() avec 3 arguments

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

  • Syntaxe introduite avec Perl 5.6

  • Seule solution simple : retransformer en open() à 2 arguments

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

  • Note : le code suivant ne marche pas :-(

        my @args = ('>', $path);
        open(FH, @args);

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

    • Cas non rencontré personnellement

Syntaxe - Chaînes

  • v-strings

    • Solution : utiliser version (fonctionne à partir de 5.005_04)

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

      • 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

    • 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) {
                ...
            }
      • Exemple avec des chaînes :

            if ($version gt "2.034") {
                ...
            }

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

      • Le remplacement de qr// risque surtout d'entraîner une diminution de la lisibilité du code, par exemple sur une grosse regexp écrite avec qr/.../x

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.006 ? "\\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}  caractère hexadecimal étendu
       \N{nom}   caractère nommé
       \pP  Reconnaît la propriété P (nommée). Utiliser \p{Prop} pour des noms longs
       \X   Reconnaît le séquence de caractères Unicode étendue équivalent a (?:\\PM\pM*)
       \C   Reconnaît un caractère d'un seul octet même sous utft8.

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)

    • (?<=pattern) (Perl 5.005)

    • (?<!pattern) (Perl 5.005)

Syntaxe - Regexps

  • Motifs étendus

    • (?>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

    • modules du CPAN

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

    • 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.03.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

  • 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

Pragamas

  • base

    • Solution : remplacer par le code équivalent

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

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

Pragmas

  • constant

    • Introduit dans Perl 5.004

    • Mais les fonctionnalités ont évolué

    • En particulier, les déclarations groupées :

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

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

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
  • 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

    • 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 : version, CPANPLUS et ses nombreuses dépendances (Archive::Tar, IO::Zlib, Module::Pluggable)

Modules du core

  • Modules cpanifiés :

    • 2001 : Test::Harness

    • 2002 : Safe, ExtUtils::MakeMaker

    • 2003 : Data::Dumper

    • 2004 : XSLoader

    • 2005 : Sys::Syslog

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

Évolution

  • Modules du core évoluent aussi

    • Ajouts de fonctionnalités

    • Corrections de bugs

  • Mises à jour indépendantes de Perl

Propagande

  • Utiliser des modules du CPAN

  • Utilisez des modules du CPAN

    • Ceci est un ordre, pas une requête

  • Pas de raison de s'en priver

    • L'argument « je veux me limiter aux modules du core » n'est pas recevable

Déploiement

  • Solution simple et efficace :

    • placer les modules utilisés dans un répertoire privé

    • use lib

  • Utilisation de paquets conseillée pour le déploiement et la gestion

Disponibilité

  • CPAN

    • Les anciennes versions des modules à double vie sont souvent conservés sur le CPAN

  • BackPAN

Questions ?

Merci