Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

locating specific function calls

by markov (Scribe)
on Apr 11, 2007 at 12:33 UTC ( #609351=perlquestion: print w/ replies, xml ) Need Help??
markov has asked for the wisdom of the Perl Monks concerning the following question:

In an attempt to do quality-control, I am looking for a way to run all occurrences of a certain function in a file. For instance:
use Locale::TextDomain; my $q = __x ("Error reading file '{file}': {err}", file => $file, err => $!);

When testing, I would like to call this __x() to check that the file and err parameters are present. Of course, a different __x() during test than at run-time. The same for all uses of the same function within the code, also in the hard-to-reach corners.

I prefer to avoid the use of PPI to process dozens of files, in favor of some B:: trick. Any ideas?

Comment on locating specific function calls
Select or Download Code
Re: locating specific function calls
by Corion (Pope) on Apr 11, 2007 at 12:41 UTC

    I'm not sure I understand what you want correctly. Let me restate your problem and propose a solution - I think this solution is not a good solution but I think it would solve the problem as I perceive it:

    I think your problem is that you have a printf-like function __x() and want to make sure that wherever you have a call to it all template names in the string are present in the additional parameter list. This is complicated by the fact that people could use @additional_args, or by helper subroutines like __x_err, which is

    sub __x_err { my $msg = shift; __x("$msg: {err}", err => $!, @_); };
    , so a simple regular expression won't help too much.

    I think one tedious solution would be to use Devel::Cover to map out all branches in your code that lead to a call of __x() and then to (manually) generate those parameter conditions that are missing from your test suite to make sure all those branches are actually reached.

    I thought of writing a program that generates such conditions automatically, but so far I haven't felt the need - but maybe there is existing prior art. Hence my solution requires lots of manual work just to make sure your calling conventions are always followed. Using a regular expression to fish out all calls to __x() and then verifying that they all match a certain pattern might be saner, but there are always edge cases that might make this infeasible as well, unless you find that all your uses of __x() and __x_err() are simple uses (as they should maybe be, for a simple logging routine).

    Now, did I understand your problem well enough to propose a (bad, tedious) solution or did I run off in the wrong direction alltogether? :)

      My implementation will be a module, which does help people with internationalization. I can offer the automatic checks on call correctness, when the users refrain from the use of @additional_args and $msg, as in your example. So that's not really a no-go.

      What certainly is not possible, it to generate all conditions (automatically) to reach all corners of the code.

      I know that the first parameter of the function is a string, and that the other parameters are named. I only have to check wether the names are within the string. As second activity, for each of the strings I like to see that they are used: they must be translatable using gettext().

      Thanks for your ideas so far.

Re: locating specific function calls
by shmem (Canon) on Apr 11, 2007 at 12:44 UTC
    I am looking for a way to run all occurrences of a certain function in a file.

    I don't get what you are up to. Run the file, and all ocurrences of the function will eventually been run... what is your __x() function about?

    Maybe you are looking for a way to wrap functions?

    if ($debug) { my $sub = \&some_function; my $wrapsub = sub { __x(@_); $sub->(@_) }; *some_function = $wrapsub; }

    Then, whenever some_function() is invoked, your __x() is called first.

    --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}
      No, that's not the direction I want to go. I would like to find the calls to a function without running the module; the calls can be anywhere, even within an if() which is always false. I still need to find that application of the function.
Re: locating specific function calls
by Fletch (Chancellor) on Apr 11, 2007 at 13:10 UTC

    B::Xref may be of use? I'm likewise fuzzy on what's being requested. Perhaps Params::Validate would be another way of attacking this from the callee's end instead of verifying the callers.

      Ok, let tell me a little more about the plan.

      When your program needs to support multiple languages, then you can use gettext. Its use has various syntaxes, of which Locale::TextDomain seems to be the nicest: __x("found {count} files", count => 6). To use gettext(), the string needs to be translated into all supported languages.

      After translation, these messages have to go somewhere. Of course, you can simply die/warn/croak, but a generally applicable module is not sure about the destination of the output. More complex applications have to apply nasty tricks to catch and handle "die()" in third-party code. With Log::Dispatch and Log::Log4perl you can help the message find its way cleaner, but do not translate

      So, what I want to do, is link the translation framework with the distribution network. Any piece of distributed text must be translated, and therefore I would like to avoid the explicit call to __x(). I try to write this:

      use Log::Report textdomain => 'my-domain'; report trace => "{count} files", count => 10;
      with "compile-time" parameter checking. In stead of the unchecked
      use Log::Report textdomain => 'my-domain'; report trace => __x("{count} files", count => 10);

      In the major module of a set of related modules, you will be able to say:

      use Log::Report textdomain => 'my-domain' , directory => '/usr/share/locale';
      etc: I do not want to repeat configuration information in each pm file. And I do not want to limit the whole application (containing multiple distributions) to one domain. The default use of the current modules have these limitations: I wish to change the default.

      In the "main" script, you must be able to say something like:

      use Log::Report destinations => [ CRITICAL => 'syslog' , 'ERRORS-' => 'die' , 'TRACE,INFO' => 'ignore' ];

      Be aware: all syntax still under development, and will certainly be clearer.

      To come back to my original question: I want to simplify the use and automatically check the "report()" calls without running the program.

Re: locating specific function calls
by Moron (Curate) on Apr 11, 2007 at 13:46 UTC
    ^C It sounds like you are talking about subroutine call profiling, ^I for which Devel::AutoProfiler might be useful.
    __________________________________________________________________________________

    ^M Free your mind!

    Key to hats: ^I=white ^B=black ^P=yellow ^E=red ^C=green ^M=blue - see Moron's scratchpad for fuller explanation.

      Although a good hint (thanks), this collects the other side of the subroutine interface: it creates wrappers around the subroutine definitions (which are simple to find). What I need is access to the subroutine application... without running the code.

Re: locating specific function calls
by Moron (Curate) on Apr 11, 2007 at 14:22 UTC
    ^C Next interpretation of OP then: to find all calls to a subroutine without executing it.

    ^I The PPI set of modules provide the means to parse Perl code without executing it and includes facilities for documenting and structurising the parse tree.

    __________________________________________________________________________________

    ^M Free your mind!

    Key to hats: ^I=white ^B=black ^P=yellow ^E=red ^C=green ^M=blue - see Moron's scratchpad for fuller explanation.

      My question explicitly states that I do not want to parse thousands of lines with PPI. I am looking for a different solution.

      In my terminolgy, the application is the end-user/final product, where modules are (reusable) components.

        Oops forgot that, sorry!
        __________________________________________________________________________________

        ^M Free your mind!

        Key to hats: ^I=white ^B=black ^P=yellow ^E=red ^C=green ^M=blue - see Moron's scratchpad for fuller explanation.

Re: locating specific function calls
by Moron (Curate) on Apr 11, 2007 at 14:45 UTC
    ^C Is this then a 'cheapo' parse?

    ^I on *NIX you can grep for the subroutine identifier, or on Windows command line prompt, something like:

    type *.pl | perl -e 'print grep /\b__x\b/, <>;'
    __________________________________________________________________________________

    ^M Free your mind!

    Key to hats: ^I=white ^B=black ^P=yellow ^E=red ^C=green ^M=blue - see Moron's scratchpad for fuller explanation.

      Yes, that is about what xgettext is doing to create a lexicon which needs to be translated into all languages. I would really love to build this lexicon without that trick.
      You need double quote in Windows. But doesn't this work?
      perl -e "print grep /\b__x\b/, <>;" *.pl

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2014-08-29 13:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (280 votes), past polls