http://qs1969.pair.com?node_id=11131393

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

I'm clearly missing something here. All examples run with use strict/warnings

If I load File::Find with:

use strict; use warnings; use File::Find;

the following error goes away:

Name "File::Find::prune" used only once: possible typo at...

But if I use:

Require File::Find; File::Find->import(qw(find));

the error appears. I am confused by this. Due to a decision by fedora/redhat to remove File::Find from core modules, the Require loading is required, not my choice unfortunately.

In &wanted, I'm trying to prune a directory, following examples and explanations from for example here and in particular here.

This is a simplified example, but shouldn't matter since it's the single occurrence of $File::Find::prune = 1 that is making Perl complain.
sub wanted { if ($File::Find::name =~ m%^/path/detection%){ $File::Find::prune = 1; return; } }

Replies are listed 'Best First'.
Re: Why $File::Find::prune = 1 returns used only once error
by haukex (Archbishop) on Apr 17, 2021 at 07:56 UTC

    The difference is that use executes the module's code at compile time, basically making $File::Find::prune known earlier than with require, which executes at runtime. One really simple workaround is to use the variable twice: $File::Find::prune = $File::Find::prune = 1; (Update: Of course this removes typo protection if you happen to make the same typo twice, e.g. if you used copy&paste on the variable name.)

      This was also a clever little trick, which is likewise good to know as a possible solution. In the end, however, I ended up, basically by accident, using it twice anyway, for an actual reason, so the issue went away, but it's good to be aware of these little intricacies, which actually led me to not use this feature in the past since the warnings were a mystery to me and I just moved on, so now that's all resolved and working fine. It's odd what missing one key bit of information that isn't very intuitive or obvious can do in terms of hindering progress and development.
Re: Why $FIle::Find::prune = 1 returns used only once error
by Anonymous Monk on Apr 17, 2021 at 16:15 UTC

    There is always no warnings 'once';. My personal preference is to use this locally to the error, so that you cam still detect inadvertent cases of this elsewhere in your code.

    sub wanted { if ($File::Find::name =~ m%^/path/detection%){ no warnings 'once'; $File::Find::prune = 1; return; } }

    Don't worry about adding execution time to your subroutine; this pragma is applied at compile time.

      Or mention it twice.

      sub wanted { if ($File::Find::name =~ m%^/path/detection%){ $File::Find::prune = 1; return; } } require File::Find; $File::Find::name if 0; $File::Find::prune if 0;

      Seeking work! You can reach me at ikegami@adaelis.com

      This was a nice one too, I didn't know about the no warnings 'once' to get rid of such warnings on start, that could be in general useful. However, I just realized, after reviewing this thread, that in the end, I used prune = 1 twice once I got the first one debugged, in two blocks, and that made the Perl warning go away of course, since it was used 2 times, but I will note the no warnings 'once' for future reference.

Re: Why $FIle::Find::prune = 1 returns used only once error
by jcb (Parson) on Apr 16, 2021 at 22:19 UTC

    First, complain about the mispackaging of Perl by your distribution. Omitting core modules like that is a bug in the distribution.

    Second, use Module is equivalent to BEGIN { require Module; Module->import; }; note the lack of arguments to the import method. If asked for default imports, File::Find apparently does some magic to make $File::Find::prune known that is not done if you explicitly import only the find procedure.

      If asked for default imports, File::Find apparently does some magic to make $File::Find::prune known that is not done if you explicitly import only the find procedure.

      No, it has nothing to do with import.

      Bar.pm:

      package Bar; use warnings; use strict; our $x; 1;

      foo.pl:

      use warnings; use strict; use FindBin; use lib $FindBin::Bin; #use Bar; # no warning #use Bar (); # no warning #BEGIN { require Bar; } # no warning require Bar; # warning when var used only once $Bar::x = 1; # warning with plain 'require' #$Bar::x = $Bar::x = 1; # no warning

      Edit: Added use Bar (); example.

      Fedora isn't my distribution, but they distribute this code, so I have to make it run on Fedora without crashing on start due to missing module error, obviously I agree that this is a bug in Redhat/Fedora, but they believe it's a feature to remove core modules from Perl's core modules and then make you install them separately, so that's outside of the scope I can fix.

      While their packager can fix and I think does add as a dependency these missing core modules, many users do not use the packaged version since they want a current version, which leads to this issue. Again, the problem here which I'm looking for a solution is the Perl warning, not making Fedora do what I want them to do, which I can't do.

      I tried removing the explicit 'find' from File::Find->import, made no difference, though it was a good suggestion.

      This error appears without running this logic, it's a perl compile time warning.

      I'm guessing when "use File::Find;" is used to load the module, Perl then knows not to worry about specific instances of File::Find in the following code, but when require File::Find is used later one inside of a sub, it doesn't, but that seems weird because Perl knows not to complain about $File::Find::name for example.

      As noted, this is very confusing and is I think why I didn't use $File::Find::prune in the past for this feature, I could not figure out how to make Perl not complain on start about this without "use File::Find;" which can't be done.

        While their packager can fix and I think does add as a dependency these missing core modules, many users do not use the packaged version since they want a current version, which leads to this issue.

        There is nothing to prevent you from declaring File::Find as a dependency of your module. I always try to include core modules as dependencies when I release a dist as it avoids precisely this problem. It also means that I don't have to worry about p5p removing modules from core at a later date.

        BTW, the Name "File::Find::prune" used only once: possible typo at... message you receive is not an error, it is merely a warning.


        🦛

        because Perl knows not to complain about $File::Find::name for example.

        Based only on the code in the root node, I get the "used only once" warnings for both $File::Find::prune and $File::Find::name. Are you sure you're not using the latter twice?

        I tried removing the explicit 'find' from File::Find->import, made no difference

        find is exported by default, so yes, it doesn't make a difference. If you didn't call import at all (the equivalent with use is "use File::Find ();"), then you'd have to call it as File::Find::find(). And BTW, the other difference between importing find() at runtime (e.g. require + ->import) vs. compile time (e.g. use) is that in the latter case, you could omit the parens on the find function call.

        I found a way to trick the Perl compiler into not complaining, though I suspect this may be a bug but that's also not something I can fix or correct, the solution proved to be quite simple:

        $File::Find::prune = 1 if defined $File::Find::prune;

        I'd definitely put this in the category of a hackish fix, but it's easy to do, and worked, and didn't take any convolutions so I guess it's good enough. Hopefully anyone in the future who comes across this issue can resolve the problem with this simple trick. Since $File::Find::prune wont' be defined prior to require File::Find being run, perl doesn't care about it when it's checking the code on start, and since when this feature runs, File::Find::prune is defined, it works as expected. Not super intuitive, obviously, but there you have it, a fix, that works!

        Are you calling import in a BEGIN block? Using use implicitly wraps the require and import into a BEGIN block. If you are waiting to load File::Find until runtime, then the compiler's warning is correct: perl has not seen File::Find yet and your code only mentions File::Find::prune once.

        The solution is to always call import in a BEGIN block, so the effects will be visible during the rest of the compilation.