http://qs321.pair.com?node_id=11147506

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

I created a CSV.pm in my local source dir. Which in retrospect might have been a bad idea, but up to today it's worked fine since the local dir is at the top of @INC.

However today, it started acting very strange. In debug I did this:

DBG> f CSV (or f CSV.pm)
and it said:
DB<1> Choosing (eval 125)[/usr/local/share/perl5/Text/CSV.pm:114] matc +hing 'CSV': 1 use Text::CSV_XS 1.02 2: ;
which is a 2-line file it found somewhere I guess? I grepped my each DIR in @INC and didn't see a file like that. /usr/local/share/perl5/Text/CSV.pm has hundreds of lines, but the file it found apparently only has those two. @INC is:
0 '/var/www/cgi-bin/project' 1 '../' 2 '/usr/local/lib64/perl5' 3 '/usr/local/share/perl5' 4 '/usr/lib64/perl5/vendor_perl' 5 '/usr/share/perl5/vendor_perl' 6 '/usr/lib64/perl5' 7 '/usr/share/perl5' 8 '.' DB<2>
and I have a CSV.pm in '/var/www/cgi-bin/project' with 755 privs, which it doesn't find. Also, if I add a line near the top of the pl like CSV::test() , a method in MY CSV.pm, it gets there. Any suggestions kind monks?

TY

Replies are listed 'Best First'.
Re: @INC order not followed
by Fletch (Bishop) on Oct 19, 2022 at 03:25 UTC

    As it says those two lines are the contents of an eval done at line 114 from Text::CSV (which it appears you have a very slightly behind copy; in the latest that call has moved a few lines). The perldebug docs for f say that if you don't give it a full path the argument is taken as a regex which it searches for in the keys for %main::. If something else pulled in Text::CSV and it ran its eval and that happens to be the first match for qr{^_<.*CSV} as the keys happen to be ordered then that's what it'll switch to. Has nothing to do with @INC not being honoured.

    Edit: the guts of the f command's search (5.34.0 version):

    # if not in magic file list, try a close match. if ( !defined $main::{ '_<' . $file } ) { if ( ($try) = grep( m#^_<.*$file#, keys %main:: ) ) { { $try = substr( $try, 2 ); print $OUT "Choosing $try matching '$file':\n"; $file = $try; } } ## end if (($try) = grep(m#^_<.*$file#... } ## end if (!defined $main::{ ...

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      Ahh yes that does make sense, EXCEPT why did the f yield a 2-line file? f is supposed to match on filenames I thought- not grep into files? Why didnt it show me ALL of Text::CSV.pm ?

      I learned something here that f doesn't find the first match in @INC order- it finds a random match on eval of the regex? Seems odd NOT to yield match 1 in @INC since that's how Perl will run the files so it would make sense to go to THAT file I would think. Also in the past it went to MY CSV which I guess was just luck.

      TYVM!

        Because that's not what matched on when it searched for qr/CSV/ on the entire list of loaded "file" names. When you (string?) eval something it stubs in a representation of the location of the eval call and its showing you just the text that got eval'd there.

        And again @INC and its ordering doesn't have anything to do with the order keys returns things for the %main:: stash ( a hash, but it's a special hash). If you have %foo = qw( 2 a 22 b 222 c ) then you similarly can't say what (grep /2/, keys %h)[0] is going to wind up returning.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        Please note that to list a file after you have chosen it with f, you need to use l to list one "page" at a time (more on that can be found in the debugger's help interface -- you will want to h to get the general overview of possible commands, and h l to get more help on l specifically; v might also be of use).

        Caveat: I am not a debugger expert. I actually learned this earlier today when I read your post, and then started playing around in the debugger to replicate. But I knew other monks were much better at the debugger than I, and could explain the f portion of your difficulty, whereas I couldn't. I waited until I read good explanations of that part, but since no one else explained how to see the contents of the file, I thought I'd now share what I learned, one debugger-newb to another.

Re: @INC order not followed
by LanX (Saint) on Oct 18, 2022 at 19:28 UTC
    > it's worked fine since the local dir is at the top of @INC.

    Nope, your output shows '.' is last, and you have '..' on second position.

    So what's your current working directory?

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

      Greetings Rolf my wd is $INC[0] = '/var/www/cgi-bin/project'
Re: @INC order not followed
by ikegami (Patriarch) on Oct 19, 2022 at 19:17 UTC

    The correct modules are being loaded. f CSV was simply too vague, matching multiple files.


    Let's start by looking at what f does.

    f filename

    Switch to viewing a different file or eval statement. If filename is not a full pathname found in the values of %INC, it is considered a regex.

    evaled strings (when accessible) are considered to be filenames: f (eval 7) and f eval 7\b access the body of the 7th evaled string (in the order of execution). The bodies of the currently executed eval and of evaled strings that define subroutines are saved and thus accessible.

    You did not provide a full path, so you provided a regex.

    As it turns out, there were more than one match for the regex pattern CSV. /var/www/cgi-bin/project/CSV.pm matched. Your program also loads Text::CSV, so /usr/local/share/perl5/Text/CSV.pm matched as well. But there are at least two more matches.

    Text::CSV is not a CSV parser. It's a front end for Text::CSV_PP and Text::CSV_XS. It loads one of these modules, and it does so using an eval. This is the match you obtained. The eval that was used to load Text:CSV_XS (which is also a match).

    So that's four possible matches for your query, and I suspect you get one "at random".


    To get the correct file, first find out which one was loaded by a use/require.

    If you used the bareword syntax (use Foo or require Foo rather than require "Foo.pm"), first convert the bareword to a string using these rules: Replace every :: with / (even on Windows), then append .pm.

    Then look at the the value of the element of %INC that has that string for key.

    DB<1> x $INC{"CSV.pm"} 0 '/var/www/cgi-bin/project/CSV.pm' DB<2> x $INC{"Text/CSV.pm"} 0 '/home/ikegami/usr/perlbrew/perls/5.36.0t/lib/site_perl/5.36.0/Text +/CSV.pm' DB<3> x $INC{"Text/CSV_XS.pm"} 0 '/home/ikegami/usr/perlbrew/perls/5.36.0t/lib/site_perl/5.36.0/x86_ +64-linux-thread-multi/Text/CSV_XS.pm'

    Alternatively, you could perform the same search f CSV performs.

    DB<4> x grep /^_<.*CSV/, keys %:: 0 '_<(eval 11)[/home/ikegami/usr/perlbrew/perls/5.36.0t/lib/site_perl +/5.36.0/Text/CSV.pm:136]' 1 '_</home/ikegami/usr/perlbrew/perls/5.36.0t/lib/site_perl/5.36.0/x8 +6_64-linux-thread-multi/Text/CSV_XS.pm' 2 '_</home/ikegami/usr/perlbrew/perls/5.36.0t/lib/site_perl/5.36.0/Te +xt/CSV.pm' 3 '_</var/www/cgi-bin/project/CSV.pm'

    From there, you can use f

    f /var/www/cgi-bin/project/CSV.pm

    Finally, if your module was named My::CSV and stored as /var/www/cgi-bin/project/My/CSV.pm, you could have used

    f My/CSV
    since that only has one match.
      TY All for the enlightening info, Im a better JAPH for it!