Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

load a module or another: Dumper or dd

by Discipulus (Canon)
on Jan 09, 2019 at 22:09 UTC ( [id://1228281]=perlquestion: print w/replies, xml ) Need Help??

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

dear community,

I'm almost finished witha little module and I forget to implement debugging. In some points I'll just want to dump some datastructure, maybe preceded by a short description.

The fact is that I'm now addicted to Data::Dump cleaner output but it is no in core, while Data::Dumper is everywhere since ever, and I dont want to add a prerequisite (well I can, or I can use what in core but if so no question arise ;)

I'm planning something like:

package MyModule; use strict; use warnings; { local $@; eval { use Data::Dump; 1;}; # tried the following to simulate # Data::Dump absence but did not work # $@ = 1; no Data::Dump; use if $@, 'Data::Dumper'; } _mydump('this is %INC',\%INC); sub _mydump{ my ( $msg, $ref) = @_; print "$msg:\n"; { local $@; eval {dd $ref}; print Dumper $ref if $@; } }

But:

  • 1) I'm unable to simulate the absence of the Data::Dump module (all strawberrys have it bundled): if I try use Data::DumpNOTEXISTING I got errors about %INC or if I use require in a BEGIN block I receive print() on unopened filehandle Dumper error.
  • 2) I got also Dumper used only once warning in other tests
  • 3) Is this something sane or just a byzantinisme?
thanks

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.

Replies are listed 'Best First'.
Re: load a module or another: Dumper or dd
by 1nickt (Canon) on Jan 09, 2019 at 22:22 UTC

    I'd say slightly Byzantine. I would use Data::Dumper for public modules. But there is Module::Load::Conditional which is in core, and also:

    eval { require Data::Dump; 1} or require Data::Dumper;

    Hope this helps!


    The way forward always starts with a minimal test.

      require always returns a true value when it succeeds. (You know how Perl modules have to end in a true value? It returns that; though repeated use of require to load the same module just returns 1.) So the "1" in your code is completely unnecessary.

      eval { require Data::Dump } or require Data::Dumper;
      Thanks 1nickt and yes Byzantine ;)

      anyway:

      package MyModule; use strict; use warnings; eval { require Data::Dump; 1} or require Data::Dumper; _mydump('this is %INC',\%INC); sub _mydump{ my ( $msg, $ref) = @_; print "$msg:\n"; { local $@; eval {dd $ref}; print Dumper $ref if $@; } } #out perl sopw.pl Name "MyModule::Dumper" used only once: possible typo at sopw.pl line +15. this is %INC: print() on unopened filehandle Dumper at sopw.pl line 15.

      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.

        Hi Discipulus,

        The last message is precedence with print() -- the first is because you don't restrict that code path to the appropriate context. You could use goto conditionally:

        package MyModule; use strict; use warnings; my $Dump; if (eval { require Data::Dump; 1}) { $Dump = sub { goto &Data::Dump::dd }; } else { require Data::Dumper; $Dump = sub { goto &Data::Dumper::Dumper }; } print "this is %INC:\n"; print $Dump->(\%INC); __END__
        Output:
        this is %INC: { "Data/Dump.pm" => "/usr/share/perl5/Data/Dump.pm", "Exporter.pm" => "/usr/share/perl/5.26/Exporter.pm", "overload.pm" => "/usr/share/perl/5.26/overload.pm", "overloading.pm" => "/usr/share/perl/5.26/overloading.pm", "strict.pm" => "/usr/share/perl/5.26/strict.pm", "subs.pm" => "/usr/share/perl/5.26/subs.pm", "vars.pm" => "/usr/share/perl/5.26/vars.pm", "warnings.pm" => "/usr/share/perl/5.26/warnings.pm", "warnings/register.pm" => "/usr/share/perl/5.26/warnings/register.pm +", }


        The way forward always starts with a minimal test.
Re: load a module or another: Dumper or dd
by haukex (Archbishop) on Jan 10, 2019 at 09:46 UTC

    As an alternative, here's something I regularly use some variation of (Update: examples: 1, 2, 3) to output strings or other small data structures as part of my debug output:

    sub pp { die "bad number of args to pp" unless @_==1; return Data::Dumper->new([shift])->Terse(1)->Purity(1)->Useqq(1) ->Quotekeys(0)->Sortkeys(1)->Indent(0)->Pair('=>')->Dump; }

    As for your question of wanting to load either one or the other module, here's an example of how I would have done it - with this, you can always use dd, no matter which module is available, although of course the output will look different:

    BEGIN { if ( eval { require Data::Dump; 1 } ) { Data::Dump->import('dd'); } else { require Data::Dumper; *dd = sub { print Data::Dumper->new(\@_)->Purity(1) ->Useqq(1)->Quotekeys(0)->Sortkeys(1)->Dump }; } }
Re: load a module or another: Dumper or dd
by stevieb (Canon) on Jan 09, 2019 at 22:39 UTC

    Discipulus, here's an example using the autoload function from the Module::Load distribution. The autoload will bring in all available functions from the loaded module, where the load function only literally "requires" it (ie. doesn't bring in any functions):

    package MyModule; use strict; use warnings; use Module::Load; my $dd_loaded; BEGIN { $dd_loaded = eval { autoload Data::Dump; 1; }; use if ! $dd_loaded, 'Data::Dumper'; } _mydump(\%INC); sub _mydump{ my ($ref) = @_; if ($dd_loaded){ print "using Data::Dump dd\n"; dd $ref; } else { print "using Data::Dumper\n"; print Dumper $ref; } }

    I have Data::Dump installed, so here's my output:

    using Data::Dump dd { "bytes.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/bytes.pm", "Carp.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/Carp.pm", "constant.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/constant.pm", "Data/Dump.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/site_perl/5.26.1/Data/Dump.pm", "Data/Dumper.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/x86_64-linux/Data/Dumper.pm", "Exporter.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/Exporter.pm", "File/Spec.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/x86_64-linux/File/Spec.pm", "File/Spec/Unix.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/x86_64-linux/File/Spec/Unix.pm", "if.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/if.pm", "Module/Load.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/Module/Load.pm", "overload.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/overload.pm", "overloading.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/overloading.pm", "strict.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/strict.pm", "subs.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/subs.pm", "vars.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/vars.pm", "warnings.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/warnings.pm", "warnings/register.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/warnings/register.pm", "XSLoader.pm" => "/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/XSLoader.pm", }

    Now, if I change the following line in that script:

    $dd_loaded = eval { autoload Data::Dump; 1; };

    To this (note the "x" at the end of Data::Dump):

    $dd_loaded = eval { autoload Data::Dumpx; 1; }; # ^ <-- here

    ...the output changes, and it falls back on Data::Dumper:

    using Data::Dumper $VAR1 = { 'Carp.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26.1/li +b/5.26.1/Carp.pm', 'constant.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26. +1/lib/5.26.1/constant.pm', 'Exporter.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26. +1/lib/5.26.1/Exporter.pm', 'warnings/register.pm' => '/home/spek/perl5/perlbrew/perls/p +erl-5.26.1/lib/5.26.1/warnings/register.pm', 'Module/Load.pm' => '/home/spek/perl5/perlbrew/perls/perl-5. +26.1/lib/5.26.1/Module/Load.pm', 'XSLoader.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26. +1/lib/5.26.1/XSLoader.pm', 'vars.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26.1/li +b/5.26.1/vars.pm', 'File/Spec.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26 +.1/lib/5.26.1/x86_64-linux/File/Spec.pm', 'Data/Dumper.pm' => '/home/spek/perl5/perlbrew/perls/perl-5. +26.1/lib/5.26.1/x86_64-linux/Data/Dumper.pm', 'strict.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26.1/ +lib/5.26.1/strict.pm', 'if.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26.1/lib/ +5.26.1/if.pm', 'bytes.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26.1/l +ib/5.26.1/bytes.pm', 'warnings.pm' => '/home/spek/perl5/perlbrew/perls/perl-5.26. +1/lib/5.26.1/warnings.pm', 'File/Spec/Unix.pm' => '/home/spek/perl5/perlbrew/perls/perl +-5.26.1/lib/5.26.1/x86_64-linux/File/Spec/Unix.pm' };

    I use a BEGIN block in this case, as well as a global variable to hold the status of the Data::Dump load, but this is after all, just an example.

      Thanks stevieb,

      I was sure I answered yesterday night: the sense was: thanks this works as a charm! Sometimes my perl is not so DoWhatDiscipulsMeans ;)

      Reviewing your code during daylight I suppsosed Module::Load it is not necessary: $dd_loaded = eval { require Data::Dump; Data::Dump->import(); 1; };

      But did you noticed your output when Data::Dump is loaded succesfully?

      "Data/Dumper.pm"       => "/home/spek/perl5/perlbrew/perls/perl-5.26.1/lib/5.26.1/x86_64-linux/Data/Dumper.pm",

      It is loaded anyway! The opposite is not true..

      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: load a module or another: Dumper or dd
by perlancar (Hermit) on Jan 10, 2019 at 05:48 UTC

      Another module to simulate the absence of modules is Test::Without::Module, which also makes modules disappear temporarily.

Re: load a module or another: Dumper or dd
by markong (Pilgrim) on Jan 09, 2019 at 22:29 UTC

    I hope to have understood what you're trying to achieve: if you want to test that code, you should re-factor the use Data::Dump; 1; inside a sub{} and then make that explode at will by mocking. In this case Test::MockObject would help a lot with testing, but seen that you're trying to minimize required deps you have to decide if the whole thing is still worth the extra mocking work "by hand".

      My understanding is that OP wants to use Data::Dump if it's found on the system, and Data::Dumper if it isn't. I don't believe that this is a test situation that could be mocked out.

        Yes that part (the fact that the OP wants to optionally use a module if present) is quite clear. What it is not is why he wants to mock the absence of that particular module in its current setup/environment!

        The only thing which I thought would make sense is for testing that optionally loading code logic: I assumed that the problem was testing the module absence, which it can be done having a subroutine which mocks the loading of a fantasy module, e.g.: sub{ use Supercalifragilistichespiralidoso; 1 }

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (3)
As of 2024-03-29 05:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found