python-pickle 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog.md +17 -0
- data/README.md +4 -1
- data/lib/python/pickle/deserializer.rb +142 -80
- data/lib/python/pickle/instructions/bin_persid.rb +31 -0
- data/lib/python/pickle/instructions/global.rb +11 -41
- data/lib/python/pickle/instructions/has_namespace_and_name.rb +61 -0
- data/lib/python/pickle/instructions/inst.rb +34 -0
- data/lib/python/pickle/instructions/next_buffer.rb +5 -1
- data/lib/python/pickle/instructions/obj.rb +30 -0
- data/lib/python/pickle/instructions/persid.rb +31 -0
- data/lib/python/pickle/instructions/readonly_buffer.rb +4 -0
- data/lib/python/pickle/instructions.rb +64 -0
- data/lib/python/pickle/protocol0.rb +313 -68
- data/lib/python/pickle/protocol1.rb +225 -93
- data/lib/python/pickle/protocol2.rb +205 -124
- data/lib/python/pickle/protocol3.rb +92 -123
- data/lib/python/pickle/protocol4.rb +188 -165
- data/lib/python/pickle/protocol5.rb +98 -166
- data/lib/python/pickle/version.rb +1 -1
- data/lib/python/pickle.rb +71 -39
- data/spec/deserializer_spec.rb +359 -0
- data/spec/fixtures/set_v0.pkl +11 -0
- data/spec/fixtures/set_v1.pkl +0 -0
- data/spec/fixtures/set_v2.pkl +0 -0
- data/spec/fixtures/set_v3.pkl +0 -0
- data/spec/fixtures/set_v4.pkl +0 -0
- data/spec/fixtures/set_v5.pkl +0 -0
- data/spec/generate_pickles2.py +1 -0
- data/spec/generate_pickles3.py +1 -0
- data/spec/integration/load/protocol0_spec.rb +10 -0
- data/spec/integration/load/protocol1_spec.rb +10 -0
- data/spec/integration/load/protocol2_spec.rb +10 -0
- data/spec/integration/load/protocol3_spec.rb +10 -0
- data/spec/integration/load/protocol4_spec.rb +10 -0
- data/spec/integration/load/protocol5_spec.rb +10 -0
- data/spec/pickle_spec.rb +61 -0
- data/spec/protocol0_read_instruction_examples.rb +44 -0
- metadata +14 -2
data/spec/deserializer_spec.rb
CHANGED
@@ -2,6 +2,11 @@ require 'spec_helper'
|
|
2
2
|
require 'python/pickle/deserializer'
|
3
3
|
|
4
4
|
describe Python::Pickle::Deserializer do
|
5
|
+
module TestDeserializer
|
6
|
+
class MyClass
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
5
10
|
describe "#initialize" do
|
6
11
|
it "must initialize #meta_stack to an empty Array" do
|
7
12
|
expect(subject.meta_stack).to eq([])
|
@@ -26,6 +31,10 @@ describe Python::Pickle::Deserializer do
|
|
26
31
|
expect(subject.constants['__builtin__']['object']).to be(described_class::OBJECT_CLASS)
|
27
32
|
end
|
28
33
|
|
34
|
+
it "must contain '__builtin__.set' for Python 2.x support" do
|
35
|
+
expect(subject.constants['__builtin__']['set']).to be(Set)
|
36
|
+
end
|
37
|
+
|
29
38
|
it "must contain '__builtin__.bytearray' for Python 2.x support" do
|
30
39
|
expect(subject.constants['__builtin__']['bytearray']).to be(Python::Pickle::ByteArray)
|
31
40
|
end
|
@@ -34,6 +43,10 @@ describe Python::Pickle::Deserializer do
|
|
34
43
|
expect(subject.constants['builtins']['object']).to be(described_class::OBJECT_CLASS)
|
35
44
|
end
|
36
45
|
|
46
|
+
it "must contain 'builtins.set' for Python 2.x support" do
|
47
|
+
expect(subject.constants['builtins']['set']).to be(Set)
|
48
|
+
end
|
49
|
+
|
37
50
|
it "must contain 'builtins.bytearray' for Python 2.x support" do
|
38
51
|
expect(subject.constants['builtins']['bytearray']).to be(Python::Pickle::ByteArray)
|
39
52
|
end
|
@@ -44,9 +57,75 @@ describe Python::Pickle::Deserializer do
|
|
44
57
|
end
|
45
58
|
|
46
59
|
context "when initialized with the `extensions:` keyword argument" do
|
60
|
+
let(:extensions) do
|
61
|
+
{
|
62
|
+
0x41 => Object.new,
|
63
|
+
0x42 => Object.new,
|
64
|
+
0x43 => Object.new
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
subject { described_class.new(extensions: extensions) }
|
69
|
+
|
70
|
+
it "must add the extensions: values to #extensions" do
|
71
|
+
expect(subject.extensions).to eq(extensions)
|
72
|
+
end
|
47
73
|
end
|
48
74
|
|
49
75
|
context "when initialized with the `constants:` keyword argument" do
|
76
|
+
let(:constants) do
|
77
|
+
{
|
78
|
+
'__main__' => {
|
79
|
+
'MyClass' => TestDeserializer::MyClass
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
subject { described_class.new(constants: constants) }
|
85
|
+
|
86
|
+
it "must merge the constants: keyword argument with the default constants" do
|
87
|
+
expect(subject.constants).to eq(
|
88
|
+
{
|
89
|
+
'copy_reg' => {
|
90
|
+
'_reconstructor' => subject.method(:copyreg_reconstructor)
|
91
|
+
},
|
92
|
+
|
93
|
+
'__builtin__' => {
|
94
|
+
'object' => described_class::OBJECT_CLASS,
|
95
|
+
'set' => Set,
|
96
|
+
'bytearray' => Python::Pickle::ByteArray
|
97
|
+
},
|
98
|
+
|
99
|
+
'builtins' => {
|
100
|
+
'object' => described_class::OBJECT_CLASS,
|
101
|
+
'set' => Set,
|
102
|
+
'bytearray' => Python::Pickle::ByteArray
|
103
|
+
},
|
104
|
+
|
105
|
+
'__main__' => {
|
106
|
+
'MyClass' => TestDeserializer::MyClass
|
107
|
+
}
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when initialized with the `buffers:` keyword argument" do
|
114
|
+
let(:buffer1) { "hello world" }
|
115
|
+
let(:buffer2) { "foo bar" }
|
116
|
+
let(:buffers) do
|
117
|
+
[
|
118
|
+
buffer1,
|
119
|
+
buffer2
|
120
|
+
]
|
121
|
+
end
|
122
|
+
|
123
|
+
subject { described_class.new(buffers: buffers) }
|
124
|
+
|
125
|
+
it "must set #buffers to an Enumerator of the buffers" do
|
126
|
+
expect(subject.buffers).to be_kind_of(Enumerator)
|
127
|
+
expect(subject.buffers.to_a).to eq(buffers)
|
128
|
+
end
|
50
129
|
end
|
51
130
|
end
|
52
131
|
|
@@ -364,6 +443,37 @@ describe Python::Pickle::Deserializer do
|
|
364
443
|
end
|
365
444
|
end
|
366
445
|
|
446
|
+
context "when given a Python::Pickle::Instructions::EMPTY_SET" do
|
447
|
+
let(:instruction) { Python::Pickle::Instructions::EMPTY_SET }
|
448
|
+
|
449
|
+
before do
|
450
|
+
subject.execute(instruction)
|
451
|
+
end
|
452
|
+
|
453
|
+
it "must push an empty Set object onto the #stack" do
|
454
|
+
expect(subject.stack).to eq([ Set.new ])
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
context "when given a Python::Pickle::Instructions::FROZENSET" do
|
459
|
+
let(:instruction) { Python::Pickle::Instructions::FROZENSET }
|
460
|
+
|
461
|
+
before do
|
462
|
+
subject.meta_stack << []
|
463
|
+
subject.stack << 1 << 2 << 3
|
464
|
+
subject.execute(instruction)
|
465
|
+
end
|
466
|
+
|
467
|
+
it "must pop the #meta_stack and create a frozen Set from the previous #stack and push the frozen Set onto the new #stack" do
|
468
|
+
expect(subject.stack.length).to eq(1)
|
469
|
+
|
470
|
+
set = subject.stack[-1]
|
471
|
+
|
472
|
+
expect(set).to be_frozen
|
473
|
+
expect(set).to eq(Set[1,2,3])
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
367
477
|
context "when given a Python::Pickle::Instructions::APPEND" do
|
368
478
|
context "and when the previous element on the stack is an Array" do
|
369
479
|
let(:instruction) { Python::Pickle::Instructions::APPEND }
|
@@ -378,6 +488,19 @@ describe Python::Pickle::Deserializer do
|
|
378
488
|
end
|
379
489
|
end
|
380
490
|
|
491
|
+
context "and when the previous element on the stack is a Set" do
|
492
|
+
let(:instruction) { Python::Pickle::Instructions::APPEND }
|
493
|
+
|
494
|
+
before do
|
495
|
+
subject.stack << Set.new << 2
|
496
|
+
subject.execute(instruction)
|
497
|
+
end
|
498
|
+
|
499
|
+
it "must pop the last element from the #stack and push it onto the next list element" do
|
500
|
+
expect(subject.stack).to eq([ Set[2] ])
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
381
504
|
context "but when the previous element on the stack is not an Array" do
|
382
505
|
let(:instruction) { Python::Pickle::Instructions::APPEND }
|
383
506
|
let(:item) { 2 }
|
@@ -410,6 +533,20 @@ describe Python::Pickle::Deserializer do
|
|
410
533
|
end
|
411
534
|
end
|
412
535
|
|
536
|
+
context "and when the previous element on the stack is a Set" do
|
537
|
+
let(:instruction) { Python::Pickle::Instructions::APPENDS }
|
538
|
+
|
539
|
+
before do
|
540
|
+
subject.meta_stack << [ Set[1,2,3] ]
|
541
|
+
subject.stack << 4 << 5 << 6
|
542
|
+
subject.execute(instruction)
|
543
|
+
end
|
544
|
+
|
545
|
+
it "must pop the #meta_stack, store the #stack, and concat the previous #stack onto the last element of the new #stack" do
|
546
|
+
expect(subject.stack).to eq([ Set[1,2,3,4,5,6] ])
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
413
550
|
context "but when the previous element on the stack is not an Array" do
|
414
551
|
let(:instruction) { Python::Pickle::Instructions::APPENDS }
|
415
552
|
let(:items) { [3,4,5] }
|
@@ -428,6 +565,39 @@ describe Python::Pickle::Deserializer do
|
|
428
565
|
end
|
429
566
|
end
|
430
567
|
|
568
|
+
context "when given a Python::Pickle::Instructions::ADDITEMS" do
|
569
|
+
context "and when the previous element on the stack is a Set" do
|
570
|
+
let(:instruction) { Python::Pickle::Instructions::ADDITEMS }
|
571
|
+
|
572
|
+
before do
|
573
|
+
subject.meta_stack << [ Set[1,2,3] ]
|
574
|
+
subject.stack << 4 << 5 << 6
|
575
|
+
subject.execute(instruction)
|
576
|
+
end
|
577
|
+
|
578
|
+
it "must pop the #meta_stack, store the #stack, and concat the previous #stack onto the last element of the new #stack" do
|
579
|
+
expect(subject.stack).to eq([ Set[1,2,3,4,5,6] ])
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
context "but when the previous element on the stack is not a Set" do
|
584
|
+
let(:instruction) { Python::Pickle::Instructions::ADDITEMS }
|
585
|
+
let(:items) { [3,4,5] }
|
586
|
+
let(:set) { [] }
|
587
|
+
|
588
|
+
before do
|
589
|
+
subject.meta_stack << [ set ]
|
590
|
+
subject.stack << items[0] << items[1] << items[2]
|
591
|
+
end
|
592
|
+
|
593
|
+
it do
|
594
|
+
expect {
|
595
|
+
subject.execute(instruction)
|
596
|
+
}.to raise_error(Python::Pickle::DeserializationError,"cannot add items #{items.inspect} to a non-Set object: #{set.inspect}")
|
597
|
+
end
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
431
601
|
context "when given a Python::Pickle::Instructions::LIST" do
|
432
602
|
let(:instruction) { Python::Pickle::Instructions::LIST }
|
433
603
|
|
@@ -578,6 +748,142 @@ describe Python::Pickle::Deserializer do
|
|
578
748
|
end
|
579
749
|
end
|
580
750
|
|
751
|
+
context "when given a Python::Pickle::Instructions::Inst object" do
|
752
|
+
let(:namespace) { '__main__' }
|
753
|
+
let(:name) { 'MyClass' }
|
754
|
+
let(:instruction) { Python::Pickle::Instructions::Inst.new(namespace,name) }
|
755
|
+
|
756
|
+
before do
|
757
|
+
subject.meta_stack << []
|
758
|
+
subject.stack << 1 << 2
|
759
|
+
subject.execute(instruction)
|
760
|
+
end
|
761
|
+
|
762
|
+
context "when the constant can be resolved" do
|
763
|
+
module TestInstInstruction
|
764
|
+
class MyClass
|
765
|
+
attr_reader :x, :y
|
766
|
+
|
767
|
+
def initialize(x,y)
|
768
|
+
@x = x
|
769
|
+
@y = y
|
770
|
+
end
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
subject do
|
775
|
+
described_class.new(
|
776
|
+
constants: {
|
777
|
+
'__main__' => {
|
778
|
+
'MyClass' => TestInstInstruction::MyClass
|
779
|
+
}
|
780
|
+
}
|
781
|
+
)
|
782
|
+
end
|
783
|
+
|
784
|
+
it "must resolve the class from the INST instruction's #namespace and #name, pop the meta stack, use the previous sstack as the initialization arguments, initialize a new instance of the class, and push it onto the new #stack" do
|
785
|
+
expect(subject.stack.length).to eq(1)
|
786
|
+
|
787
|
+
object = subject.stack[-1]
|
788
|
+
|
789
|
+
expect(object).to be_kind_of(TestInstInstruction::MyClass)
|
790
|
+
expect(object.x).to eq(1)
|
791
|
+
expect(object.y).to eq(2)
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
context "but the constant cannot be resolved" do
|
796
|
+
it "must push a new Python::Pickle::PyClass object onto the #stack" do
|
797
|
+
py_object = subject.stack[-1]
|
798
|
+
|
799
|
+
expect(py_object).to be_kind_of(Python::Pickle::PyObject)
|
800
|
+
expect(py_object.py_class).to be_kind_of(Python::Pickle::PyClass)
|
801
|
+
expect(py_object.py_class.namespace).to eq(namespace)
|
802
|
+
expect(py_object.py_class.name).to eq(name)
|
803
|
+
expect(py_object.init_args).to eq([1,2])
|
804
|
+
end
|
805
|
+
end
|
806
|
+
end
|
807
|
+
|
808
|
+
context "when given a Python::Pickle::Instructions::OBJ" do
|
809
|
+
let(:instruction) { Python::Pickle::Instructions::OBJ }
|
810
|
+
|
811
|
+
context "and when the constant on the #stack is a Ruby class" do
|
812
|
+
module TestObjInstruction
|
813
|
+
class MyClass
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
context "but there are no additional arguments on the #stack after the class" do
|
818
|
+
before do
|
819
|
+
subject.stack << TestObjInstruction::MyClass
|
820
|
+
subject.execute(instruction)
|
821
|
+
end
|
822
|
+
|
823
|
+
it "must pop off first element, and initialize a new instance of the class, and push the new instance onto the #stack" do
|
824
|
+
expect(subject.stack.length).to eq(1)
|
825
|
+
expect(subject.stack[-1]).to be_kind_of(TestObjInstruction::MyClass)
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
context "but there are additional arguments on the #stack after the class" do
|
830
|
+
module TestObjInstruction
|
831
|
+
class MyClassWithArgs
|
832
|
+
attr_reader :x, :y
|
833
|
+
|
834
|
+
def initialize(x,y)
|
835
|
+
@x = x
|
836
|
+
@y = y
|
837
|
+
end
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
before do
|
842
|
+
subject.stack << TestObjInstruction::MyClassWithArgs << 1 << 2
|
843
|
+
subject.execute(instruction)
|
844
|
+
end
|
845
|
+
|
846
|
+
it "must call #initialize with the splatted tuple's arguments" do
|
847
|
+
object = subject.stack[-1]
|
848
|
+
|
849
|
+
expect(object.x).to eq(1)
|
850
|
+
expect(object.y).to eq(2)
|
851
|
+
end
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
context "and when the constant on the #stack is a PyClass" do
|
856
|
+
let(:namespace) { '__main__' }
|
857
|
+
let(:name) { 'MyClass' }
|
858
|
+
let(:py_class) { Python::Pickle::PyClass.new(namespace,name) }
|
859
|
+
|
860
|
+
context "but there are no additional arguments on the #stack after the class" do
|
861
|
+
before do
|
862
|
+
subject.stack << py_class
|
863
|
+
subject.execute(instruction)
|
864
|
+
end
|
865
|
+
|
866
|
+
it "must pop off the two last elements and push the new Python::Pickle::PyObject onto the #stack" do
|
867
|
+
expect(subject.stack.length).to eq(1)
|
868
|
+
expect(subject.stack[-1]).to be_kind_of(Python::Pickle::PyObject)
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
context "but there are additional arguments on the #stack after the class" do
|
873
|
+
before do
|
874
|
+
subject.stack << py_class << 1 << 2
|
875
|
+
subject.execute(instruction)
|
876
|
+
end
|
877
|
+
|
878
|
+
it "must set the object's #init_args to the tuple's elements" do
|
879
|
+
object = subject.stack[-1]
|
880
|
+
|
881
|
+
expect(object.init_args).to eq([1,2])
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|
581
887
|
context "when given a Python::Pickle::Instructions::NEWOBJ" do
|
582
888
|
let(:instruction) { Python::Pickle::Instructions::NEWOBJ }
|
583
889
|
|
@@ -1150,6 +1456,59 @@ describe Python::Pickle::Deserializer do
|
|
1150
1456
|
end
|
1151
1457
|
end
|
1152
1458
|
end
|
1459
|
+
|
1460
|
+
context "when given a Python::Pickle::Instructions::NEXT_BUFFER" do
|
1461
|
+
let(:instruction) { Python::Pickle::Instructions::NEXT_BUFFER }
|
1462
|
+
|
1463
|
+
context "and the #{described_class} was initialized with the buffers: keyword argument" do
|
1464
|
+
let(:buffer1) { String.new("hello world") }
|
1465
|
+
let(:buffer2) { String.new("foo bar") }
|
1466
|
+
let(:buffers) do
|
1467
|
+
[
|
1468
|
+
buffer1,
|
1469
|
+
buffer2
|
1470
|
+
]
|
1471
|
+
end
|
1472
|
+
|
1473
|
+
subject { described_class.new(buffers: buffers) }
|
1474
|
+
|
1475
|
+
before do
|
1476
|
+
subject.execute(instruction)
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
it "must take the next element from #buffers and push it onto the #stack" do
|
1480
|
+
expect(subject.stack).to eq([buffer1])
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
it "must not modify the underlying buffers Array" do
|
1484
|
+
expect(buffers).to eq([buffer1, buffer2])
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
context "but the #{described_class} was not initialized with the buffers: keyword argument" do
|
1489
|
+
it do
|
1490
|
+
expect {
|
1491
|
+
subject.execute(instruction)
|
1492
|
+
}.to raise_error(Python::Pickle::DeserializationError,"pickle stream includes a NEXT_BUFFER instruction, but no buffers were provided")
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
context "when given a Python::Pickle::Instructions::READONLY_BUFFER" do
|
1498
|
+
let(:instruction) { Python::Pickle::Instructions::READONLY_BUFFER }
|
1499
|
+
|
1500
|
+
let(:buffer1) { String.new("hello world") }
|
1501
|
+
let(:buffer2) { String.new("foo bar") }
|
1502
|
+
|
1503
|
+
before do
|
1504
|
+
subject.stack << buffer1 << buffer2
|
1505
|
+
subject.execute(instruction)
|
1506
|
+
end
|
1507
|
+
|
1508
|
+
it "must freeze the buffer at the top of the #stack" do
|
1509
|
+
expect(subject.stack[-1]).to be_frozen
|
1510
|
+
end
|
1511
|
+
end
|
1153
1512
|
end
|
1154
1513
|
|
1155
1514
|
describe "#copyreg_reconstructor" do
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/generate_pickles2.py
CHANGED
@@ -26,6 +26,7 @@ objects = {
|
|
26
26
|
"bin_str": b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
27
27
|
"list": [None, True, False, 42, 'ABC'],
|
28
28
|
"nested_list": [1, [2, [3, [4]]]],
|
29
|
+
"set": set([1,2,3,4]),
|
29
30
|
"dict": {"foo": "bar"},
|
30
31
|
"nested_dict": {"a": {"b": {"c": "d"}}},
|
31
32
|
"function": func,
|
data/spec/generate_pickles3.py
CHANGED
@@ -25,6 +25,7 @@ objects = {
|
|
25
25
|
"bin_str": b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
|
26
26
|
"list": [None, True, False, 42, 'ABC'],
|
27
27
|
"nested_list": [1, [2, [3, [4]]]],
|
28
|
+
"set": set([1,2,3,4]),
|
28
29
|
"dict": {"foo": "bar"},
|
29
30
|
"nested_dict": {"a": {"b": {"c": "d"}}},
|
30
31
|
"function": func,
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v0.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v0.pkl') }
|
109
119
|
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v1.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v1.pkl') }
|
109
119
|
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v2.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v2.pkl') }
|
109
119
|
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v3.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v3.pkl') }
|
109
119
|
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v4.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v4.pkl') }
|
109
119
|
|
@@ -104,6 +104,16 @@ describe Python::Pickle do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
+
context "and it contains a set type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'set_v5.pkl') }
|
109
|
+
|
110
|
+
it "must return a Set object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
Set[1, 2, 3, 4]
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
context "and it contains a dict type" do
|
108
118
|
let(:file) { File.join(fixtures_dir,'dict_v5.pkl') }
|
109
119
|
|
data/spec/pickle_spec.rb
CHANGED
@@ -52,6 +52,33 @@ describe Python::Pickle do
|
|
52
52
|
it "must deserialize the Python Pickle data in the given file" do
|
53
53
|
expect(subject.load(data)).to eq({"foo" => "bar"})
|
54
54
|
end
|
55
|
+
|
56
|
+
context "when the constants: keyword argument is given" do
|
57
|
+
let(:path) { File.join(fixtures_dir,'object_v4.pkl') }
|
58
|
+
|
59
|
+
module TestLoad
|
60
|
+
class MyClass
|
61
|
+
attr_reader :x, :y
|
62
|
+
|
63
|
+
def __setstate__(attributes)
|
64
|
+
@x = attributes['x']
|
65
|
+
@y = attributes['y']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
let(:constants) do
|
71
|
+
{
|
72
|
+
'__main__' => {
|
73
|
+
'MyClass' => TestLoad::MyClass
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "must map the Python classes to the Ruby classes" do
|
79
|
+
expect(subject.load(data, constants: constants)).to be_kind_of(TestLoad::MyClass)
|
80
|
+
end
|
81
|
+
end
|
55
82
|
end
|
56
83
|
|
57
84
|
describe ".load_file" do
|
@@ -60,9 +87,43 @@ describe Python::Pickle do
|
|
60
87
|
it "must deserialize the Python Pickle data in the given file" do
|
61
88
|
expect(subject.load_file(path)).to eq({"foo" => "bar"})
|
62
89
|
end
|
90
|
+
|
91
|
+
context "when the constants: keyword argument is given" do
|
92
|
+
let(:path) { File.join(fixtures_dir,'object_v4.pkl') }
|
93
|
+
|
94
|
+
module TestLoad
|
95
|
+
class MyClass
|
96
|
+
attr_reader :x, :y
|
97
|
+
|
98
|
+
def __setstate__(attributes)
|
99
|
+
@x = attributes['x']
|
100
|
+
@y = attributes['y']
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
let(:constants) do
|
106
|
+
{
|
107
|
+
'__main__' => {
|
108
|
+
'MyClass' => TestLoad::MyClass
|
109
|
+
}
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
it "must map the Python classes to the Ruby classes" do
|
114
|
+
expect(subject.load_file(path, constants: constants)).to be_kind_of(TestLoad::MyClass)
|
115
|
+
end
|
116
|
+
end
|
63
117
|
end
|
64
118
|
|
65
119
|
describe ".dump" do
|
120
|
+
let(:object) { Object.new }
|
121
|
+
|
122
|
+
it do
|
123
|
+
expect {
|
124
|
+
subject.dump(object)
|
125
|
+
}.to raise_error(NotImplementedError,"pickle serializing is currently not supported")
|
126
|
+
end
|
66
127
|
end
|
67
128
|
|
68
129
|
describe ".infer_protocol_version" do
|