Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Prevent Strings From Being Interpreted As A File Handle

by amon (Scribe)
on Apr 27, 2014 at 09:36 UTC ( #1083985=perlquestion: print w/replies, xml ) Need Help??

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

Perl has the feature that strings named like a file handle are taken to be a file handle, which for example means that “open DUP, '>&STDOUT'”, but also “'STDOUT'->say("hello")” works.

This leads to conflicts if we have a class and a bareword file handle of the same name:

use strict; use warnings; use feature 'say'; say "This is perl $^V"; package Input { sub awesome { say "yay, this works"; } } # this works 'Input'->awesome; # the "open" is parsed, but not actually executed eval <<'END'; sub red_herring { open Input, "<&", \*STDIN or die $!; } END say "eval failed: $@" if $@; # this will die eval { 'Input'->awesome; }; say "Caught: $@" if $@;

Example Output:

This is perl v5.16.2 yay, this works Caught: Can't locate object method "awesome" via package "IO::File" at + prog.pl line 27.

I am aware that this can be avoided with lexical filehandles, and that adhering to proper naming conventions makes this collision less likely. For example, the above code does not fail if the script is in “package Foo” and the eval'd code in “package main”, but the problem occurs again when we do “'main::Input'->awesome” which should in theory be absolutely equivalent to “'Input'->awesome”.

However, my question is: How can I disambiguate a method call on a string so that it is always resolved as a class method call, and not as a call on an IO instance?

I assume one would have to write a pragma to hook into method resolution, e.g. by replacing the pp_method_named opcode, but I'd much rather hear the wisdom of more experienced monks on this.

Context: I'm hacking on the utility module Util::Underscore that misguidedly stuffs various utils into the “_” namespace so that they can be used comfortably without having to import them. As I want to prevent the “_” package from being used directly, I boobie-trapped the “_::import” method to blow with a helpful error message whenever touched. My tests started failing when another module contained file tests on the magic “_” file handle, like “-d $file && -w _”.

This question has previously been asked on Stack Overflow, but so far without the answer I'm seeking.

Replies are listed 'Best First'.
Re: Prevent Strings From Being Interpreted As A File Handle
by BrowserUk (Pope) on Apr 27, 2014 at 10:49 UTC

    The simple solution is to stop calling a subroutine as a method and just use: Input::awesome;.

    Or, if awesome really is a method, then invoke it via a reference blessed into the class 'Input':

    $o = bless [], 'Input';; ... $o->awesome;; Yay!

    Or, as you've pointed out, use lexicals.

    Bottom line: stop going out of your way to artificially create problems.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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.

      Thank you for coming up with the instance workaround.

      However, I am not trying to prevent this ambiguity, but to resolve it in a specific way (i.e. I'm seeking Perl wisdom, not trying to solve the immediate problem which I'm already mitigating using other means). In general, Perl is flexible enough to change such core behavior, and I would like to find out how this can be done. I was very careful to word my question as “How can I disambiguate a method call on a string so that it is always resolved as a class method call, and not as a call on an IO instance?” – both “method call” and “string” are central to my question.

        both “method call” and “string” are central to my question.

        I covered your objections with my last sentence: Bottom line: stop going out of your way to artificially create problems.

        Even if you succeed in finding a way to subvert Perl into doing this stupid thing; it won't stop it being stupid.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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: Prevent Strings From Being Interpreted As A File Handle
by LanX (Cardinal) on Apr 27, 2014 at 13:58 UTC

    = Avoiding use Module

    > As I want to prevent the “_” package from being used directly, I boobie-trapped the “_::import” method to blow with a helpful error message whenever touched.

    this seems to be the real question and the answer was already given at stack overflow, don't boobie-trap the import but the module. Any required module is evaled via do FILE.

    Just put your code before the package declaration and it's executed in the context of the caller.

    > cat Module.pm script.pl; perl script.pl #---- Module.pm warn "NO NO NO"; package Module; sub whatever {} 1; #---- script.pl use Module; #---- output NO NO NO at Module.pm line 1.

    = Regarding the magic filehandle _

    I didn't know (anymore?) about This, and it scares me a little bit, that it has such global effects.

    - The main namespace

    Please note, it always belongs to the '%main::' stash:

    $\="\n"; package TST; print "exists main::_ ", exists $main::{_}; print -f 'Module.pm' && -w _; print "exists TST::_ ", exists $TST::{_}; print -w main::_;

    # OUTPUT exists main::_ 1 1 exists TST::_ 1

    - Workaround

    As a practical solution, I'd say avoid _ and take double __ when operating in main.

    - Documentation

    Finding documentation in perldoc wasn't easy, thats what I detected in perlfunc

    #5.10 If any of the file tests (or either the "stat" or "lsta +t" operators) are given the special filehandle consisting +of a solitary underline, then the stat structure of the prev +ious file test (or stat operator) is used, saving a system c +all. # ... special cases + example code ... # BUT ALSO As of Perl 5.9.1, as a form of purely syntactic sugar, +you can stack file test operators, in a way that "-f -w -x $fil +e" is equivalent to "-x $file && -w _ && -f _

    - Deprecation?

    the last paragraph makes we wondering if we can't slowly deprecate such global side effects.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Prevent Strings From Being Interpreted As A File Handle
by Anonymous Monk on Apr 27, 2014 at 11:10 UTC

    However, my question is: How can I disambiguate a method call on a string so that it is always resolved as a class method call, and not as a call on an IO instance?

    You can't :) apparently filehandles rule the typeglob, thats why it hasn't been recommended for a long time

    $ perl -fle " sub Snacks::Ahoy { q{Nom} } open Snacks, q{>}, \q{}; pri +nt $Snacks::{Ahoy}->() " Nom $ perl -fle " sub Snacks::Ahoy { q{Nom} } open Snacks, q{>}, \q{}; pri +nt bless(\(my$q=\{Snacks}),q{Snacks})->Ahoy " Nom $ perl -fle " sub Snacks::Ahoy { q{Nom} } sub Snacks { q{Rice} } print + Snacks::->Ahoy " Nom $ perl -fle " sub Snacks::Ahoy { q{Nom} } open Snacks , q{<}, \q{Rice} +; print Snacks::->Ahoy " Can't locate object method "Ahoy" via package "IO::File" at -e line 1.
      disambiguation with local
      $ perl -fle " sub Snacks::Ahoy { q{Nom} } open Snacks , q{<}, \q{Rice} +; local *Snacks; print Snacks::->Ahoy " Nom

        This is clever. I like it.

        So we'd end up with something inconvenient like

        sub invoke_static { my ($class, $method, @args) = @_; no strict 'refs'; # I'd like to only localize *{$class}{IO}, # but we can't have everything, right? local *{$class}; $class->$method(@args); }

        or something insane like

        sub safe_from_evil_filehandles(&) { my ($cb) = @_; my @evil_globs; ... # walk through %:: and list all globs # where *{$entry}{IO} is defined # unless they're OK like "STDOUT" my $localizer; $localizer = sub { return $cb->() unless @evil_globs; local *{shift @evil_globs}; return $localizer->(); }; return $localizer->(); }

        This works. Good job.

        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: Prevent Strings From Being Interpreted As A File Handle
by Anonymous Monk on Apr 27, 2014 at 13:56 UTC

    If I understand correctly, you're looking for all uses of _ as a name space. You are trying to do this by changing the way Perl parses the module you are working on. Of course, you could do this (at least in theory), but I am in no position to advise you on how, and you might want to contemplate the module quote at the beginning of toke.c before you go too far down this road.

    Have you considered other routes to your goal? Maybe a source analysis using PPI and/or App-Ack would serve you better. Be warned that the App-Ack install may fail the first time you try it if you don't already have all the prerequisites installed. The solution, of course, is just to rerun the install.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (5)
As of 2020-12-01 12:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you use taint mode?





    Results (6 votes). Check out past polls.

    Notices?