Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Introspecting the current Perl file via DATA, even w/o DATA?

by LanX (Sage)
on Feb 04, 2016 at 18:18 UTC ( #1154412=perlquestion: print w/replies, xml ) Need Help??

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

I'm using this code to read the current Perl file into @line to be able to enrich my error messages with the originating code line.
CHECK { my $datapos = tell main::DATA; seek main::DATA,0,0; @lines =<main::DATA>; seek main::DATA, $datapos,0; } __DATA__ whatever

This works only if there is a __DATA__ line, otherwise the filehandle DATA doesn't exist.

I'm pretty sure that DATA is only an alias of anther filehandle pointing to the current code...

Any idea how to get this filehandle?

(don't want to explicitly open $0 again for reasons of complexity and performance)

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

Replies are listed 'Best First'.
Re: Introsepcting the current Perl file via DATA, even w/o DATA?
by Anonymous Monk on Feb 04, 2016 at 21:59 UTC
    I'm pretty sure that DATA is only an alias of anther filehandle pointing to the current code...
    Why do you think so? It seems such a thing doesn't exist:
    for ( values %main:: ) { my $io = *{$_}{IO} or next; my $name = *{$_}{NAME}; if ( defined fileno $io ) { printf "fileno of %6s is %d\n", $name, fileno $io; } else { print "($name is a virtual handle)\n"; } }
    output:
    fileno of stdin is 0 fileno of stdout is 1 fileno of STDIN is 0 fileno of STDOUT is 1 fileno of stderr is 2 fileno of STDERR is 2 (ARGV is a virtual handle)
    and if we add __DATA__ to the file:
    fileno of STDERR is 2 fileno of STDIN is 0 fileno of STDOUT is 1 fileno of stdout is 1 fileno of stdin is 0 (ARGV is a virtual handle) fileno of DATA is 3 fileno of stderr is 2
    So only __DATA__ is opened on fd 3... maybe there is some secret pro tech, but I suspect there isn't and then your best option is probably to just open __FILE__
      > Why do you think so?

      Because the filepointer is in sync with the parsing phases of Perl. There is an explicit warning not to read DATA at BEGIN or use phases because the end of file isn't reached yet.

      And testing showed that in this cases you can readline the currently processed source line resp. the next one.

      Not finding the handle in the main stash doesn't mean it doesn't exist somewhere else (probably hidden somewhere in B )

      update
      • Using a neutral source filter might be another option to fill @lines ...
      • IIRC is the debugger keeping the content source files in similar arrays, maybe I should have again a look into perl5db.pl.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

        Because the filepointer is in sync with the parsing phases of Perl
        Not necessarily, no... Let's see
        BEGIN { print "BEGIN\n"; } UNITCHECK { print "UNITCHECK\n"; } CHECK { print "CHECK\n"; } INIT { print "INIT\n"; } print "MAIN\n";
        Let's strace it (Debian Jessie, Perl 5.22)
        open("t.pl", O_RDONLY) = 3 ... read(3, "BEGIN {\n print \"BEGIN\\n\";\n}\nU"..., 8192) = 146 write(1, "BEGIN\n", 6) = 6 read(3, "", 8192) = 0 close(3) = 0 write(1, "UNITCHECK\n", 10) = 10 write(1, "CHECK\n", 6) = 6 write(1, "INIT\n", 5) = 5 write(1, "MAIN\n", 5) = 5
        Note that the file is closed right after BEGIN (and before UNITCHECK). So no handle to it can exist. But, if we add
        __DATA__ whatever
        that it changes a bit...
        open("t.pl", O_RDONLY) = 3 ... read(3, "BEGIN {\n print \"BEGIN\\n\";\n}\nU"..., 8192) = 165 write(1, "BEGIN\n", 6) = 6 fcntl(3, F_SETFD, FD_CLOEXEC) = 0 write(1, "UNITCHECK\n", 10) = 10 write(1, "CHECK\n", 6) = 6 write(1, "INIT\n", 5) = 5 write(1, "MAIN\n", 5) = 5 ... close(3) = 0 exit_group(0) = ?
        Now the file isn't closed until exit. So without __DATA__ there is no handle to the file, and the only handle (that I can find :) is DATA.

        Anyway, let us know if you'll find something in perl5db.pl or somewhere else.

Re: Introsepcting the current Perl file via DATA, even w/o DATA?
by Discipulus (Abbot) on Feb 04, 2016 at 20:56 UTC
    I hope to ear working solutions but i humbly suspect we'll not. I also suspect that this file handle is bring to existence only when logical end of the file is different from the physical one (as you have read in Special Literals) and this is due to the special token __DATA__

    If no token is found (i suspect) that anyone that was reading the code close the file before any possible existence of a Perl filehandle. Infact DATA is not available during a BEGIN block, obviously. Also the fact that $. starts just after the token, with the value of 1 make me suspicious. I've still waiting for that trick solution.. but some test is still interesting (oneliners acts like plain file here):

    perl -we "print tell DATA" Name "main::DATA" used only once: possible typo at -e line 1. tell() on unopened filehandle DATA at -e line 1. -1 perl -we "print tell DATA;__DATA__" -e"aaa" -e "bbb" + # uh?! no error! 0 perl -we "print tell DATA;__DATA__" -e"aaa" -e "bbb" -MO=Deparse BEGIN { $^W = 1; } print tell DATA; __DATA__ -e syntax OK

    About the fool idea of opening $0 and just for curiosity, was my 7th post here.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Introsepcting the current Perl file via DATA, even w/o DATA?
by stevieb (Canon) on Feb 05, 2016 at 02:26 UTC

    I dedicated a few hours to this today to see if there's a way to explicitly gain access to and modify the __DATA__ target into existence, when it wasn't explicitly defined, or gain access to $0 without opening it.

    I tried all manner of hacking the Symbol Table, trying to force things into position, tried to merge the current running script into something that could be used, and alas, I honestly think it can't be done the way you desire.

    I agree with AnonymousMonk, that you're going to have to load __FILE__. Do it in BEGIN, stash the results, and deal with the penalty.

    I have modules that'll do this, but your efficiency goes down the toilet.

    Discipulus said "I hope to hear working solutions but i humbly suspect we'll not", and I agree. I have one module that does deep file 'stuff', but from what I can tell, there's no way to hack a file handle to point to the current running script without opening it directly (I'm hoping with all I have that someone will direct us to a different understanding).

    I keenly await responses from those who might be able to correct this understanding, through perhaps perlapi, symtab or otherwise.

    Good question.

      > I agree with AnonymousMonk, that you're going to have to load __FILE__. Do it in BEGIN, stash the results, and deal with the penalty.

      The penalty of reopening a file might be minimal nowadays, especially if it is still cached by the filesystem/ harddisk, but ...

      ... I'm not sure if __FILE__ is always holding something "openable".

      For instance consider the eval flag

      C:\>perl -e "print __FILE__" -e

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2022-05-24 19:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (84 votes). Check out past polls.

    Notices?