User Tools

Site Tools


normesprogrammation:normesperl

Normes de code - A utiliser pour écrire du perl

Quand utiliser les normes de code ?

  • Les normes de code décrites ici doivent être utilisées dès lors que le programme dépasse 50 lignes de code, et qu'il ne peut donc plus être considéré comme un développement rapidégueu (“Quick & Dirty”).
  • Les noms des scripts développés en “mode rapidégueu” doivent commencer par les lettres qd_

Appliquer les normes de code est très important pour que nous obtenions des codes lisibles et aisément maintenables. Les scripts rapidégueu ne sont pas destinés à être maintenus, ils peuvent donc être écrits de manière moins rigoureuse… à condition qu'ils soient rapidement identifiés. Cela dit, il n'est pas interdit d'utiliser les normes de code aussi pour ces scripts.

Normes applicables à l'écriture d'un script perl (exécutable)

Un squelette

  • Avant de démarrer votre programme, récupérez ce squelette. Vous n'avez plus qu'à remplacer le code de ce squelette par votre code à vous.

Dans quelle langue écrire ?

  • Le code sera écrit exclusivement en anglais pour ce qui est des noms de variables, subs, etc.
  • Les commentaires et la documentation seront écrits de préférence en anglais, éventuellement en français.
    • Si les commentaires sont écrits en Français, n'utilisez pas les caractères accentués

Il est préférable d'écrire tout en anglais, afin de pouvoir aisément partager le code avec un collègue. Toutefois, mieux vaut une bonne documentation écrite en français qu'une mauvaise documentation écrite en anglais… d'autant plus qu'en cas de besoin, il est relativement simple de réécrire la documentation sans toucher au code, alors que traduire le code lui-même équivaut à refaire un développement.

Documentation générale du code

  • Il faut documenter le code, en utilisant le système (docpod ou doc de référence.
  • Afin que la mise en pages soit respectée par le pd, il faut mettre un espace en première colonne
  • Ne pas mettre de tabulations dans les commentaires. Mettre plutôt des espaces

Les directives pod pourront être relues par le programme perldoc, ou encore transformées en html par le programme pod2html. On peut voir un exemple de ce que cela donne, sur plusieurs scripts dédiés au traitement de données micro-arrays: on a uniquement inséré les pod lors du développement, puis exécuté sur chaque fichier la commande pod2html. Cela a permis de générer les fichiers de documentation html.

Documentation en début de fichier

  • Les directives à insérer en tête de fichier sont les suivantes:
=head1 NAME

myprog.pl - One line description

=head1 SYNOPSIS

myprog.pl [--switch] file1 file2 ...

=head1 DESCRIPTION

Complete program description, meaning and use of the switches, etc.
Pas de limite sur la longueur de ce paragraphe, il doit
être aussi concis et précis que possible.

=head1 SUBROUTINES

The list of all subroutines

=cut

Documentation avant chaque sub

  • Les directives à insérer avant chaque sub sont les suivantes:
=head2 fonction calc_moy

 Title        : ComputeMean
 Usage        : $mean = &ComputeMean($ra_lines)
 Prerequisite : none
 Function     : calcule la moyenne des colonnes 6 a 13 des lignes passees en parametre
 Returns      : une string 
 Args         : $ra_lines   Tableau comportant les lignes a analyser
 Access       : public, private, protected //(dans le cas d'une méthode d'objet)//
 TODO         : ce qu'il faudrait améliorer
 Globals      : none

=cut 
  • Un sub est une “boîte noire”; encore faut-il expliquer ce qui entre, ce qui sort, ce que fait le sub.
  • Ne pas oublier, en particulier, le mot-clé fonction ou procedure: une fonction renvoie quelque chose, une procédure ne renvoie rien.
  • Dans le cas d'une fonction, sur la ligne Usage doit figurer une expression du style $color = &SetColor($nb) avec des noms de variable informatifs

Indentation

  • Il faut indenter le code, c-à-d décaler chaque bloc d'une tabulation (ne pas remplacer les tabulations par des espaces)
  • Le style d'indentation doit respecter la forme suivante:
if (condition)
{
  ....
  ....
  ....
}
else
{
    ....
    ....
}

Alignement, espacement

  • Lorsqu'on doit mettre plusieurs lignes d'affectation de variables, s'arranger pour que les signes = soient dans la même colonne
  • Pour ce faire, mettre des espaces pas des tabulations
  • Mettre au moins un espace autour du signe = et dans les parenthèses lors d'une expression conditionnelle
my $v1                             = 40;
my $v23                            = 50;
my $tres_tres_long_nom_de_variable = 1000;
my $v2                             = 0;
if ( $v2 == 1 ) 
{
   ...bla...
}

Expressions conditionnelles

  • Lorsqu'on n'a qu'une seule instruction dans un bloc if ou unless, et pas de bloc else, on peut utiliser la forme suivante:
&do_something($with_this) if ($that == 0);

Espaces dans les expressions

  • Dans une affectation, mettre un ou plusieurs espaces entre les noms de variables et le signe =
  • Dans une expression, mettre un ou plusieurs espaces entre les noms de variables et l'opérateur, ainsi qu'entre l'expression et les parenthèses:
my $color            = 'white';
my $background_color = 'blue';

if ( $background_color eq 'black' ) 
{
    print "this is too sad\n";
}

Variables et constantes

strict

  • Tout programme doit commencer par la déclaration suivante:
use strict;

Cette directive oblige le programmeur à déclarer toutes ses variables; de la sorte, un grand nombre d'erreurs sont découvertes par le système lui-même (typos dans les noms de variables, en particulier).

warnings

  • Tout programme doit commencer par la déclaration suivante:
use warnings;

Un grand nombre de messages de messages seront générés, mais s'astreindre à les supprimer tous est une manière d'avoir un code propre…

Déclarations de variables

  • Utiliser my pour déclarer vos variables.
  • La fonction local ne doit pas être utilisée.
my $a;
my $color = "green";

Constantes

  • Les paramètres qui ne sont pas modifiés dans tout le code doivent être implémentés à l'aide de constantes
  • les noms de ces constantes doivent comporter seulement des lettres majuscules, des chiffres, ou le signe _
  • Pour référencer une constante par la suite dans le code, mettre un & avant le nom, comme dans &DIMENSION.
use constant DIMENSION     => 25;
...
$A = 3 * &DIMENSION;

Le caractère & est obligatoire dans certains cas, en particulier lorsque la constante est définie à l'intérieur d'un module, ou d'un fichier appelé par require. Le plus simple est donc de l'employer systématiquement.

Déclaration de Variables globales

  • Le code doit comporter le moins possible de variables globales.
  • La plupart des variables globales seront avantageusement remplacées par des constantes.
  • les variables globales déclarées dans un module doivent, être déclarées avec la directive use var
use vars qw($VAR_GLO_1 $VAR_GLO_2 $VAR_GLO_3);      # dans un module

my ($VAR_GLO_4,$VAR_GLO_5,$VAR_GLO_6) = (4,5,6);    # dans un script executable

Noms de variables, de subs, de classes:

  • Plus la portée d'une variable est longue, plus le nom doit être informatif.
  • On doit utiliser la politique de nommage suivante:
    • Les variables locales sont écrites en lettres minuscules ($sts).
    • Les variables globales et les constantes sont écrites en lettres majuscules, le nom doit être informatif ($GLOBAL_STATUS, HOST).
    • Un nom de variable peut être séparé en deux parties par un caractère _:
      • $lcl_hst pour une variable locale
      • $GLOBAL_OBJECT pour une variable globale
    • Les noms de classes (de modules) sont écrits en mettant en majuscule la première lettre de chaque mot (package Class, package OtherClass)
    • Les noms de subs sont écrits en mettant en majuscule la première lettre de chaque mot (comme une classe, donc) (sub ChangeColor)
  • Les règles suivantes s'appliquent pour nommer variables et subs:
    • Les noms de variables sont des noms communs ($color)
    • Les noms de subs commencent par un verbe (mais éviter de commencer par Do, trop vague) (sub ChangeColor)
    • Les noms de variables autres que les scalaires commencent par un préfixe:
      • a_ pour un tableau (array)
      • h_ pour un tableau associatif (hash)
      • ra_ pour une référence vers un tableau
      • rh_ pour une référence vers un hash
      • rs_ pour une référence vers un scalaire
      • rf_ pour une référence vers une fonction
      • o_ pour un objet.
      • fh_ pour un fichier ouvert
      • A_,H_,RA_,RH_,RS_,RF_,O_,FH_ pour l'équivalent en variables globales.

Initialisation de variables

my @a_name = (); # tableau
my $ra_ref = []; # reference vers un tableau
my %h_name = (); # hash
my $rh_ref = {};  # reference vers un hash

my $fh_input  = &GetStreamIn("$fileName");
my $fh_output = &GetStreamOut("$fileName");
my $o_object = LipmObject->New();

Les variables réservées @_, $_

  • Elles ne doivent pas être utilisées, sauf pour le passage des paramètres de sous-programmes.

En particulier, il est important de ne pas les utiliser dans le match d'expressions régulières ni dans les boucles: il est extrêmement difficile de relire un code qui repose sur l'utilisation de ces variables, elles ne doivent être utilisées que les pour les scripts qd_

Les variables de boucle

  • Des variables locales de boucle doivent être utilisées à chaque fois que c'est possible;
  • Leur nom doit être court et significatif
foreach my $i (@ARRAY)
{
   ...boucle...
}

for (my $j=0; $j<=$MAX; $j++)
{
   ... 
}

while (my $line=<$fh_file>) 
{ 
   ...
}


foreach my $key ( keys %{$rh_} )
{
  ...
}

Les expressions

  • Toute expression doit être écrite complètement; en particulier s'il s'agit de comparer une expression à 0, il faut écrire:
if ( 0 == $x ) 
{
   ...
}
  • S'il s'agit de comparer une expression à une chaine nulle, écrire:
if ( '' eq $x ) 
{
   ...
}
  • Dans tous les cas, éviter:
if ($x) 
{
   ...
}

Ces deux expressions ne sont pas équivalentes: si $x est undefined, il n'est pas égal à 0 et if ($x==0) reflètera cette situation. Par contre, if ($x) ne distinguera pas entre les valeurs 0 et undefined, ce qui peut causer des soucis.

Evaluation d'expressions régulières

  • Pour les expressions rationnelles utiliser de préférence ^ et $ comme début et fin d'expression (et pas \A et \Z)
  • Utiliser une variable locale dans l'évaluation d'expressions régulières, cette variable doit apparaitre juste après l'expression elle-même:
if ( $line =~ /^AC *(.+)/ ) 
{
   my $ac = $1;
};

et pas:

if ( /^AC *(.+)/ ) 
{
   ...
};
  • Si on doit récupérer un ou plusieurs champs après un match d'expression régulière, on utilisera la notation suivante:
my ($u1,$u2) = ( $line =~ /^AC *(.+) (.+)/ );
  • Si la même expression régulière est utilisée plusieurs fois, il peut être plus clair de la mettre dans une variable:
my $ac_regex = '^AC *(.+)';
if ( $line =~ /$ac_regex/ ) 
{
   my $ac = $1;
};
  • Lorsqu'on utilise les expressions régulières pour analyser un fichier, mettre en commentaire un exemple de la ligne qu'on est en train d'analyser

Cela permet de mieux comprendre ce que l'expression régulière est censée faire, la syntaxe des expressions régulières étant un peu compliquée

Descripteurs de fichiers

  • Utiliser systématiquement les fonctions GetStreamIn et GetStreamOut (de General.pm), qui gèrent les fichiers compressés ou non
my $fh_input  = &GetStreamIn("sequence.zip");

my $fh_output = &GetStreamOut("outsequence.xz");
print $fh_output ">seq1\nATTGTC\n";
$fh_output->close();
  • Ne pas oublier de fermer les fichiers ouverts : cela n'est pas indispensable, car dès qu'on sort de la portée le fichier sera immédiatement fermé. Mais cela clarifie le code.
    • Pour fermer le fichier, on peut soit appeler la méthode close(), soit affecter undef à la variable.

On ne peut pas passer un descripteur de fichiers en paramètres: On ne peut passer en paramètre qu'une variable, éventuellement un objet. Utiliser les entrées-sorties objets permet donc de passer des descripteurs de fichiers comme des variables ordinaires, l'utiliser systématiquement permet d'homogénéiser le code.

FIXME - REPERTORIER TOUS LES MODULES File::Copy etc.

Sous-programmes:

Fonction ou procédure ?

  • Une procédure est un sous-programme qui ne renvoie pas de valeur.
  • Une fonction est un sous-programme qui renvoie une ou plusieurs valeurs.
  • on doit respecter les conventions suivantes:
    • Une procédure possède une ou plusieurs instructions return;
    • Une fonction qui renvoie une seule valeur possède une ou plusieurs expressions du type return $a;
    • Une fonction qui renvoie plusieurs valeurs renvoie en fait une seule liste. On l'écrit donc avec des parenthèses: return ($a,$b,$c).

Imposer return pour une procédure assure que celle-ci renvoie bien la valeur undef. Sinon on renvoie la dernière expression évaluée, ce qui est assez perturbant.

Appel de fonctions ou procédures:

  • Appeler les subs en utilisant le symbole &
sub SomeFunction 
{
    ...
};

my $y = &SomeFunction(1);

Paramètres ou variables globales ?

  • Un sub ne doit pas modifier de variables globales. Au lieu de modifier une variable globale à l'intérieur d'un sub, on doit:
    • Passer la variable globale en paramètre
    • Dérouler le sub, en modifiant éventuellement la valeur passée
    • Renvoyer la valeur modifiée.

Passage de paramètres: pas trop

  • Le sub doit commencer par une ligne de passage de paramètres
  • Lorsqu'il y a peu de paramètres (en-dessous de 4 ou 5), on doit passer les paramètres de la manière indiquée ci-dessous
  • Après la ligne de passage de paramètres, sauter une ligne
sub SomeSub 
{
    my ($x, $y) = @_;

    bla bla
    ...
}
  • Dans le cas d'une méthode d'objet, le premier paramètre (implicite) est $self, on doit passer les paramètres comme cela:
sub SomeMethod
{
   my $self = shift;
   my ($x, $y) = @_;

   bla bla
}
...
my $result = $obj->SomeMethod($x,$y);

Passage de paramètres: beaucoup et avec des valeurs par défaut

  • Lorsqu'il y a un grand nombre de paramètres à passer à la fonction et qu'on désire affecter des valeurs par défaut à ces paramètres, on peut utiliser la manière suivante:
sub SelectFont {
   my %args = (font=>"times",
               size=>10,
               face=>"normal", @_);
   ...
}

&SelectFont (size=>20);

Exceptions

Les traitements d'erreur peuvent se faire de trois manières:

  • die
  • carp (cf. ci-dessous)
  • throw

La méthode throw, associée aux traitements d'exception par try…catch doit être préférée le plus souvent possible

Traitement d'exception en utilisant LipmError

  • Un exemple de programme principal figure ci-dessous
  • Des objets (de la lipmutils) permettent de réaliser un traitement d'erreur plus sophistiqué
use strict;
use warnings;

use Error ":try";

MAIN:
{
    try
    {
        &main();
    }
    catch Error::Simple with
    {
        my ($err) = @_;
        warn ($err->text());
        exit 1;
    }
    otherwise
    {
        my ($err) = @_;
        warn $err->text();
        exit 2;
    };
}

sub main
{
    my $a = 1;
    if ( $a != 0 )
    {
        throw Error::Simple("ERREUR");
    }
    print "bye\n";
}

Normes de code - A utiliser pour écrire un module perl

Un squelette

Avant de démarrer votre module, récupérez le module_exemple, et un programme principal_test. Vous n'avez plus qu'à remplacer le code de ce squelette par votre code à vous.

Numéro de version

Un module doit avoir toujours un numéro de version dans une variable $VERSION. Cette variable ne sera pas exportée (sinon on risque les conflits en cas d'utilisation de plusieurs modules). Ainsi, le programme utilisateur de ce module peut par exemple afficher au début:

print "using module.pm $module::VERSION\n";

Exportations

Un module comporte toujours 3 parties:

  • Importations d'autres modules
  • Exportations de fonctions-membres, éventuellement de variables ou de constantes
  • Implémentation

EXPORT_OK

Pour exporter fonctions, variables ou constantes, utiliser de préférence le tableau @EXPORT_OK: en effet, les objets décrits dans ce tableau ne seront exportés que en cas de besoin par le programme utilisateur, il est donc possible d'éviter qu'ils polluent l'espace de nom.

dans le module:

@EXPORT_OK = qw($VAR &CONST &fonction);

Dans le programme utilisateur:

use module qw($VAR &fonction); # Seules $VAR et &fonction sont exportees.

EXPORT

Dans ce tableau sont listés les fonctions, variables ou constantes qui seront toujours exportés. Attention de ne pas en mettre trop, pour éviter les risques de conflits.

dans le module:

@EXPORT = qw(&fonction1);

Dans le programme utilisateur:

use module;                  # seule fonction1 est importee
use module qw($VAR);         # seule $VAR est exportee
use module qw(:DEFAULT $VAR); # Tout ce qui est dans @EXPORT, ainsi que $VAR est exporte

Modules Objets

Classes de base

  • Si un objet est une classe de base, ou une classe qui prévoit des classes dérivées, la dérivation doit se faire de la manière suivante:
    • Le fichier de la lipmutils LipmObject.pm doit être accessible dans le chemin des bibliothèques: tous les objets doivent soit hériter de LipmObject.pm, soit hériter d'un object qui en hérite.
    • Les objets ne doivent doivent pas fournir de fonction New. S'ils ont besoin d'un constructeur, celui-ci doit s'appeler _Init. La fonction New est en effet fournie par LipmObject, entre autres choses cette fonction appelle elle-même _Init.

Ce système garantit que lors d'une chaine d'héritages complexe, les _Init de chaque classe de la chaine d'héritage seront correctement appelés.

Classes dérivées

  • On précisera qu'une classe dérive d'une classe de base en utilisant la directive use base:
use base qw(UneClasseDeBase UneautreClasseDeBase);

Constructeur et variables membres

Les variables membres (propriétés) de l'objet sont mises dans un hash appelé $self, lui-même initialisé par le constructeur. Le constructeur est une fonction du module, appelée New. Les variables membres obéissent au même système de nommage que les variables locales ordinaires, avec les contraintes supplémentaires décrites ci-dessous:

Variables publiques, protégés, privées

Il n'y a pas de contrôle d'accès à ce niveau en perl. Mais il est important de montrer à l'utilisateur de l'objet qui est privé, qui est protégé, qui est public. D'où la convention:

  • Variable privée: son nom doit commencer par __
  • Variable protégée: son nom doit commencer par _
  • Variable publique: son nom obéit aux mêmes règles de nommage qu'une variable local ordinaire.

Il est important d'accéder aux variables membres en utilisant les fonctions définies dans LipmObject et appelées _Get ou _Set: en effet, ces fonctions modifient le nom des varaibles privées, de sorte qu'il est possible d'avoir une variable appelée __X dans une classe, une variable appelée __X dans une classe dérivée, sans que ces deux variables entrent en conflit.

D'où le code suivant:

use base qw( LipmObject );

sub __Init 
{
   my $self = shift;

   $self->_Set('__priv',2);
   $self->_Set('prot',0);
   $self->_Set('pub',0);
   return $self;
}

Méthodes publiques, protégées, privées

La même règle de nommage s'applique aux méthodes (fonctions-membres) de l'objet. Ces méthodes sont des fonctions dont le premier paramètre est le hash $self, elles suivent donc les principes de nommage qui s'appliquent aux subs, avec en plus la règles des _ pour préciser le contrôle d'accès:

  • Les noms de méthodes sont écrits en mettant en majuscule la première lettre de chaque mot
  • Pour une méthode protégée, faire précéder le nom de _
  • Pour une méthode privée, faire précéder le nom de __
  • Les méthodes doivent être précédées d'un paragraphe POD de même que pour une fonction ordinaire.

ATTENTION: A l'inverse des attributs (voir ci-dessus), il n'est pas possible d'avoir deux fonctions privées qui aient le même nom dans une classe de base et dans sa classe dérivée. Cela est une limitation de perl.

Un exemple de classe

Le code suivant donne un exemple de classe (ici un type Complex, qui implémente les nombres complexes). On peut voir qu'il est parfaitement possible de mettre un module “en ligne”, sans créer de fichier séparé.

package Complex;

use strict;
use warnings;

use base qw(LipmObject);

BEGIN 
{
    our $VERSION="1.0"; 
}

# 
# FONCTIONS PUBLIQUES
#

=head2 f

onction _Init 

 Title        : Complexe::_Init
 Usage        : my $c = Complexe->New()
                my $c = Complexe->New(1);
                my $c = Complexe->New(1,1);
 Prerequisite : none
 Function     : constructeur d'un objet Complexe
                Si appelé sans paramètres, x=y=0
                Si appelé avec un paramètre, y=0
 Access       : Protege, mais appelé par _Init de LipmObject.pm
 Returns      : $self
 Args         : $x, $y (optionnels)
 Globals      : none

=cut  

sub _Init 
{ 
    my $self = shift; 

    my ($x,$y)     = (@_);

    $x = 0 unless(defined $x);  
    $y = 0 unless(defined $y);  
    $self->_Set('__x',$x);
    $self->_Set('__y',$y);
    $self->__CalcModule();  
    return $self;  
}; 
 
=pod 

=head2 fonction Real

 Title        : Complexe::Real
 Usage        : $c->Real(4);
                my $r = $c->Real();
 Prerequisite : none
 Function     : renvoie ou modifie la partie reelle
                Si appelé avec parametre, modifie x
                Si appelé sans parametre, renvoie la valeur de x
 Returns      : $x
 Args         : $x ou aucun
 Access       : public
 Globals      : none 

=cut 

sub Real 
{
    my $self = shift;
    my ($x)  = (@_);

    if (defined $x)
    {
	$self->_Set('__x',$x);
	$self->__calc_module();
    } else {
	$x = $self->_Get('__x';
    }
    return $x;
}

=head2 fonction Imaginaire

 cf. fonction Real 

=cut

sub Imaginaire
{
    my $self = shift;
    my ($y)  = (@_);

    if (defined $y) 
    {
	$self->_Get('__y',$y);
	$self->__CalcModule();
    } else {
	$y = $self->_Get('__y');
    }
    return $y;
}

=head2 fonction Module

 Title        : Complexe::Module
 Usage        : my $m = $c->Module();
 Prerequisite : none
 Function     : renvoie le module
 Returns      : le module
 Args         : none
 Access       : public
 Globals      : none

=cut

sub Module 
{
    my $self = shift;
    return $self->_Get('__m');
}

# 
# FONCTIONS PRIVEES
#

=head2 procedure __CalcModule

 Title        : CalcModule
 Usage        : $self->__CalcModule();
 Prerequisite : none
 Procedure    : calcule le module et stoque le resultat dans $self
 Returns      : none
 Args         : none
 Access       : private
 Globals      : none

=cut

sub __CalcModule 
{
    my $self = shift;

    my $x = $self->_Get('__x');
    my $y = $self->_Get('__y');
    my $m = sqrt($x*$x+$y*$y);
    $self_>Set('__m',$m);
}

======================================
package main;
use strict;
use warnings;

MAIN:
{
    my $zero = Complex->New();
    my $un   = Complex->New(1);
    my $c1   = Complex->New(3,4);
    print "Essai de l'objet Complex version ".$Complex::VERSION. " \n";
    print "module de c1 (3,4)           = ".$c1->Module()."\n";  
    $c1->Real(5);
    print "nouvelle partie reelle de c1 = ".$c1->Real()."\n";
    print "nouveau  module de c1        = ".$c1->Module()."\n";
    print "module de zero               = ".$zero->Module()."\n";

    print "module de un                 = ".$un->Module()."\n";
}

Messages d'erreur et déboguage

Le cas général: utiliser Les exceptions

  • Les modules sont le lieu où utiliser l'instruction throw pour traiter les cas d'erreur.
  • Trois types d'erreur peuvent actuellement être utilisées:
    • Error::simple
    • LipmError::IOException
    • LipmError::ParameterException.pm
  • Certaines erreurs doivent à tout prix arrêter le programme. Dans ce cas, le système carp ou confess doit être utilisé (mais pas die ou warn)

Cas particuliers: carp, cluck, croak, confess:

carp est un remplacement de warn, plus malin en ce sens qu'il va chercher à afficher un numéro de ligne de manière “intelligente”. De même, croak remplace die. Enfin, cluck (non exporté par défaut, il faudra donc l'appeler par Carp::cluck) et confess sont d'autres remplacements de warn et die, qui impriment l'intégralité de la pile. Une option de perl permet enfin de s'arranger pour que carp fonctionne de la même manière que cluck, et que croak se comporte en fait comme confess… le tableau suivant résume la situation:

Appel normal de perl
Affichage de la ligne de l'instruction Affichage d'une ligne d'appel Affichage de la pile d'appel complète
Warnings warn carp cluck
Erreur die croak confess
Appel normal perl par perl -MCarp=verbose
Warnings warn - carp, cluck
Erreur die - croak,confess

Quelle ligne d'appel choisir ?

carp (respectivement croak) font un choix, réputé intelligent, dans la pile d'appel, afin de présenter à l'utilisateur un numéro de ligne dont on pense qu'il peut apporter une information utile. Comment ce choix est-il fait ?

  • Les appels en provenance du module dans lequel se trouve la fonction, ou des modules dont il hérite, sont ignorés
  • Les appels en provenance de fonctions internes à perl sont ignorés
  • La première fonction ne répondant pas à ces deux critères est considérée comme la source possible de l'erreur, et le numéro de ligne correspondant est imprimé.

Cela est intéressant si l'on développe un module. Si l'on se contente de développer un seul script (le module main), ces règles ne pourront s'appliquer correctement, puisque tous les appels viendront du même module. Dans ce cas, la pile d'appel complète est affichée.

Tests

Tester vos classes. Voir des exemples dans lipmutils/t/

Modules objets Lipm "clé en main"

Utiliser les modules Lipm plutôt que de redévelopper vos bidouilles. Les indispensables (lipmutils/corelib):

  • General Fonctions génériques utiles
  • GeneralBioinfo Fonctions pour la bioinfo
  • ParamParser Pour lire ou écrire des paramètres de ligne de commande, de cgi, d'environnement, depuis des fichiers ,…
  • WebBuilder Pour écrire des scripts cgi: affiche les menus et sous-menus, appelle les callbacks, gère les paramètres cachés, etc. Dialogue également avec les objets javascript de lipmutil. La doc d'installation sur votre poste de travail, histoire de bricoler un peu.

Les autres (lipmutils/lib/) :

  • Authentic Gestion de l'authentification
  • Logger Objet permettant générer des “logs” de manière structurée, il est donc simple de les lire (à l'oeil ou à l'ordi)
  • AnotherTemplate Remplir des templates dans lesquels vous aurez mis des tags

Modules Perl à utiliser

  • Pour la sérialisation, utiliser Data::Serializer qui propose une abstraction de la grande majorité des méthodes existantes

Outils...

La rubrique Outils propose quelques outils permettant de générer la documentation, etc. En particulier, il serait souhaitable que tous les scripts se retrouvent sur un site web, après génération du code au format html grâce à perltidy. Pour cela, il faut utiliser le script “maison” perlclean qui se trouve dans cette rubrique.

Normes définies à titre expérimental

Commutateurs

-T

Ce commutateur est particulièrement intéressant lorsqu'on écrit des scripts cgi, car il passe alors en mode “paranoïaque”: toutes les données fournies par l'utilisateur sont “teintées”, ce qui signifie qu'elles sont considérées comme “mauvaises” (dangereuses), à moins que le programmeur ne les marque délibérément comme bonnes. Pour “blanchir” une variable teintée, il est nécessaire de faire un match contre une expression régulière, ce qui donne l'occasion de tester s'il y a des caractères “illégaux”. Voir perlsec pour plus d'informations.

if ($data =~ /^(\d)$/) {           # On autorise seulement les chiffres
   $data = $1;                     # $data now untainted
} else {
   die "Bad data in $data";        # log this somewhere
}

Arborescence modèle d'un projet

MONPROJET
│
├── bin
│ ├── ext : les programmes/scripts récupérés dans d'autres projets (ex: ncbi-blast, paraloop)
│ └── int : les programmes/scripts développés dans le cadre de MONPROJET
│
├── cgi : les scripts CGI de MONPROJET (sous contrôle Apache)
│
├── doc : la documentation du projet, les procédures
│
├── corelib : La corelib de lipmutils (ParamParser, Appli, WebBuilder)
│
├── lib
│ ├── ext : les librairies/modules récupérés dans d'autres projets (ex: BioPerl, lipmutils/lib)
│ └── int : les librairies/modules développés dans le cadre de MONPROJET
│
├── site
│ ├── cfg : Fichiers de configuration de l'instance de MONPROJET
│ ├── prj : les données de l'instance de MONPROJET
│ └── tmp : le répertoire temporaire (pour sessions web, donc droits pour apache notamment)
│
└── web : LES DOCUMENTS WEB STATIQUES
  ├── img : Images pour le web
  ├── js
  │ ├── ext : Librairies JavaScript récupérées dans d'autres projets (ex: YUI, LipmAjax)
  │ └── int : Librairies JavaScript développées dans le cadre de MONPROJET
  ├── css : Feuilles de style CSS
  ├── html : Pages HTML (aides, doc online)
  ├── xml : Structure XML du portail web (cf. WebBuilder.pm)
  └── xsl : Feuilles de style XSL

Auteurs

Emmanuel Courcelle, Sébastien Carrere, Jérôme Gouzy, Ludovic Cottret, Erika Sallet avec la collaboration d'Olivier Stahl et Sébastien Letort

normesprogrammation/normesperl.txt · Last modified: 2013/02/05 09:36 by sallet