Retrouvez cet article dans : Linux Magazine 98
Depuis le numéro 59, les Mongueurs de Perl vous proposent tous les mois de découvrir les scripts jetables qu’ils ont pu coder ou découvrir dans leur utilisation quotidienne de Perl. Bref, des choses trop courtes pour en faire un article, mais suffisamment intéressantes pour mériter d’être publiées. Ce sont les perles de Mongueurs.
Détecter la disponibilité de modules à l’exécution
Pour utiliser des modules externes en Perl, il y a le classique use. Le problème est que, si le module n’existe pas, le programme ne sera même pas compilé. Il y a pourtant des cas où le module n’est nécessaire que pour une fonctionnalité optionnelle du programme. Celui-ci fonctionnera alors dans un mode dégradé, mais il faut pouvoir détecter que l’on est dans ce cas pour effectuer les opérations nécessaires (comme griser un élément dans une interface graphique).
Il faut donc, pendant l’exécution, détecter si un module est présent ou non pour savoir qu’il faut désactiver certaines parties (ou faire n’importe quel autre traitement). Cela peut principalement être fait de deux manières.
La méthode manuelle
Quand on parle de déplacer un problème de la phase de compilation à celle d’exécution, on pense normalement à eval. Ce mot-clé peut être utilisé de deux manières, en le faisant suivre par un bloc ou par une chaîne. La version avec bloc ne permet pas de contourner ce qui nous intéresse ici, car son contenu est compilé en même temps que tout le reste. On va donc utiliser la version avec une chaîne qui contiendra un bout de code Perl qui ne sera compilé qu’au moment de l’exécution.
Si un problème survient pendant la compilation de ce qui est passé à eval, la variable $@ contiendra une description de ce problème. Dans le cas contraire, elle sera vide. On peut alors facilement tester la présence d’un module de cette manière :
eval "use Module::A::Tester";
if ($@)
{
# Code à exécuter si le module est absent
}
else
{
# Ici tout va bien
}
Dans le else, on peut aussi tester le numéro de version en regardant la variable $Module::A::Tester::VERSION.
On peut également utiliser un bloc eval avec require plutôt que use de cette manière :
eval {
require Module::A::Tester;
import Module::A::Tester;
};
Cela marchera comme précédemment, si ce n’est que le nom du module ne peut être déterminé à l’exécution, car le comportement de require est d’alors considérer cela comme un nom de fichier et non pas de module. Il faudrait alors utiliser le module Module::Util avec sa fonction module_path pour convertir le nom du module.
On peut mettre tout ceci dans une fonction qui prendra en entrée un nom de module et un numéro de version facultatif. Elle retournera une valeur vraie si le module est disponible, une valeur fausse sinon. Un petit exemple suit la définition de la fonction. Il affichera Module Présent ou Module Absent selon les cas.
sub checkModule
{
my ($module, $version) = @_;
eval "use $module $version";
return 0 if $@;
return 1;
} print ‘Module ‘ .
(checkModule(‘HTML::Parser’, 3.55) ?
‘Présent’ : ‘Absent’) ."\n";
Son fonctionnement est assez simple. On essaye d’abord de charger le module avec eval. Si on a une erreur, pas besoin d’aller plus loin et on retourne 0. Si on a passé cette phase, le module est bien disponible. On va alors immédiatement retourner que tout va bien si le numéro de version n’est pas spécifié. Sinon, on teste qu’il est bien plus grand que la valeur en entrée.
Cette méthode n’est utile que si on utilise réellement le module ensuite et non pas seulement pour tester sa présence. En effet, les éventuels blocs BEGIN présents seront exécutés.
La méthode avec un module CPAN
Il existe un module CPAN qui permet de faire à peu près la même chose. Comme la plupart des modules déjà existants, il a été plus testé et gère plus de cas que la simple fonction précédente. Ce module est UNIVERSAL::require. Son utilisation crée une fonction require qui s’utilise dans un style objet. Pour reprendre l’exemple précédent :
use UNIVERSAL::require; my $module = ‘HTML::Parser’;
print ‘Module ‘ .
(($module->require(3.55)) ?
‘Présent’ : ‘Absent’) ."\n";
Le message d’erreur de chargement, si require a renvoyé une valeur fausse, est présent dans $@ ou dans $UNIVERSAL::require::ERROR.
Par rapport à un use, ceci n’aura pas appelé la fonction import() du module. Il faudra donc éventuellement l’appeler ensuite explicitement, si require s’est bien passé, comme ceci :
$module->use;
Un module équivalent s’appelle Module::Load::Conditional avec la fonction can_load(). On lui passe en paramètre une référence vers un hash qui contient la liste des modules à tester comme clés et les numéros de versions pour les valeurs ou undef pour ne pas la vérifier.
L’exemple précédent devient alors :
use Module::Load::Conditional qw(can_load);
my $modulesATester = {
‘HTML::Parser’ => 3.55 };
print ‘Module ‘ .
(can_load(modules => $modulesATester) ?
‘Présent’ : ‘Absent’) ."\n";
On aurait pu indiquer plusieurs modules dans $modulesATester. La fonction renvoie une valeur vraie seulement si tous les modules ont pu être chargés avec succès.
À vous !
Envoyez vos perles à perles@mongueurs.net. Elles seront peut-être publiées dans un prochain numéro de GNU/Linux Magazine.
Retrouvez cet article dans : Linux Magazine 98


