【问题标题】:Perl Moose TypeDecorator error. How do I debug?Perl Moose TypeDecorator 错误。我该如何调试?
【发布时间】:2011-01-07 00:59:50
【问题描述】:

我最近遇到了一个问题,如果有任何见解,我将不胜感激。我在圣诞节前在 PerlMonks 上发布了一个类似的问题,并提供了一些关于从 MooseX::Declare 切换的反馈([http://www.perlmonks.org/?node_id=877703][1])。我现在已经使用 MooseX::Types 和 MooseX::Params::Validate 将代码切换到 vanilla Moose。然而,同样的错误发生在同一个地方。这并不奇怪,因为它似乎与 MooseX::Types 相关。

我收到以下错误(为了便于阅读,尝试将其隔开 并且堆栈底部被截断):


plxc16479> tmp10.pl

Argument cannot be 'name' at /nfs/pdx/disks/nehalem.pde.077/perl/lib64/site_perl/MooseX/Types/TypeDecorator.pm line 88

MooseX::Types::TypeDecorator::new('MooseX::Types::TypeDecorator=HASH(0x1620c58)', 'name', 'g1145114N5582201_16161616a2x_FU02xxT_2bxc2e3_6x0xxxp0fx0xxx0x...', 'mask_data', '', 'tags', 0) called at /nfs/pdx/disks/nehalem.pde.077/projects/lib/Program-Plist-Pl/lib/Program/Plist/Pl.pm line 61

Program::Plist::Pl::_create_pattern_obj(undef, 'name', 'g1145114N5582201_16161616a2x_FU02xxT_2bxc2e3_6x0xxxp0fx0xxx0x...', 'mask_data', '', 'tag_data', '') called at /nfs/pdx/disks/nehalem.pde.077/projects/lib/Program-Plist-Pl/lib/Program/Plist/Pl.pm line 77

Program::Plist::Pl::BUILD('Program::Plist::Pl=HASH(0x162d6c0)', 'HASH(0x162d648)') called at generated method (unknown origin) line 101

Program::Plist::Pl::new('Program::Plist::Pl', 'name', 'bist_hfmmin_16161616_list', 'parents', 'HASH(0xccf040)', 'fh', 'GLOB(0xccc928)', 'external_pl_code', 'CODE(0x14910b0)', ...) called at /nfs/pdx/disks/nehalem.pde.077/projects/lib/Program-Roles-PlHandler/lib/Program/Roles/PlHandler.pm line 52

Program::Roles::PlHandler::_create_global_pl_obj(undef, 'name', 'bist_hfmmin_16161616_list', 'parents', 'HASH(0xccf040)', 'fh', 'GLOB(0xccc928)') called at /nfs/pdx/disks/nehalem.pde.077/projects/lib/Program-Plist-Pl/lib/Program/Plist/Pl.pm line 77

Program::Plist::Pl::BUILD('Program::Plist::Pl=HASH(0xccd300)', 'HASH(0xccc628)') called at generated method (unknown origin) line 101

Program::Plist::Pl::new('Program::Plist::Pl', 'name', 'bist_list', 'parents', 'HASH(0xccce80)', 'fh', 'GLOB(0xccc928)', 'external_pl_code', 'CODE(0x14910b0)', ...) called at /nfs/pdx/disks/nehalem.pde.077/projects/lib/Program-Roles-PlHandler/lib/Program/Roles/PlHandler.pm line 52

我的问题似乎是对 TypeDecorator::new 的最高调用。 TypeDecorator 构造函数似乎需要两个参数,class/self 参数和对 TypeDecorator 或 TypeConstraint 对象的引用。相反,它以某种方式从我的创建模式对象调用中接收参数。我已经验证了进入 _create_pattern_obj 函数的参数是正确的,并且进入 Pattern->new 调用的参数也是正确的(由堆栈跟踪参数证实)。 _create_pattern_obj 函数如下所示:

sub _create_pattern_obj {
    my ($self, $name, $mask_data, $tag_data) = validated_list(\@_,
                                                              name => {isa => Str},
                                                              mask_data => {isa => Str, optional => 1},
                                                              tag_data => {isa => Str, optional => 1});

    $mask_data = '' if !defined $mask_data;

    my $tags = defined $tag_data ? map {$_ => 1} split(',', $tag_data) : {};

    my $pattern_obj = Program::Plist::Pl::Pattern->new(name => $name,
                                                       mask_data => $mask_data,
                                                       tags => $tags);
    $self->_add_pattern($pattern_obj);
}

函数在 Program::Plist::Pl::Pattern->new 调用上死掉了,也就是第 61 行 在上述调用堆栈中引用的文件 Pl.pm 文件中,TypeDecorator::new 调用声称来自该文件。

Pattern 类是:

package Program::Plist::Pl::Pattern;

use 5.012002;
our $VERSION = sprintf "2.%03d", q($Revision: 473 $) =~ /: (\d+)/;

use Moose;
use namespace::autoclean;

use MooseX::Types::Moose qw(Str Num Int HashRef);
use MooseX::Params::Validate;

has 'name' => (isa => Str,
               is => 'ro',
               required => 1);

has 'tuple' => (isa => Int,
                is => 'ro');

has 'tid' => (isa => Int,
              is => 'ro');

has 'weight' => (isa => Num,
                 is => 'ro');

has 'tags' => (isa => HashRef[Str],
               is => 'ro',
               default => sub {{}});

has 'mask_data' => (isa => Str,
                    is => 'rw',
                    default => '',
                    writer => '_set_mask_data');

sub has_tag {
    my ($self, $tag) = (shift,
                        pos_validated_list(\@_, {isa => Str}));

    exists $self->{tags}->{$tag} ? return 1 : return 0;
}

sub _add_tag {
    my ($self, $tag) = (shift,
                        pos_validated_list(\@_, {isa => Str}));
    $self->{tags}->{$tag} = 1;
}

sub BUILDARGS {
    print STDERR 'CALLED '.__PACKAGE__."BUILDARGS\n";
    print STDERR 'ARGUMENTS:'.join(',', @_)."\n";
}

sub BUILD {
    my ($self) = @_;
    print STDERR 'CALLED '.__PACKAGE__."::BUILD\n";
}

__PACKAGE__->meta->make_immutable;

1;

不知何故,从调用堆栈中的参数来看,我对 Pattern->new 调用的参数最终被传递给了 TypeDecorator::new 调用,并且让它们窒息。我已经验证了对该子例程的良好调用(来自和更早的堆栈跟踪)看起来像这样(注意两个参数):


数据库 T
$ = MooseX::Types::TypeDecorator::new('MooseX::Types::TypeDecorator', ref(Moose::Meta::TypeConstraint)) 从文件 `/nfs/pdx/disks/nehalem.pde.077 调用/perl/lib64/site_perl/MooseX/Types.pm' 第 464 行


问题是我不知道如何调试正在发生的事情。单步执行代码时,执行直接从 Pattern->new 调用传递到 TypeDecorator 代码。这是在我的任何类代码执行之前发生的。我知道 Moose 正在为我创建新方法,但我不知道如何调试我看不到的代码。

我查看了有关 Moose 的文档,但这些都是关于如何使用它的,而不是在幕后发生的事情。我确实阅读了 Class::MOP 文档,但我不清楚这段代码的确切创建位置和时间。虽然我从所有研究中学到了很多东西,但没有一个能直接帮助我解决我的问题:)

首先,对于正在发生的事情的任何想法将不胜感激。其次,我该如何调试这个问题?我所有常用的调试工具都失败了!执行直接从我的新调用跳转到问题代码,我似乎无法追踪 TypeDecorator::new 参数实际上是从哪里传递的。最后,关于 Moose 究竟是如何做到的,有没有什么好的文章?还是 Class::MOP?

编辑 - 这是我的类型定义。我可能会补充说,这是我第一次涉足 Moose,所以如果你看到我在做的任何事情很奇怪,请随时指出。

package Program::Types;

use 5.012002;
use strict;
use warnings;

our $VERSION = sprintf "2.%03d", q($Revision: 473 $) =~ /: (\d+)/;

# predeclare types
use MooseX::Types
-declare => [qw(NonemptyStr FilePath DirectoryPath FilePathThatExists DirectoryPathThatExists
                TwoDigNum Pl LocalPl Pattern Program_Env Program_Whichload Program_Tpl
                Program_Plist Program_Bmfc Program_Tpl_Test Program_Tpl_Flow
                Program_Tpl_Flow_Item Program_Tpl_Flow_Item_Result Word)];

# import some MooseX builtin types that will be built on
use MooseX::Types::Moose qw(Str Int Object);

# types base on some objects that I use
class_type Pl, {class => 'Program::Plist::Pl'};

class_type LocalPl, {class => 'Program::Plist::LocalPl'};

class_type Pattern, {class => 'Program::Plist::Pl::Pattern'};

class_type Program_Env, {class => 'Program::Env'};

class_type Program_Whichload, {class => 'Program::Whichload'};

class_type Program_Tpl, {class => 'Program::Tpl'};

class_type Program_Tpl_Test, {class => 'Program::Tpl::Test'};

class_type Program_Tpl_Flow, {class => 'Program::Tpl::Flow'};

class_type Program_Tpl_Flow_Item, {class => 'Program::Tpl::Flow::Item'};

class_type Program_Tpl_Flow_Item_Result, {class => 'Program::Tpl::Flow::Item::Result'};

class_type Program_Plist, {class => 'Program::Plist'};

class_type Program_Bmfc, {class => 'Program::Bmfc'};

subtype Word,
    as Str,
    where {$_ =~ /^\w*$/};

coerce Word,
    from Str,
    via {$_};

subtype NonemptyStr,
  as Str,
  where {$_ ne ''};

coerce NonemptyStr,
  from Str,
  via {$_};

subtype TwoDigNum,
  as Int,
  where {$_ =~ /^\d\d\z/},
  message {'TwoDigNum must be made of two digits.'};

coerce TwoDigNum,
  from Int,
  via {$_};

subtype FilePath,
  as Str,
  where {!($_ =~ /\0/)},
  message {'FilePath cannot contain a null character'};

coerce FilePath,
  from Str,
  via {$_};

subtype DirectoryPath,
  as Str,
  where {!($_ =~ /\0/)},
  message {'DirectoryPath cannot contain a null character'};

coerce DirectoryPath,
  from Str,
  via {$_};

subtype FilePathThatExists,
  as Str,
  where {(!($_ =~ /\0/) and -e $_)},
  message {'FilePathThatExists must reference a path to a valid existing file.'.
           "Path ($_)"};

coerce FilePathThatExists,
  from Str,
  via {$_};

coerce FilePathThatExists,
  from FilePath,
  via {$_};

subtype DirectoryPathThatExists,
  as FilePath,
  where {(!($_ =~ /\0/) and -d $_)},
  message {'DirectoryPathThatExists must reference a path to a valid existing '.
           "directory.  Path ($_)"};

coerce DirectoryPathThatExists,
  from Str,
  via {$_};

coerce DirectoryPathThatExists,
  from DirectoryPath,
  via {$_};

1;

Edit2 -- 由于明显的运算符错误而被删除 :) 请注意,我在 Pattern 类中使用 BUILDARGS 而不返回参数列表。我已经在当前代码中删除了这个,没有改变错误。

Phaylon,这是 Program::Plist::Pl 类。

package Program::Plist::Pl;

use 5.012002;
our $VERSION = sprintf "2.%03d", q($Revision: 473 $) =~ /: (\d+)/;

use Moose;
use namespace::autoclean;

use Program::Plist::Pl::Pattern;
use Program::Types qw(Pl LocalPl TwoDigNum Pattern);
use Program::Utils qw(rchomp);

use MooseX::Types::Moose qw(HashRef GlobRef Str);
use MooseX::Params::Validate;

with 'Program::Roles::PlHandler';

has 'name' => (isa => Str,
               is => 'ro',
               required => 1);

has 'parents' => (isa => HashRef[Pl|LocalPl],
                  is => 'ro',
                  required => 1);

has 'children' => (isa => HashRef[Pl|LocalPl],
                   is => 'ro');

has 'prefixes' => (isa => HashRef[TwoDigNum],
                   is => 'ro',
                   default => sub{{}});

has 'patterns' => (isa => HashRef[Pattern],
                   is => 'ro',
                   default => sub{{}});

sub _add_child {
    my ($self, $obj) = (shift,
                        pos_validated_list(\@_, {isa => Pl|LocalPl}));
    $self->{children}->{$obj->name} = $obj;
}

sub _add_pattern {
    my ($self, $obj) = (shift,
                        pos_validated_list(\@_, {isa => Pattern}));
    $self->{patterns}->{$obj->name} = $obj;
}

sub _create_pattern_obj {
    $DB::single = 1;
    my ($self, $name, $mask_data, $tag_data) = validated_list(\@_,
                                                              name => {isa => Str},
                                                              mask_data => {isa => Str, optional => 1},
                                                              tag_data => {isa => Str, optional => 1});

    $mask_data = '' if !defined $mask_data;

    my $tags = defined $tag_data ? map {$_ => 1} split(',', $tag_data) : {};

    $DB::single = 1;
    my $pattern_obj = Program::Plist::Pl::Pattern->new(name => $name,
                                                       mask_data => $mask_data,
                                                       tags => $tags);
    $self->_add_pattern($pattern_obj);
}

sub BUILD {
    my ($self, $fh) = (shift,
                       pos_validated_list([$_[0]->{fh}], {isa => GlobRef}));

    while (<$fh>) {
        # skip empty or commented lines
        rchomp;
        next if ((/^\s*#/) or (/^\s*$/));

        # handle global plist declarations
        if (my @m = /^\s*GlobalPList\s+(\w+)/) {
            # creating new object and adding it to our data print STDERR
            #                    "SELF($self)\n".join("\n",sort keys
            #                    %Program::Plist::Pl::)."\n"; 
            $self->_create_global_pl_obj(name => $m[0],
                                         parents => {%{$self->parents},
                                                     $self->name => $self},
                                         fh => $fh);
        }

        # handle local referenced plist declarations
        elsif (@m = /^\s*PList\s+(\w+):(\w+)/) {
            $self->_create_local_pl_obj(file => $m[0],
                                        name => $m[1]);
        }
        # handling pattern lines
        elsif (@m = /^\s*Pat\s+(\w+)\s*(\[.*\])?\s*;\s*(#([\w,])#)?\s*$/) {
            $self->_create_pattern_obj(name => $m[0],
                                       mask_data => do {defined $m[1] ? $m[1] : ''},
                                       tag_data => do {defined $m[2] ? $m[2] : ''});
        }
        # handling our patlist closure
        elsif (/^\s*\}/) {
            last;
        }
    }

    # need to populate our hash of child plists
    for (@{$self->data}) {
        if (($_->isa('Pl')) or ($_->isa('LocalPl'))) {
            $self->_add_child($_);
        }
    }
}

__PACKAGE__->meta->make_immutable;

1;

【问题讨论】:

  • 您是否尝试过删除 MooseX::Types 用法?这里没有什么是普通 Moose 类型无法完成的。它可能解决不了任何问题,但至少值得一试。
  • 您的“Program::Plist::Pl”中有导出的“模式”吗?你能出示那个文件吗?
  • 我可以删除 MooseX::Types,但我还必须检查和修改大量其他文件。我也讨厌尝试解决我不理解的问题。我发现这通常会导致以后重新创建类似或新的问题。我宁愿现在花额外的时间调试并查明问题。 Phaylon -- 我已在原始帖子的末尾添加了文件的内容。
  • 跳过 make_immutable 可能有助于使围绕构造函数的控制流更易于理解。
  • 好主意。我看到了更多,我们会看看它是否有帮助:)

标签: perl moose moosex-types


【解决方案1】:

问题出在我相信这里。

use Program::Types qw(Pl LocalPl TwoDigNum Pattern);

您正在将一个名为 Pattern 的函数导入到您的 Program::Plist::Pl 类中。然后,您在此处(无意地)调用此函数:

    my $pattern_obj = Program::Plist::Pl::Pattern->new(name => $name,
                                                   mask_data => $mask_data,
                                                   tags => $tags);

特别是Program::Plist::Pl::Pattern 解析为您的完全限定的函数 名称,而不是您期望的类(技术上的包名称)。此函数返回一个 TypeObject,然后您将其调用 new()

注意:这正是上面 cmets 中 phaylon 所建议的内容。

除了知道你总是可以用它的完全限定名称调用一个函数,因此真的没有办法调试它,因此永远不应该有一个 MooseX::Type 和一个有效的类名冲突。

如果是我,我会开始编写一个非常简单的测试用例并添加代码来复制原始文件,直到它中断。我可能会从调用 new 开始。然后慢慢地添加假设,直到我找到一个打破的假设。希望您在该过程中尽早添加 MooseX::Types 调用,以触发“哦,显然就是这样”的时刻。

【讨论】:

  • 你(和 Phaylon)是绝对正确的。我确实在另一个问题线程中解决了这个问题,但忘了把它放在这里!出于某种原因,我没有想到我使用 Program::Types 库导入的类型定义实际上是子例程。不好的部分是类型定义常用的命名约定(FirstLetterCapitalNoUnderscores)与包的命名约定相同。现在我已经弄清楚了,我有一个新的类型定义命名约定,以确保它们不会意外地与包名称发生冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-20
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-06
  • 2010-11-02
相关资源
最近更新 更多