2017-10-20 36 views
2

Étant donné une petite bibliothèque Perl:(Comment) puis-je recréer un paquet après avoir appelé Symbol :: delete_package dessus?

package P; 

use strict; 
use warnings; 

print("Loading P\n"); 

our $k1 = 'v1'; 
our $k2 = 'v2'; 
our $k3 = 'v2'; 

J'ai essayé d'écrire un programme qui charge, décharge et recharge le paquet pour obtenir une meilleure compréhension de la façon dont les paquetages fonctionnent en Perl:

# main.pl 
use strict; 
use warnings; 
use Symbol qw(delete_package); 

# Load module 
require "./P.pm"; 
my @incs = sort keys %INC; 
my $numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

# Unload module & delete package 
delete_package('P'); 
delete $INC{'./P.pm'}; 
@incs = sort keys %INC; 
$numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

# Load module again 
require "./P.pm"; 
@incs = sort keys %INC; 
$numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

L'exécution de cette programme imprime quelque chose le long des lignes de (l'ordre dans lequel les modules sont répertoriés par keys %INC peuvent varier):

Loading P 
Includes: ./P.pm Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 4 
Includes: Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 0 
Loading P 
Includes: ./P.pm Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 0 

C'est à dire. il semble que le rechargement de la bibliothèque a fonctionné comme prévu, mais la table de symboles %P:: est toujours vide. Pourquoi n'a-t-il pas été réapprovisionné lorsque la bibliothèque a été chargée une deuxième fois? J'essaie de trouver un moyen de recharger un module sans utiliser de paquets CPAN.

Répondre

6

Le problème est que %P:: est résolu au moment de la compilation, de sorte qu'il fait référence à la glob delete_package effacée et a provoqué la désynchronisation de la table de symboles.

Vous obtiendrez le résultat attendu si vous forcez la recherche de se produire lors de l'exécution en remplaçant

keys %P::; 

avec

keys %{ no strict qw(refs); \%{"P::"} }; 

ou

keys %{ $::{"P::"} }; 

Ce que cela signifie est que ce n'est pas suffisant pour décharger un paquet; vous devez décharger le code qui a une référence codée en dur sur le paquet et le code importé du paquet!

Les programmes qui suppriment des packages (par exemple, un chargeur de script dans un démon Fast CGI) ne codent généralement pas les références aux packages qu'ils suppriment, de sorte qu'ils ne rencontrent normalement pas ce problème. Voici un exemple:

use strict; 
use warnings; 
use Symbol qw(delete_package); 

use FindBin qw($RealBin); 
use lib $RealBin; 

sub mod_path { 
    my ($mod_name) = @_; 
    return ($mod_name =~ s{::}{/}gr) . ".pm"; 
} 

sub load_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    require $mod_path; 
} 

sub unload_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    delete_package($mod_name); 
    delete($INC{$mod_path}); 
} 

sub get_package { 
    my ($pkg_name) = @_; 
    $pkg_name .= '::' if $pkg_name !~ /::\z/; 
    my $pkg = \%::; 
    $pkg = $pkg->{$_} for split /(?<=::)/, $pkg_name; 
    return $pkg; 
} 

sub dump_info { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    my $pkg = get_package($mod_name); 

    my $is_in_inc = grep { $_ eq $mod_path } keys %INC; 
    printf("Included: %s\n", $is_in_inc ? "yes" : "no"); 

    my $num_syms = keys(%$pkg); 
    print("Number of symbols: $num_syms\n"); 

    print("\n"); 
} 

for $mod_name ('P', 'P') { 
    load_module($mod_name); dump_info($mod_name); 
    # $mod_name->run(); 
    unload_module($mod_name); dump_info($mod_name); 
} 
+0

Ah, c'est intéressant! Si j'ai un sous-programme 'p' auquel on donne actuellement une référence à un hachage (c'est-à-dire comme' p (\% P: :); ') et qui énumère les symboles en interne, est-il possible de traduire 'p' à une chaîne de caractères telle que l'une de vos solutions pourrait être utilisée? Ou aurais-je besoin d'ajuster les appelants de telle sorte qu'ils passent une chaîne à la place? –

+0

Ajuste l'appelant pour transmettre une référence au bon glob. – ikegami

+0

Merci; Je suppose que je devrais lire sur ce que glob veut dire dans ce contexte (je l'ai toujours seulement connu dans le contexte de l'appariement des noms de fichiers). –

0

Je reçois une fuite de mémoire avec le code du Ikegami. J'ai utilisé Test :: LeakTrace et il signale quelques problèmes. Voici le code qui trouve légèrement les fuites de mémoire:

#!/usr/bin/perl -w 
use strict; 
use lib './'; 
use Test::LeakTrace; 
use Symbol 'delete_package'; 

use FindBin qw($RealBin); 
use lib $RealBin; 

sub mod_path { 
    my ($mod_name) = @_; 
    return ($mod_name =~ s{::}{/}gr) . ".pm"; 
} 

sub load_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    require $mod_path; 
} 

sub unload_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    delete_package($mod_name); 
    delete($INC{$mod_path}); 
} 

sub get_package { 
    my ($pkg_name) = @_; 
    $pkg_name .= '::' if $pkg_name !~ /::\z/; 
    my $pkg = \%::; 
    $pkg = $pkg->{$_} for split /(?<=::)/, $pkg_name; 
    return $pkg; 
} 

sub dump_info { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    my $pkg = get_package($mod_name); 

    my $is_in_inc = grep { $_ eq $mod_path } keys %INC; 
    printf("Included: %s\n", $is_in_inc ? "yes" : "no"); 

    my $num_syms = keys(%$pkg); 
    print("Number of symbols: $num_syms\n"); 

    print("\n"); 
} 

leaktrace { 
    foreach my $mod_name ('P', 'P') { 
     load_module($mod_name); dump_info($mod_name); 
     unload_module($mod_name); dump_info($mod_name); 
    } 
} 

est ici la sortie:

Included: yes 
Number of symbols: 1 

Included: no 
Number of symbols: 0 

Included: yes 
Number of symbols: 1 

Included: no 
Number of symbols: 0 

leaked SCALAR(0x556efcaf9a90) from /home/terry/projects/robinson/dev/trunk/command-line/experiments/perl-hacks/P.pm line 2. 
leaked SCALAR(0x556efcbb5628) from /usr/share/perl/5.26/Symbol.pm line 74. 

Ce âgé de sept ans suggère que le problème dans un bogue dans Perl lui-même.

Est-ce que cela semble toujours être un bug?