File Coverage

blib/lib/Data/Dmp.pm
Criterion Covered Total %
statement 97 110 88.1
branch 47 54 87.0
condition 17 21 80.9
subroutine 12 14 85.7
pod 4 4 100.0
total 177 203 87.1


line stmt bran cond sub pod time code
1             ## no critic: Modules::ProhibitAutomaticExportation
2              
3              
4             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
5             our $DATE = '2022-08-28'; # DATE
6             our $DIST = 'Data-Dmp'; # DIST
7             our $VERSION = '0.242'; # VERSION
8              
9             use 5.010001;
10 1     1   498 use strict;
  1         8  
11 1     1   12 use warnings;
  1         2  
  1         20  
12 1     1   3  
  1         1  
  1         35  
13             use Scalar::Util qw(looks_like_number blessed reftype refaddr);
14 1     1   5  
  1         2  
  1         1670  
15             require Exporter;
16             our @ISA = qw(Exporter);
17             our @EXPORT = qw(dd dmp);
18             our @EXPORT_OK = qw(dd_ellipsis dmp_ellipsis);
19              
20             # for when dealing with circular refs
21             our %_seen_refaddrs;
22             our %_subscripts;
23             our @_fixups;
24              
25             our $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS = 70;
26             our $OPT_PERL_VERSION = "5.010";
27             our $OPT_REMOVE_PRAGMAS = 0;
28             our $OPT_DEPARSE = 1;
29             our $OPT_STRINGIFY_NUMBERS = 0;
30              
31             # BEGIN COPY PASTE FROM Data::Dump
32             my %esc = (
33             "\a" => "\\a",
34             "\b" => "\\b",
35             "\t" => "\\t",
36             "\n" => "\\n",
37             "\f" => "\\f",
38             "\r" => "\\r",
39             "\e" => "\\e",
40             );
41              
42             # put a string value in double quotes
43             local($_) = $_[0];
44              
45 17     17   29 # If there are many '"' we might want to use qq() instead
46             s/([\\\"\@\$])/\\$1/g;
47             return qq("$_") unless /[^\040-\176]/; # fast exit
48 17         31  
49 17 100       62 s/([\a\b\t\n\f\r\e])/$esc{$1}/g;
50              
51 1         9 # no need for 3 digits in escape for these
52             s/([\0-\037])(?!\d)/sprintf('\\%o',ord($1))/eg;
53              
54 1         3 s/([\0-\037\177-\377])/sprintf('\\x%02X',ord($1))/eg;
  0         0  
55             s/([^\040-\176])/sprintf('\\x{%X}',ord($1))/eg;
56 1         2  
  0         0  
57 1         2 return qq("$_");
  0         0  
58             }
59 1         4 # END COPY PASTE FROM Data::Dump
60              
61             # BEGIN COPY PASTE FROM String::PerlQuote
62             local($_) = $_[0];
63             s/([\\'])/\\$1/g;
64             return qq('$_');
65 3     3   6 }
66 3         7 # END COPY PASTE FROM String::PerlQuote
67 3         7  
68             my $code = shift;
69              
70             state $deparse = do {
71             require B::Deparse;
72 4     4   6 B::Deparse->new("-l"); # -i option doesn't have any effect?
73             };
74 4         5  
75 1         5 my $res = $deparse->coderef2text($code);
76 1         55  
77             my ($res_before_first_line, $res_after_first_line) =
78             $res =~ /(.+?)^(#line .+)/ms;
79 4         3495  
80             if ($OPT_REMOVE_PRAGMAS) {
81 4         51 $res_before_first_line = "{";
82             } elsif ($OPT_PERL_VERSION < 5.016) {
83             # older perls' feature.pm doesn't yet support q{no feature ':all';}
84 4 100       16 # so we replace it with q{no feature}.
    50          
85 3         6 $res_before_first_line =~ s/no feature ':all';/no feature;/m;
86             }
87             $res_after_first_line =~ s/^#line .+//gm;
88              
89 1         6 $res = "sub" . $res_before_first_line . $res_after_first_line;
90             $res =~ s/^\s+//gm;
91 4         16 $res =~ s/\n+//g;
92             $res =~ s/;\}\z/}/;
93 4         10 $res;
94 4         17 }
95 4         16  
96 4         12 $_[0] =~ /\A-?[A-Za-z_][A-Za-z0-9_]*\z/ ||
97 4         9 $_[0] =~ /\A-?[1-9][0-9]{0,8}\z/ ? $_[0] : _double_quote($_[0]);
98             }
99              
100             my ($val, $subscript) = @_;
101 9 100 100 9   46  
102             my $ref = ref($val);
103             if ($ref eq '') {
104             if (!defined($val)) {
105             return "undef";
106 49     49   77 } elsif (looks_like_number($val) && !$OPT_STRINGIFY_NUMBERS &&
107             # perl does several normalizations to number literal, e.g.
108 49         64 # "+1" becomes 1, 0123 is octal literal, etc. make sure we
109 49 100       90 # only leave out quote when the number is not normalized
110 27 100 100     151 $val eq $val+0 &&
    100 100        
      100        
111 1         3 # perl also doesn't recognize Inf and NaN as numeric
112             # literals (ref: perldata) so these unquoted literals will
113             # choke under 'use strict "subs"
114             $val !~ /\A-?(?:inf(?:inity)?|nan)\z/i
115             ) {
116             return $val;
117             } else {
118             return _double_quote($val);
119             }
120             }
121             my $refaddr = refaddr($val);
122 15         32 $_subscripts{$refaddr} //= $subscript;
123             if ($_seen_refaddrs{$refaddr}++) {
124 11         22 my $target = "\$var" .
125             ($_subscripts{$refaddr} ? "->$_subscripts{$refaddr}" : "");
126             push @_fixups, "\$var->$subscript=$target;";
127 22         40 return _single_quote($target);
128 22   100     89 }
129 22 100       46  
130             my $class;
131 3 50       11  
132 3         7 if ($ref eq 'Regexp' || $ref eq 'REGEXP') {
133 3         5 require Regexp::Stringify;
134             return Regexp::Stringify::stringify_regexp(
135             regexp=>$val, with_qr=>1, plver=>$OPT_PERL_VERSION);
136 19         23 }
137              
138 19 100 66     46 if (blessed $val) {
139 1         477 $class = $ref;
140 1         604 $ref = reftype($val);
141             }
142              
143             my $res;
144 18 100       34 if ($ref eq 'ARRAY') {
145 2         3 $res = "[";
146 2         6 my $i = 0;
147             for (@$val) {
148             $res .= "," if $i;
149 18         20 $res .= _dump($_, "$subscript\[$i]");
150 18 100       43 $i++;
    100          
    100          
    100          
    50          
151 4         6 }
152 4         5 $res .= "]";
153 4         6 } elsif ($ref eq 'HASH') {
154 8 100       12 $res = "{";
155 8         18 my $i = 0;
156 8         15 for (sort keys %$val) {
157             $res .= "," if $i++;
158 4         5 my $k = _quote_key($_);
159             my $v = _dump($val->{$_}, "$subscript\{$k}");
160 5         7 $res .= "$k=>$v";
161 5         6 }
162 5         25 $res .= "}";
163 9 100       13 } elsif ($ref eq 'SCALAR') {
164 9         15 if (defined $class) {
165 9         26 $res = "do{my\$o="._dump($$val, $subscript).";\\\$o}";
166 9         21 } else {
167             $res = "\\"._dump($$val, $subscript);
168 5         8 }
169             } elsif ($ref eq 'REF') {
170 3 100       7 $res = "\\"._dump($$val, $subscript);
171 1         3 } elsif ($ref eq 'CODE') {
172             $res = $OPT_DEPARSE ? _dump_code($val) : 'sub{"DUMMY"}';
173 2         10 } else {
174             die "Sorry, I can't dump $val (ref=$ref) yet";
175             }
176 1         4  
177             $res = "bless($res,"._double_quote($class).")" if defined($class);
178 5 100       26 $res;
179             }
180 0         0  
181             our $_is_dd;
182             our $_is_ellipsis;
183 18 100       37 local %_seen_refaddrs;
184 18         33 local %_subscripts;
185             local @_fixups;
186              
187             my $res;
188             if (@_ > 1) {
189             $res = "(" . join(",", map {_dump($_, '')} @_) . ")";
190 28     28   44 } else {
191 28         37 $res = _dump($_[0], '');
192 28         34 }
193             if (@_fixups) {
194 28         31 $res = "do{my\$var=$res;" . join("", @_fixups) . "\$var}";
195 28 50       69 }
196 0         0  
  0         0  
197             if ($_is_ellipsis) {
198 28         48 $res = substr($res, 0, $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS) . '...'
199             if length($res) > $OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS;
200 28 100       80 }
201 2         7  
202             if ($_is_dd) {
203             say $res;
204 28 100       47 return wantarray() || @_ > 1 ? @_ : $_[0];
205 2 100       9 } else {
206             return $res;
207             }
208             }
209 28 50       34  
210 0         0  
211 0 0 0     0  
212             1;
213 28         145 # ABSTRACT: Dump Perl data structures as Perl code
214              
215              
216             =pod
217 0     0 1 0  
  0         0  
218 26     26 1 4065 =encoding UTF-8
219              
220 0     0 1 0 =head1 NAME
  0         0  
  0         0  
221 2     2 1 868  
  2         5  
222             Data::Dmp - Dump Perl data structures as Perl code
223              
224             =head1 VERSION
225              
226             This document describes version 0.242 of Data::Dmp (from Perl distribution Data-Dmp), released on 2022-08-28.
227              
228             =head1 SYNOPSIS
229              
230             use Data::Dmp; # exports dd() and dmp()
231             dd [1, 2, 3]; # prints "[1,2,3]"
232             $var = dmp({a => 1}); # -> "{a=>1}"
233              
234             Print truncated dump (capped at L</$Data::Dmp::OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS>
235             characters):
236              
237             use Data::Dmp qw(dd_ellipsis dmp_ellipsis);
238             dd_ellipsis [1..100];
239              
240             =head1 DESCRIPTION
241              
242             Data::Dmp is a Perl dumper like L<Data::Dumper>. It's compact (only about 200
243             lines of code long), starts fast and does not use any non-core modules except
244             L<Regexp::Stringify> when dumping regexes. It produces compact single-line
245             output (similar to L<Data::Dumper::Concise>). It roughly has the same speed as
246             Data::Dumper (usually a bit faster for smaller structures) and faster than
247             L<Data::Dump>, but does not offer the various formatting options. It supports
248             dumping objects, regexes, circular structures, coderefs. Its code is first based
249             on L<Data::Dump>: I removed all the parts that I don't need, particularly the
250             pretty formatting stuffs) and added some features that I need like proper regex
251             dumping and coderef deparsing.
252              
253             =head1 VARIABLES
254              
255             =head2 $Data::Dmp::OPT_PERL_VERSION
256              
257             String, default: 5.010.
258              
259             Set target Perl version. If you set this to, say C<5.010>, then the dumped code
260             will keep compatibility with Perl 5.10.0. This is used in the following ways:
261              
262             =over
263              
264             =item * passed to L<Regexp::Stringify>
265              
266             =item * when dumping code references
267              
268             For example, in perls earlier than 5.016, feature.pm does not understand:
269              
270             no feature ':all';
271              
272             so we replace it with:
273              
274             no feature;
275              
276             =back
277              
278             =head2 $Data::Dmp::OPT_REMOVE_PRAGMAS
279              
280             Bool, default: 0.
281              
282             If set to 1, then pragmas at the start of coderef dump will be removed. Coderef
283             dump is produced by L<B::Deparse> and is of the form like:
284              
285             sub { use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval'; $a <=> $b }
286              
287             If you want to dump short coderefs, the pragmas might be distracting. You can
288             turn turn on this option which will make the above dump become:
289              
290             sub { $a <=> $b }
291              
292             Note that without the pragmas, the dump might be incorrect.
293              
294             =head2 $Data::Dmp::OPT_DEPARSE
295              
296             Bool, default: 1.
297              
298             Can be set to 0 to skip deparsing code. Coderefs will be dumped as
299             C<sub{"DUMMY"}> instead, like in Data::Dump.
300              
301             =head2 $Data::Dmp::OPT_STRINGIFY_NUMBERS
302              
303             Bool, default: 0.
304              
305             If set to true, will dump numbers as quoted string, e.g. 123 as "123" instead of
306             123. This might be helpful if you want to compute the hash of or get a canonical
307             representation of data structure.
308              
309             =head2 $Data::Dmp::OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS
310              
311             Int, default: 70.
312              
313             Used by L</dd_ellipsis> and L</dmp_ellipsis>.
314              
315             =head1 BENCHMARKS
316              
317             [1..10]:
318             Rate/s Precision/s Data::Dump Data::Dumper Data::Dmp
319             Data::Dump 24404 95 -- -61.6% -75.6%
320             Data::Dumper 63580 210 160.5+-1.3% -- -36.4%
321             Data::Dmp 99940 130 309.5+-1.7% 57.18+-0.55% --
322            
323             [1..100]:
324             Rate/s Precision/s Data::Dump Data::Dumper Data::Dmp
325             Data::Dump 2934.3 7.8 -- -75.3% -76.2%
326             Data::Dumper 11873 32 304.6+-1.5% -- -3.7%
327             Data::Dmp 12323.4 4 320+-1.1% 3.8+-0.28% --
328            
329             Some mixed structure:
330             Rate/s Precision/s Data::Dump Data::Dmp Data::Dumper
331             Data::Dump 7161 12 -- -69.3% -78.7%
332             Data::Dmp 23303 29 225.43+-0.7% -- -30.6%
333             Data::Dumper 33573 56 368.8+-1.1% 44.07+-0.3% --
334              
335             =head1 FUNCTIONS
336              
337             =head2 dd
338              
339             Usage:
340              
341             dd($data, ...); # returns $data
342              
343             Exported by default. Like C<Data::Dump>'s C<dd> (a.k.a. C<dump>), print one or
344             more data to STDOUT. Unlike C<Data::Dump>'s C<dd>, it I<always> prints and
345             return I<the original data> (like L<XXX>), making it convenient to insert into
346             expressions. This also removes ambiguity and saves one C<wantarray()> call.
347              
348             =head2 dmp
349              
350             Usage:
351              
352             my $dump = dmp($data, ...);
353              
354             Exported by default. Return dump result as string. Unlike C<Data::Dump>'s C<dd>
355             (a.k.a. C<dump>), it I<never> prints and only return the dump result.
356              
357             =head2 dd_ellipsis
358              
359             Usage:
360              
361             dd_ellipsis($data, ...); # returns data
362              
363             Just like L</dd>, except will truncate its output to
364             L</$Data::Dmp::OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS> characters if dump is too long.
365             Note that truncated dump will probably not be valid Perl code.
366              
367             =head2 dmp_ellipsis
368              
369             Usage:
370              
371             my $dump = dd_ellipsis($data, ...); # returns data
372              
373             Just like L</dmp>, except will truncate dump result to
374             L</$Data::Dmp::OPT_MAX_DUMP_LEN_BEFORE_ELLIPSIS> characters if dump is too long.
375             Note that truncated dump will probably not be valid Perl code.
376              
377             =head1 FAQ
378              
379             =head2 When to use Data::Dmp? How does it compare to other dumper modules?
380              
381             Data::Dmp might be suitable for you if you want a relatively fast pure-Perl data
382             structure dumper to eval-able Perl code. It produces compact, single-line Perl
383             code but offers little/no formatting options. Data::Dmp and Data::Dump module
384             family usually produce Perl code that is "more eval-able", e.g. it can recreate
385             circular structure.
386              
387             L<Data::Dump> produces visually nicer output (some alignment, use of range
388             operator to shorten lists, use of base64 for binary data, etc) but no built-in
389             option to produce compact/single-line output. It's more suitable for debugging.
390             It's also relatively slow. I usually use its variant, L<Data::Dump::Color>, for
391             console debugging.
392              
393             L<Data::Dumper> is a core module, offers a lot of formatting options (like
394             disabling hash key sorting, setting verboseness/indent level, and so on) but you
395             usually have to configure it quite a bit before it does exactly like you want
396             (that's why there are modules on CPAN that are just wrapping Data::Dumper with
397             some configuration, like L<Data::Dumper::Concise> et al). It does not support
398             dumping Perl code that can recreate circular structures.
399              
400             Of course, dumping to eval-able Perl code is slow (not to mention the cost of
401             re-loading the code back to in-memory data, via eval-ing) compared to dumping to
402             JSON, YAML, Sereal, or other format. So you need to decide first whether this is
403             the appropriate route you want to take. (But note that there is also
404             L<Data::Dumper::Limited> and L<Data::Undump> which uses a format similar to
405             Data::Dumper but lets you load the serialized data without eval-ing them, thus
406             achieving the speed comparable to JSON::XS).
407              
408             =head2 Is the output guaranteed to be single line dump?
409              
410             No. Some things can still produce multiline dump, e.g. newline in regular
411             expression.
412              
413             =head1 HOMEPAGE
414              
415             Please visit the project's homepage at L<https://metacpan.org/release/Data-Dmp>.
416              
417             =head1 SOURCE
418              
419             Source repository is at L<https://github.com/perlancar/perl-Data-Dmp>.
420              
421             =head1 SEE ALSO
422              
423             L<Data::Dump> and other variations/derivate works in Data::Dump::*.
424              
425             L<Data::Dumper> and its variants.
426              
427             L<Data::Printer>.
428              
429             L<YAML>, L<JSON>, L<Storable>, L<Sereal>, and other serialization formats.
430              
431             =head1 AUTHOR
432              
433             perlancar <perlancar@cpan.org>
434              
435             =head1 CONTRIBUTING
436              
437              
438             To contribute, you can send patches by email/via RT, or send pull requests on
439             GitHub.
440              
441             Most of the time, you don't need to build the distribution yourself. You can
442             simply modify the code, then test via:
443              
444             % prove -l
445              
446             If you want to build the distribution (e.g. to try to install it locally on your
447             system), you can install L<Dist::Zilla>,
448             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
449             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
450             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
451             that are considered a bug and can be reported to me.
452              
453             =head1 COPYRIGHT AND LICENSE
454              
455             This software is copyright (c) 2022, 2021, 2020, 2017, 2016, 2015, 2014 by perlancar <perlancar@cpan.org>.
456              
457             This is free software; you can redistribute it and/or modify it under
458             the same terms as the Perl 5 programming language system itself.
459              
460             =head1 BUGS
461              
462             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Dmp>
463              
464             When submitting a bug or request, please include a test-file or a
465             patch to an existing test-file that illustrates the bug or desired
466             feature.
467              
468             =cut