Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Was a module use'd or require'd?

by Ovid (Cardinal)
on Aug 13, 2007 at 08:33 UTC ( [id://632158]=perlquestion: print w/replies, xml ) Need Help??

Ovid has asked for the wisdom of the Perl Monks concerning the following question:

Last night, in working with Devel::Recreate (suggest a better module name, please!), I discovered that it's devilishly difficult to differentiate between use and require or capture import lists. After discussions with Andy Armstrong, the only thing I could think of is either coderefs in @INC or overriding *CORE::GLOBAL::require. I can't tell if either solution allows me to differentiate between the use and require. The only thing I could think of is that use is basically this:

BEGIN { require Module; import Module LIST; }

In other words, if require is triggered, I could use HookLexWrap to wrap the import statement (considering inheritance, of course) to fetch the import list. I'd also have to write some XS code to try and grab PL_beginav to find out if the require happened in the BEGIN phase. Thus, the following would be considered a use statement:

BEGIN { require Some::Module; Some::Module->import(@args); }

Naturally, this is all very rickety. Is there a better way to do this?

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Was a module use'd or require'd?
by Anno (Deacon) on Aug 13, 2007 at 12:09 UTC
    One idea would be to make a snapshot of %INC when compile time ends. If an entry is in the snapshot, the module was use()d, otherwise it was require()d.

    For instance:

    CHECK { my %snap = %INC; sub report { for ( keys %INC ) { my $ans = $snap{ $_} ? 'use()d' : 'require()d'; print "$_ was $ans\n"; } } }
    Here, report() simply prints a list with each module's status. A more elaborate version could return the status of a module that is passed in.

    Of course, %INC can be manipulated in every which way, so the method isn't fool-proof. It makes up for that with simplicity.

    Update:
    Another possible drawback of this approach is that the module that implements it must itself be use()d and can't be loaded at run time. (It would be too late to run the CHECK block.) That may be true for other solutions also.

    Anno

      I haven't tried to lay it out clearly, but it seems to me that this node works under the flawed but common (it seems) assumption that there is some one "compile time" followed by some one "run time". Each line of code has one "compile time" and zero or more "run times" and those intermix quite a bit in even simple cases. The CHECK block of one module seems unlikely to be reliably run between the "compile time" and "run time" of some other module.

      Without checking (since "perldoc -f CHECK" and other similar attemps to easily find the relevent documentation are likely all still useless), your use makes me guess that CHECK blocks are the ones that get run after the main script has finished compiling. So this would only be useful for distinguishing mundane use vs require from the main script.

      So I don't think that addresses the requested problem at all, which is trying to reconstruct whether Some::Module should be reconstructed as having used or as having required each of the modules it loaded.

      - tye        

        I think you are right. My method will tell whether a module was loaded before or after the main program was compiled, not whether it was a "use" or "require" statement that did the loading. It would be useless for the given purpose (about which I wans't quite clear when I wrote the reply).

        Putting a subref on @INC looks like the best bet. It should be possible to tell the cases from another by looking at some levels of caller(). One problem here is to make sure you are always called. If someone unshifts new components onto @INC in front of you, you may never be for some modules.

        Update: tie-ing @INC could help. It isn't magical, so tie should be no problem.

        Anno

Re: Was a module use'd or require'd?
by TheDamian (Vicar) on Aug 13, 2007 at 20:18 UTC
    Does this achieve what you were hoping for?
    package Whatever; use Filter::Simple; my $was_used; my @use_args; FILTER { $was_used = 1; @use_args = @_; }; sub report { warn $was_used ? "I feel used (with: @use_args)\n" : "I feel required\n" ; } 1;
    and then:
    use Whatever qw<this that tother>; Whatever::report(); # I feel used (with: this that tother)
    vs:
    require Whatever; Whatever::report(); # I feel required
Re: Was a module use'd or require'd? (do better)
by tye (Sage) on Aug 13, 2007 at 13:59 UTC

    I think it would be more useful as a debugging aid to not produce results in terms of use. One not-uncommon mistake is getting the order of execution wrong. So instead of trying to output what the module looks like on disk, output something more informative.

    Output the order in which each require actually happened and the order in which each import() happened (with what arguments). I think I'd actually hook this via the debugger trace mechanism so you can output the order in which each statement was executed followed by the decompiled subroutines that existed when the require finished and then when import() finished.

    - tye        

Re: Was a module use'd or require'd?
by shmem (Chancellor) on Aug 13, 2007 at 16:12 UTC
    What a misnomer. I've always thought of Devel::Recreate as working along the lines
    package Devel::Recreate; require Other::World; BEGIN { require Control; Control->stop ($_) for split /$interface/, $ENV{Computers}; UNIVERSAL::DESTROY(Control->get('worries')); unimport Telephone; Control->loose; } sub relax { my $self = shift; $self->forget('work'); $self->enjoy($self->peer->reflect); $self->inspect('hobbies'); Other::World->get_callback($self); goto $self->callback; # magic goto, doesn't return, at least not t +o here... } return relax(caller);

    I might come up with a better name for yours after testing this one ;-)

    --shmem (tired developing right now)

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Was a module use'd or require'd?
by BrowserUk (Patriarch) on Aug 13, 2007 at 12:58 UTC

    I keep re-reading this. and I cannot, for the life of me, see what value you can derive from knowing that a module was required rather than used?

    Despite your explanations elsewhere, I would suggest that is it dangerous, and even unethical(in the most minor, trivial sense of that term) for you to make that determination.

    What the f*** is he prattling on about?I hear the time-sync'd chorus emote.

    Well, think of it like the cold war. And the continuous escalation that was the primary result of it.

    If you can determine some difference between the overall effect of my module by determining whether it was required or used, then maybe there is some action I can take within my module to make that same determination and so alter that effect. But then, if you can detect it, and I can alter it, maybe there is some mechanism that will allow me to alter it but not allow you to detect it.

    Of course, it's inevitable, that if I start taking action dependant upon whether you can detect whether I was required or used, that you are going to look for a method of subverting my method of detection whether I was required or used.

    But if my module renames perl5x.dll and substitutes itself in that namespace, then I can intercept your calls to pp_require() and, falsely, report failure to require, but the go on to install myself anyway.

    Alternatively, I could override require so that it was always actioned at BEGIN time, and always called the module import routine with a default value of :all.

    That may not work identically to use in all cases, but it would stop Ovid from telling the difference between use and require in 87.243789100000000000001% of cases. And hey, that got to be worth the effort :)


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      This is merely for a debugging module to offer hints for what's going on under the hood. That's all. It's for developers only, not intended for code to rely on and it has heavy caveats about what the output is and why it is intended as a crutch, but should not be relied upon.

      Cheers,
      Ovid

      New address of my CGI Course.

        This is merely for a debugging module to offer hints for what's going on under the hood.

        I understand that, but I still can't see the purpose. That is, to derive any benefit from a hint that a module was required rather than used, when debugging or otherwise, there would have to be some practical, effective difference in the way the module performed in those two scenarios. If there is no difference, then there is no benefit to knowing.

        And if there is a difference, then eventually someone is going to find a way of either:

        • exploiting that difference to provide some additional functionality.
        • detecting that difference so as to counter the effects of it in the way their module performs.

        Since I couldn't (and still cannot) think of any difference in the way a module would perform dependant upon whether it was required or used, I couldn't see the benefit of jumping through hoops to make the determination.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Was a module use'd or require'd?
by sfink (Deacon) on Aug 14, 2007 at 04:41 UTC
    How about Devel::Snapshot?

    I read the linked blog, and I'm still not clear on why you need to distinguish the two. At least as a first cut, wouldn't it be adequate to just print out all of the effects of the use/require? As in, symbol table entries, variable assignments, etc.? You can figure out what package a particular subroutine was defined in via B::Deparse's coderef2text, so you can still group things by package and then output an alias statement (sub A::f {...}; *B::g = *A::f;) rather than a redefinition (sub A::F {...}; sub B::g { ... }) or a chained assignment: *A::f = *B::g = sub { ... }.

    I have to admit that I haven't thought very hard about it, though.

    I guess I can see the value in making the output code look as much as possible like the input code, although for things where this would be useful, I'm not sure if that's relevant. (If you're debugging something that autogenerates classes, then there's really no input that's useful to imitate.)

Re: Was a module use'd or require'd?
by bart (Canon) on Aug 14, 2007 at 10:40 UTC
    I had seen your post about Devel::Recreate on use.perl.org and I couldn't make heads nor tails of it, due to the bad (IMO) name, so I simply stopped reading. I thought you were just having fun with developing? Huh?

    Now I see I have to read it as "re-create". Ah. (Perhaps name it "Devel::ReCreate", then, that'll be a bit clearer.)

    Well, I'd explore two routes:

    1. Was the action (use or require) called using compile time or runtime? In the former, it must have been used, otherwise, it's likely to be required. Well, it's just a heuristic, good for 95% of all cases, because both use of BEGIN blocks and of eval can make this detection fail.
    2. Intercept the calls to import, which is just a plain (class) method, but called automatically. You can create one if none exists, or you could use Hook::LexWrap if it does.

    I'm not saying this will solve all of your problems, but I thinks they are good options to explore.

Re: Was a module use'd or require'd?
by shmem (Chancellor) on Aug 14, 2007 at 13:32 UTC
    IIUC the only distinction between use and require; import is the way they are triggered. Both perform OP_REQUIRE, but for use the parser calls Perl_utilize() in op.c, where a method call to import is faked, and the require and import calls are faked into a BEGIN block.

    Other than inserting some hook right into Perl_utilize() and compile a development binary, I can see no way to reliably tell whether use Foo qw(bar) was called or BEGIN { require Foo; Foo->import qw(bar)}.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://632158]
Approved by naikonta
Front-paged by almut
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (4)
As of 2024-04-19 22:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found