(How)我可以在调用Symbol :: delete_package之后重新创建一个包吗?

问题描述:

给定一个小Perl库:(How)我可以在调用Symbol :: delete_package之后重新创建一个包吗?

package P; 

use strict; 
use warnings; 

print("Loading P\n"); 

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

我试着写一个程序加载,卸载和重装包装,以获得更好的理解包在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"); 

运行此程序打印沿(其中该模块通过keys %INC列出可能不同的顺序)的路线的东西:

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 

即似乎重新加载库的方式按预期工作,但符号表%P::仍为空。为什么图书馆第二次加载时不会重新填充?我试图找到一种方法来重新加载模块,而不使用任何CPAN软件包。

问题是%P::在编译时被解析,所以它指的是glob delete_package被清除并导致符号表不符合。

因此当你用

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

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

这意味着在更换

keys %P::; 

强制查找在运行时发生得到预期的输出仅卸载一个包是不够的;您需要卸载硬编码引用的代码以及从包中导入的代码!

程序,删除包(例如在快速CGI守护进程的脚本加载器)通常不硬编码到他们删除软件包引用,所以他们通常不会遇到这个问题。以下是一个示例:

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

啊,真有意思!如果我有一个子程序'p'这是目前给予裁判的哈希(即它的调用像'P(\%P::);'),并在内部枚举的符号,是有办法内平移裁判'p'到一个字符串,这样你的任何解决方案都可以使用?或者,我是否需要调整呼叫者,让他们传递一个字符串? –

+0

调整调用者以将引用传递给正确的glob。 – ikegami

+0

谢谢;我想我必须阅读在这种情况下'glob'的含义(我总是只知道在文件名匹配的情况下)。 –

我收到ikegami代码的内存泄漏。我使用了Test :: LeakTrace,它报告了一些问题。这里是指找到内存泄漏稍微编码:

#!/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); 
    } 
} 

下面是输出:

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. 

这 七岁表明,在Perl本身错误的问题。

这是否似乎仍然是一个错误?