File Coverage

blib/lib/Python/Serialise/Pickle.pm
Criterion Covered Total %
statement 214 231 92.6
branch 54 72 75.0
condition 29 40 72.5
subroutine 26 28 92.8
pod 4 4 100.0
total 327 375 87.2


line stmt bran cond sub pod time code
1             package Python::Serialise::Pickle;
2              
3 7     7   6053 use strict;
  7         15  
  7         334  
4 7     7   5404 use Data::Dumper;
  7         32197  
  7         548  
5 7     7   6764 use IO::File;
  7         85689  
  7         1240  
6 7     7   53 use vars qw($VERSION);
  7         11  
  7         18164  
7              
8             $VERSION = '0.01';
9              
10              
11              
12             my %_set_handlers = (
13             'NUMBER' => \&_set_num,
14             'STRING' => \&_set_string,
15             'HASH' => \&_set_dict,
16             'ARRAY' => \&_set_list,
17             );
18              
19              
20             my %_get_handlers = (
21             'I' => \&_get_num,
22             'L' => \&_get_num,
23             'F' => \&_get_num,
24             'S' => \&_get_string,
25             'N' => \&_get_none,
26             'l' => \&_get_list,
27             'd' => \&_get_dict,
28             'c' => \&_get_raw,
29             'p' => \&_get_id,
30             'i' => \&_get_raw,
31             '(' => \&_get_compound,
32             );
33              
34              
35             =head1 NAME
36              
37             Python::Serialise::Pickle - a file for reading and writing pickled Python files
38              
39             =head1 SYNOPSIS
40              
41              
42             use Python::Serialise::Pickle;
43              
44             my $pr = Python::Serialise::Pickle->new("file/for/reading");
45             while (my $data = $pr->load()) {
46             print Dumper $data;
47             }
48              
49             my $pw = Python::Serialise::Pickle->new(">file/for/writing");
50            
51             $pw->dump(['a' 'list']);
52             $pw->dump("a string");
53             $pw->dump(42);
54             $pw->dump({'a'=>'hash'});
55              
56             $pw->close();
57              
58             =head1 DESCRIPTION
59              
60             Pickling is a method of serialising files in Python (another method,
61             Marshalling, is also available).
62              
63             This module is an attempt to write a pure Perl implementation of the algorithm.
64              
65              
66             =head1 METHODS
67              
68             =head2 new
69              
70             Open a file for reading or writing. Can take any arguments that C can.
71              
72             =cut
73              
74             sub new {
75 18     18 1 5699 my $class = shift;
76 18   50     71 my $file = shift || die "You must pass a file\n";
77              
78             ## FIXME error here
79 18   50     102 my $fh = IO::File->new($file) || die "Couldn't open file\n";
80 18         51362 my $self = { _fh => $fh };
81              
82 18         131 return bless $self, $class;
83            
84             }
85              
86             =head2 load
87              
88             Returns the next data structure from the pickle file or undef.
89              
90             =cut
91              
92             sub load {
93 47     47 1 85 my $self = shift;
94 47         86 $self->{_cur_id} = 0;
95              
96 47         2026 print "LOAD\n";
97              
98 47         122 my $id = $self->_get_char();
99 47 50 33     283 return undef if (!defined $id or $id eq "");
100              
101 47   50     134 my $sub = $_get_handlers{$id} || die "We have no handler to deal with '$id'\n";
102 47         107 return $self->$sub();
103              
104             }
105              
106              
107             =head2 dump
108              
109             Takes a ref to an array or a hash or a number or string and pickles it.
110              
111             Structures may be nested.
112              
113             =cut
114              
115             sub dump {
116 24     24 1 39 my $self = shift;
117 24         34 my $val = shift;
118              
119              
120              
121 24         101 my $sub = $_set_handlers{$self->_type($val)};
122            
123 24         61 my $line = $self->$sub($val);
124 24         51 $line .= ".";
125            
126 24         59 $self->_write($line);
127 24         492 return $line;
128              
129             }
130              
131              
132             sub _backup {
133 10     10   13 my $self = shift;
134 10         38 print "BACKUP\n";
135 10         54 $self->{_fh}->seek(-1,1);
136             }
137              
138              
139             sub _get_char
140             {
141 1082     1082   1680 my $self = shift;
142 1082         3668 $self->{_fh}->read(my $data, 1);
143 1082         36802 print "C=$data\n";
144              
145 1082         3174 return $data;
146             }
147              
148             sub _get_line {
149 183     183   216 my $self = shift;
150 183         215 my $line = "";
151              
152 183         177 while (1) {
153 716         1553 my $char = $self->_get_char();
154 716 50       1610 last unless defined $char;
155 716 100       1364 last if $char eq "\n";
156 533         673 $line .= $char;
157             }
158              
159 183         366 return $line;
160             }
161              
162             sub _write {
163 24     24   31 my $self = shift;
164 24         38 my $val = shift;
165              
166 24         122 $self->{_fh}->write($val);
167             }
168              
169              
170             sub _get_num
171             {
172 46     46   44 my $self = shift;
173 46         88 my %opts = @_;
174              
175 46         86 my $num = $self->_get_line();
176              
177 46 100 100     192 unless (defined $opts{'ignore_end_marker'} && $opts{'ignore_end_marker'} == 1) {
178 34         64 $self->_get_char();
179             }
180              
181              
182 46         148 return $num;
183             }
184              
185             sub _set_num
186             {
187 24     24   24 my $self = shift;
188 24         26 my $num = shift;
189 24         41 my %opts = @_;
190              
191              
192 24         21 my $return;
193 24 100       45 if (int $num != $num) {
194 2         7 $return = "F$num\n";
195             } else {
196 22         40 $return = "I$num\n";
197             }
198              
199 24 100       67 $return .= $opts{'terminator'} if ($opts{'terminator'});
200              
201 24         71 return $return;
202             }
203              
204              
205             sub _get_string
206             {
207 51     51   61 my $self = shift;
208 51         254 my %opts = @_;
209              
210              
211 51         249 my $string = $self->_get_line();
212 51         330 $string =~ s!^(['"])(.*)['"]$!$2!;
213 51         97 $string =~ s!"!\\"!g;
214 51         3931 eval "\$string = \"$string\";";
215              
216 51         380 my $id = $self->_get_id();
217 51 50       270 die "No id!\n" unless defined $id;
218              
219 51 100 100     426 unless (defined $opts{'ignore_end_marker'} && $opts{'ignore_end_marker'} == 1) {
220 38         235 $self->_get_char();
221             }
222              
223              
224 51         429 return $string;
225            
226              
227             }
228              
229             sub _set_string
230             {
231 45     45   54 my $self = shift;
232 45         59 my $string = shift;
233 45         95 my %opts = @_;
234              
235             # escape some control chars
236 45         151 $string =~ s{
237             (.)
238             }{
239 171 100 66     1033 (ord($1)<33 || ord($1)>126)?sprintf '\\%.3o',ord($1):$1
240             }sxeg;
241            
242 45         101 my $return = "S";
243 45 100       99 if ($string =~ /^'.+'$/) {
244 1         14 $return .= "\"$string\"\n";
245             } else {
246 44         76 $return .= "'$string'\n";
247             }
248            
249              
250              
251 45         92 $return .= $self->_set_id();
252 45 100       132 $return .= $opts{'terminator'} if ($opts{'terminator'});
253              
254              
255 45         139 return $return;
256              
257             }
258              
259              
260             sub _get_id {
261 86     86   105 my $self = shift;
262 86         116 my %opts = @_;
263              
264 86         158 my $char = $self->_get_char();
265 86 50       204 die "Got $char - was expecting 'p' for id" unless $char eq 'p';
266 86         166 return $self->_get_line();
267             }
268              
269             sub _set_id {
270 68     68   76 my $self = shift;
271            
272 68         110 my $id = $self->{_cur_id}++;
273 68         148 return "p$id\n";
274             }
275              
276             sub _get_list {
277 20     20   27 my $self = shift;
278 20         33 my %opts = @_;
279              
280 20         79 print "GET LIST\n";
281              
282 20         41 my $oid = $self->_get_id();
283 20         26 my @vals;
284              
285 20         20 while (1) {
286 71         135 my $id = $self->_get_char();
287 71 100 66     596 if ($id eq '.' || $id eq 's' || $id eq 'a') {
      100        
288 19         59 last;
289             }
290 52 50       101 if ($id eq 'g') {
291 0         0 my $tmp = $self->_get_line();
292 0         0 last;
293             }
294              
295 52 100       232 if ($id eq 't') {
296 1         3 $self->_backup;
297 1         9 last;
298             }
299              
300            
301 51         238 my $sub = $_get_handlers{$id};
302 51         231 print "ID=$id\n";
303 51         76 $opts{'ignore_end_marker'}=0;
304 51 50       266 die "No handler for '$id' in get_list ",(join ",",@vals),"" unless defined $sub;
305 51         122 push @vals, $self->$sub(%opts);
306            
307              
308             }
309 20         80 print "END LIST\n";
310 20         236 return \@vals;
311             }
312              
313             sub _set_list {
314 18     18   31 my $self = shift;
315 18         24 my $list = shift;
316 18         35 my %opts = @_;
317            
318 18   100     71 my $terminator = $opts{'terminator'} || "";
319              
320 18         18 my $return = "";
321 18 50       47 $return .= "(" unless ($opts{ignore_compound});
322 18         27 $return .= "l";
323 18         37 $return .= $self->_set_id();
324              
325 18         38 $opts{'terminator'} = 'a';
326              
327 18         27 foreach my $val (@$list) {
328 52         101 my $sub = $_set_handlers{$self->_type($val)};
329 52 50       105 die "No handler to set '$val'" unless defined $sub;
330 52         134 $return .= $self->$sub($val, %opts);
331              
332             }
333              
334 18         24 $return .= $terminator;
335 18         53 return $return;
336              
337             }
338              
339              
340              
341             sub _get_compound {
342 35     35   35 my $self = shift;
343 35         58 my %opts = @_;
344              
345              
346 35         74 my $id = $self->_get_char();
347            
348 35 100       95 if ($id eq 'l') {
    100          
349 20         63 $self->_get_list(%opts);
350             } elsif ($id eq 'd') {
351 6         18 $self->_get_dict(%opts);
352             } else {
353 9         19 $self->_backup();
354 9         181 $self->_get_tuple(%opts);
355             }
356             }
357              
358              
359             sub _get_tuple {
360 9     9   11 my $self = shift;
361 9         22 my %opts = @_;
362 9   50     36 my $last_id = $opts{'last_tuple_marker'} || ".";
363              
364 9         8 my @vals;
365              
366 9         46 print "GET TUPLE\n";
367              
368 9         12 while (1) {
369 39         73 my $id = $self->_get_char();
370 39 100 66     220 if ($id eq '.' || $id eq 'a' || $id eq 's') {
      100        
371 6         9 last;
372             }
373              
374 33 50       55 if ($id eq 'g') {
375 0         0 $self->_get_line();
376 0         0 next;
377             }
378            
379 33 100       57 if ($id eq 't') {
380 9         23 $self->_get_id();
381 9         33 print "Getting ID\n";
382 9 100       18 if ($opts{'ignore_end_marker'}) {
383 3         3 last;
384             } else {
385 6         12 next;
386             }
387             }
388            
389 24         28 my $sub = $_get_handlers{$id};
390 24 50       42 die "No handler for type '$id'" unless defined $sub;
391 24         46 push @vals, $self->$sub(ignore_end_marker=>1);
392            
393             }
394 9         34 print "END TUPLE\n";
395 9         56 return \@vals;
396             }
397              
398              
399             sub _get_dict
400             {
401 6     6   7 my $self = shift;
402 6         8 my %opts = @_;
403 6         8 my %dict;
404              
405 6         20 print "GET DICT\n";
406 6         25 print "IGNORE END MARKER = ",$opts{'ignore_end_marker'},"\n";
407             #$opts{'ignore_end_marker'}=1;
408              
409            
410 6         14 my $id = $self->_get_id();
411              
412              
413              
414 6         6 while (1) {
415 11         56 my $key_id = $self->_get_char();
416            
417            
418              
419              
420 11 100       23 if ($key_id eq '.') {
421 5 50       13 $self->_backup() if $opts{'ignore_end_marker'};
422 5         8 last;
423             }
424 6 100 66     27 last if ($key_id eq 's' || $key_id eq 'a');
425            
426 5 50       15 if ($key_id eq 'g') {
427 0         0 my $tmp = $self->_get_line();
428 0         0 next;
429             }
430              
431              
432 5         5 my $key_sub = $_get_handlers{$key_id};
433 5 50       10 die "No handler for key '$key_id'" unless defined $key_sub;
434            
435 5         7 my $key = $self->$key_sub( ignore_end_marker => 1);
436            
437 5         15 print "GOT KEY\n";
438            
439 5         10 my $val_id = $self->_get_char();
440 5         6 my $val_sub = $_get_handlers{$val_id};
441 5 50       9 die "No handler for value '$val_id'" unless defined $val_sub;
442 5         11 my $val = $self->$val_sub(%opts);
443              
444 5         12 $dict{$key} = $val;
445              
446              
447              
448             }
449 6         23 print "END DICT\n";
450            
451              
452 6         45 return \%dict;
453              
454             }
455              
456             sub _set_dict
457             {
458 5     5   7 my $self = shift;
459 5         7 my $hash = shift;
460 5         10 my %opts = @_;
461              
462 5         7 my $return = "";
463 5         9 $return .= "(";
464 5         7 $return .= "d";
465 5         14 $return .= $self->_set_id();
466            
467              
468 5         10 $opts{'ignore_compound'} = 0;
469 5         9 $opts{'ignore_end_marker'} = 1;
470 5         7 $opts{'terminator'} = "";
471              
472 5         8 foreach my $key (keys %{$hash}) {
  5         15  
473 8         22 my $val = $hash->{$key};
474            
475 8         15 my $keysub = $_set_handlers{$self->_type($key)};
476 8 50       17 die "No handler for setting key '$key'" unless defined $keysub;
477 8         25 $return .= $self->$keysub($key, %opts);
478              
479 8         17 my $valsub = $_set_handlers{$self->_type($val)};
480 8 50       21 die "No handler for setting val '$val'" unless defined $valsub;
481 8         22 $return .= $self->$valsub($val, %opts);
482            
483              
484 8         18 $return .= "s";
485             }
486              
487 5         23 return $return;
488              
489             }
490              
491              
492              
493              
494              
495             sub _get_none {
496 0     0   0 my $self = shift;
497 0         0 return $self->_get_raw;
498              
499             }
500              
501             sub _get_raw {
502 0     0   0 my $self = shift;
503 0         0 $self->_backup;
504              
505              
506 0         0 my $val = "";
507 0         0 while (1) {
508 0         0 my $char = $self->_get_char();
509 0 0       0 last if ($char eq ".");
510 0         0 $val .= $char;
511 0         0 $val .= $self->_get_line();
512             }
513 0         0 return $val;
514            
515             }
516              
517             sub _type {
518 92     92   134 my $self = shift;
519 92         107 my $val = shift;
520              
521 92         123 my $ref = ref $val;
522              
523 92 100 66     438 return $ref if defined $ref && $ref ne "";
524              
525 69 100       282 return "NUMBER" if ($val =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/);
526 45         103 return "STRING";
527              
528             }
529              
530              
531             =head2 close
532              
533             Closes the current file.
534              
535             =cut
536              
537             sub close {
538 23     23 1 38 my $self = shift;
539 23         119 $self->{_fh}->close();
540             }
541              
542             sub DESTROY {
543 17     17   3881 my $self = shift;
544 17         46 $self->close();
545             }
546              
547              
548             =head1 BUGS
549              
550             Almost certainly lots and lots.
551              
552             =over 4
553              
554             =item Serialised objects
555              
556             At the moment we don't deal with serialised objects very well.
557             Should probably just take or return a Python::Serialise::Pickle::Object
558             object.
559              
560             =item The 'None' object
561              
562             Similar to Perl's undef but an object. At the moment we deal with it badly
563             because if we returned undef then that would signify the end of the Pickle file.
564              
565             Should probably be returned as a special object or something.
566              
567             =item Longs
568              
569             There's no testing for longs
570              
571             =item Unicode
572              
573             Ditto
574              
575             =item Some nested dictionaries
576              
577             Dictionaries are the Python equivalent of hashes. This module can deal with most nested
578             dictionaries but, for some reason, this one :
579              
580             a={'a':['two',{'goof':'foo', 'a':[1,2,3]}]}
581              
582             causes it to fail.
583              
584             Chnaging it slightly starts it working again.
585              
586             =item Bad reading of specs
587              
588             This is entirely my fault
589              
590             =back
591              
592             =head1 ALTERNATIVES
593              
594             You could always dump the data structure out as YAML in Python
595             and then read it back in with YAML in Perl.
596              
597             =head1 AUTHOR
598              
599             Simon Wistow
600              
601             =head1 COPYRIGHT
602              
603             (c) 2003 Simon Wistow
604              
605             Distributed under the same terms as Perl itself.
606              
607             This software is under no warranty and will probably ruin your life,
608             kill your friends, burn your house and bring about the apocalypse.
609              
610             =head1 SEE ALSO
611              
612             http://www.python.org, L, L and the RESOURCES file in
613             this distribution.
614              
615             =cut
616              
617             1;