如何检查是否所有Perl模块都已导入?

时间:2014-07-11 09:01:06

标签: perl perl-module

说我有一个Perl模块:

use Foo::Bar;

sub add {
    return Foo::Bar::func() + Foo::Buzz::func();
}

此模块中存在错误,因为它会忘记use Foo::Buzz。我的单元测试并不总能捕获此错误,例如,如果Foo::Buzz的测试运行较早且导入Foo::Buzz之前导入add()。如果我在生产代码中使用此模块,它将失败并显示错误Foo::Buzz未导入。

如何检查代码中使用的所有模块是否也已导入?

编辑:我想在将代码部署到生产环境之前检查代码,以避免发生任何错误。该示例将在生产中失败,我想在此之前捕获错误,例如,当我运行单元测试时。我想要一个工具或一些代码,我可以在部署之前运行它来捕获这个错误,比如flake8 for python。

2 个答案:

答案 0 :(得分:2)

简短的回答是你无法。由于Perl是一种动态语言,因此您无法检查是否在运行时加载所有模块,因为您无法检查代码中是否存在其他错误。

您仍然可以使用某些静态代码分析,尝试在未显示This::Pattern的文件中查找use This::Pattern;,但它并不保证任何内容。

答案 1 :(得分:0)

如果Perl严格来说是动态语言,您可以轻松检查程序中是否安装了模块。问题是Perl不是100%动态的。它进行了一些编译,部分编译工作在检查模块后完成。

Bulrush走在正确的轨道上。不幸的是,您可以使用use子句来执行此操作。预编译会检查use,因此您在执行eval之前会收到错误。

然而,use perldoc页面中有一条线索:

  
      
  • 使用模块列表
  •   
  • 使用模块
  •   
  • 使用版本
      
      从命名模块将一些语义导入当前包,通常通过将某些子例程或变量名称别名到包中。 完全等同于
      
      的 BEGIN { require Module; Module->import( LIST ); }
  •   
你去吧!您可以在BEGIN子句中使用require,该子句甚至在解析文件的其余部分之前执行。您可以在那里使用eval来查看这是否有效。您需要使用变量作为标志,以查看是否因为范围问题而起作用。离开BEGIN子句时,常规的词法范围变量将消失。

BEGIN {
    our $FOO_BAR_available = 0;        # Must be a package variable
    eval {
        require Foo::Bar;
        Module->import( qw(...) );     # No need if you don't import any subroutines
    };
    if (not $@ ) {
        $FOO_BAR_AVAILABLE = 0;
    }
}

然后在你的计划中,你有:

our $FOO_BAR_available;
if ( not $FOO_BAR_available ) {
    # Here be dragons...
}
else {
    # Back to your normal code...
}

our $FOO_BAR_available有点令人困惑。您没有再次声明此变量,您只是说明您要使用此变量而不使用完整的包名称作为前缀。该变量在BEGIN子句中设置,这不会影响该值。

如果此模块是正确编写,则可以完全跳过包变量的使用。模块假设设置一个名为$VERSION的包变量。您可以将此变量用作标志:

BEGIN {
    eval {
        require Foo::Bar;
        Module->import( qw(...) );   # No need if you don't import any subroutines
    };
}

注意我不仅不必声明包变量,我甚至不必验证eval语句是否有效。

然后在你的程序中......

if ( not $FOO::BAR::VERSION )  {
    # Here be dragons...
}
else {
    # Back to your normal code...
}

如果模块设置了$VERSION变量,则表示已加载。否则,您知道模块未加载。


附录

  

我希望在将代码部署到生产环境之前检查代码,以避免发生任何错误。该示例将在生产中失败,我想在此之前捕获错误,例如,当我运行单元测试时。

这是我的建议。它不像运行脚本那么简单,但它要好得多:

  • 首先,定义您的生产环境:它有什么版本的Perl?使用什么模块?这将有助于开发人员了解预期结果。我知道Foo::Bar很好,但我不应该使用Far::Bu,因为生产没有这个。这是第一步。我对那些不知道生产环境是什么的地方感到惊讶。
  • 使用Vagrant。这定义了一个与您的生产环境匹配的虚拟机。开发人员可以将其下载到他们的系统,并在他们的桌面上提供生产环境的副本。
  • 使用Jenkins。 Jenkins是一个持续构建的引擎。是的,你不能编译Perl,但你仍然可以从Jenkins中受益:

    • Jenkins可以为您运行单元测试。自动测试代码中的每个更改。你很早就发现了错误。
    • 您的Jenkins系统可以匹配您的生产机器 - 相同的Perl版本,相同的Perl模块。如果它没有安装在你的Jenkins构建机器上运行,那么很有可能它不会在生产中运行。
    • 您通过Jenkins安装。 Jenkins可以打包您的版本,您可以使用它来安装已知版本。没有从开发人员的系统中提取代码,并发现系统中存在某些不在您的版本控制系统中的内容。我不知道有多少次我看到开发人员从他们的机器上生产了一些产品(经过全面测试!相信我!),然后我们发现我们没有那些代码我们的版本控制系统,因为开发人员忘记检查内容。

您不能在生产环境中正常运行flake8。到那时,它已经晚了一点。

Perl有很多很好的工具可以执行类似的功能:

  • Perlbrew:这允许您的开发人员将具有自己的CPAN模块库的单独Perl程序安装到他们的开发系统中。他们可以使用它来匹配Perl版本和生产环境所需的模块。通过这种方式,他们可以使用相同的规则进行游戏。
  • Perlcritic:这会检查您的模块是否符合Damian Conway在其Perl最佳实践中提出的编码标准。
  • B::Lint:这就像C中的旧lint程序一样,可以捕获编码问题。

但是,在您准备好在Production中运行之前,这是要做的事情。使用Vagrant帮助开发人员设置自己的私有生产环境进行测试。使用Jenkins确保您在类似生产的环境中进行测试,并在发生错误时立即捕获错误,而不是在UAT测试之后。