python-pickle 0.1.1 → 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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +12 -0
  3. data/README.md +2 -1
  4. data/lib/python/pickle/deserializer.rb +142 -80
  5. data/lib/python/pickle/instructions/bin_persid.rb +31 -0
  6. data/lib/python/pickle/instructions/global.rb +11 -41
  7. data/lib/python/pickle/instructions/has_namespace_and_name.rb +61 -0
  8. data/lib/python/pickle/instructions/inst.rb +34 -0
  9. data/lib/python/pickle/instructions/next_buffer.rb +5 -1
  10. data/lib/python/pickle/instructions/obj.rb +30 -0
  11. data/lib/python/pickle/instructions/persid.rb +31 -0
  12. data/lib/python/pickle/instructions/readonly_buffer.rb +4 -0
  13. data/lib/python/pickle/instructions.rb +64 -0
  14. data/lib/python/pickle/protocol0.rb +313 -68
  15. data/lib/python/pickle/protocol1.rb +225 -93
  16. data/lib/python/pickle/protocol2.rb +205 -124
  17. data/lib/python/pickle/protocol3.rb +92 -123
  18. data/lib/python/pickle/protocol4.rb +188 -165
  19. data/lib/python/pickle/protocol5.rb +98 -166
  20. data/lib/python/pickle/version.rb +1 -1
  21. data/lib/python/pickle.rb +38 -32
  22. data/spec/deserializer_spec.rb +308 -0
  23. data/spec/fixtures/set_v0.pkl +11 -0
  24. data/spec/fixtures/set_v1.pkl +0 -0
  25. data/spec/fixtures/set_v2.pkl +0 -0
  26. data/spec/fixtures/set_v3.pkl +0 -0
  27. data/spec/fixtures/set_v4.pkl +0 -0
  28. data/spec/fixtures/set_v5.pkl +0 -0
  29. data/spec/generate_pickles2.py +1 -0
  30. data/spec/generate_pickles3.py +1 -0
  31. data/spec/integration/load/protocol0_spec.rb +10 -0
  32. data/spec/integration/load/protocol1_spec.rb +10 -0
  33. data/spec/integration/load/protocol2_spec.rb +10 -0
  34. data/spec/integration/load/protocol3_spec.rb +10 -0
  35. data/spec/integration/load/protocol4_spec.rb +10 -0
  36. data/spec/integration/load/protocol5_spec.rb +10 -0
  37. data/spec/protocol0_read_instruction_examples.rb +44 -0
  38. metadata +14 -2
@@ -31,6 +31,10 @@ describe Python::Pickle::Deserializer do
31
31
  expect(subject.constants['__builtin__']['object']).to be(described_class::OBJECT_CLASS)
32
32
  end
33
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
+
34
38
  it "must contain '__builtin__.bytearray' for Python 2.x support" do
35
39
  expect(subject.constants['__builtin__']['bytearray']).to be(Python::Pickle::ByteArray)
36
40
  end
@@ -39,6 +43,10 @@ describe Python::Pickle::Deserializer do
39
43
  expect(subject.constants['builtins']['object']).to be(described_class::OBJECT_CLASS)
40
44
  end
41
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
+
42
50
  it "must contain 'builtins.bytearray' for Python 2.x support" do
43
51
  expect(subject.constants['builtins']['bytearray']).to be(Python::Pickle::ByteArray)
44
52
  end
@@ -84,11 +92,13 @@ describe Python::Pickle::Deserializer do
84
92
 
85
93
  '__builtin__' => {
86
94
  'object' => described_class::OBJECT_CLASS,
95
+ 'set' => Set,
87
96
  'bytearray' => Python::Pickle::ByteArray
88
97
  },
89
98
 
90
99
  'builtins' => {
91
100
  'object' => described_class::OBJECT_CLASS,
101
+ 'set' => Set,
92
102
  'bytearray' => Python::Pickle::ByteArray
93
103
  },
94
104
 
@@ -99,6 +109,24 @@ describe Python::Pickle::Deserializer do
99
109
  )
100
110
  end
101
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
129
+ end
102
130
  end
103
131
 
104
132
  describe "#push_meta_stack" do
@@ -415,6 +443,37 @@ describe Python::Pickle::Deserializer do
415
443
  end
416
444
  end
417
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
+
418
477
  context "when given a Python::Pickle::Instructions::APPEND" do
419
478
  context "and when the previous element on the stack is an Array" do
420
479
  let(:instruction) { Python::Pickle::Instructions::APPEND }
@@ -429,6 +488,19 @@ describe Python::Pickle::Deserializer do
429
488
  end
430
489
  end
431
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
+
432
504
  context "but when the previous element on the stack is not an Array" do
433
505
  let(:instruction) { Python::Pickle::Instructions::APPEND }
434
506
  let(:item) { 2 }
@@ -461,6 +533,20 @@ describe Python::Pickle::Deserializer do
461
533
  end
462
534
  end
463
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
+
464
550
  context "but when the previous element on the stack is not an Array" do
465
551
  let(:instruction) { Python::Pickle::Instructions::APPENDS }
466
552
  let(:items) { [3,4,5] }
@@ -479,6 +565,39 @@ describe Python::Pickle::Deserializer do
479
565
  end
480
566
  end
481
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
+
482
601
  context "when given a Python::Pickle::Instructions::LIST" do
483
602
  let(:instruction) { Python::Pickle::Instructions::LIST }
484
603
 
@@ -629,6 +748,142 @@ describe Python::Pickle::Deserializer do
629
748
  end
630
749
  end
631
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
+
632
887
  context "when given a Python::Pickle::Instructions::NEWOBJ" do
633
888
  let(:instruction) { Python::Pickle::Instructions::NEWOBJ }
634
889
 
@@ -1201,6 +1456,59 @@ describe Python::Pickle::Deserializer do
1201
1456
  end
1202
1457
  end
1203
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
1204
1512
  end
1205
1513
 
1206
1514
  describe "#copyreg_reconstructor" do
@@ -0,0 +1,11 @@
1
+ c__builtin__
2
+ set
3
+ p0
4
+ ((lp1
5
+ I1
6
+ aI2
7
+ aI3
8
+ aI4
9
+ atp2
10
+ Rp3
11
+ .
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -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,
@@ -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
 
@@ -93,6 +93,27 @@ shared_examples_for "Protocol0#read_instruction examples" do
93
93
  end
94
94
  end
95
95
 
96
+ context "when the opcode is 80" do
97
+ let(:id) { "12345" }
98
+ let(:io) { StringIO.new("#{80.chr}#{id}\n") }
99
+
100
+ it "must return a Python::Pickle::Instructions::PersID object" do
101
+ expect(subject.read_instruction).to eq(
102
+ Python::Pickle::Instructions::PersID.new(id)
103
+ )
104
+ end
105
+ end
106
+
107
+ context "when the opcode is 81" do
108
+ let(:io) { StringIO.new(81.chr) }
109
+
110
+ it "must return Python::Pickle::Instructions::BINPERSID" do
111
+ expect(subject.read_instruction).to be(
112
+ Python::Pickle::Instructions::BINPERSID
113
+ )
114
+ end
115
+ end
116
+
96
117
  context "when the opcode is 82" do
97
118
  let(:io) { StringIO.new(82.chr) }
98
119
 
@@ -168,6 +189,19 @@ shared_examples_for "Protocol0#read_instruction examples" do
168
189
  end
169
190
  end
170
191
 
192
+ context "when the opcode is 105" do
193
+ let(:namespace) { "foo" }
194
+ let(:name) { "bar" }
195
+
196
+ let(:io) { StringIO.new("#{105.chr}#{namespace}\n#{name}\n") }
197
+
198
+ it "must return Python::Pickle::Instructions::Inst" do
199
+ expect(subject.read_instruction).to eq(
200
+ Python::Pickle::Instructions::Inst.new(namespace,name)
201
+ )
202
+ end
203
+ end
204
+
171
205
  context "when the opcode is 108" do
172
206
  let(:io) { StringIO.new(108.chr) }
173
207
 
@@ -178,6 +212,16 @@ shared_examples_for "Protocol0#read_instruction examples" do
178
212
  end
179
213
  end
180
214
 
215
+ context "when the opcode is 111" do
216
+ let(:io) { StringIO.new(111.chr) }
217
+
218
+ it "must return Python::Pickle::Instructions::OBJ" do
219
+ expect(subject.read_instruction).to be(
220
+ Python::Pickle::Instructions::OBJ
221
+ )
222
+ end
223
+ end
224
+
181
225
  context "when the opcode is 112" do
182
226
  let(:index) { 1 }
183
227
  let(:io) { StringIO.new("#{112.chr}#{index}\n") }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: python-pickle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-18 00:00:00.000000000 Z
11
+ date: 2023-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,6 +53,7 @@ files:
53
53
  - lib/python/pickle/deserializer.rb
54
54
  - lib/python/pickle/exceptions.rb
55
55
  - lib/python/pickle/instruction.rb
56
+ - lib/python/pickle/instructions.rb
56
57
  - lib/python/pickle/instructions/add_items.rb
57
58
  - lib/python/pickle/instructions/append.rb
58
59
  - lib/python/pickle/instructions/appends.rb
@@ -61,6 +62,7 @@ files:
61
62
  - lib/python/pickle/instructions/bin_float.rb
62
63
  - lib/python/pickle/instructions/bin_get.rb
63
64
  - lib/python/pickle/instructions/bin_int1.rb
65
+ - lib/python/pickle/instructions/bin_persid.rb
64
66
  - lib/python/pickle/instructions/bin_put.rb
65
67
  - lib/python/pickle/instructions/bin_string.rb
66
68
  - lib/python/pickle/instructions/bin_unicode.rb
@@ -82,7 +84,9 @@ files:
82
84
  - lib/python/pickle/instructions/get.rb
83
85
  - lib/python/pickle/instructions/global.rb
84
86
  - lib/python/pickle/instructions/has_length_and_value.rb
87
+ - lib/python/pickle/instructions/has_namespace_and_name.rb
85
88
  - lib/python/pickle/instructions/has_value.rb
89
+ - lib/python/pickle/instructions/inst.rb
86
90
  - lib/python/pickle/instructions/int.rb
87
91
  - lib/python/pickle/instructions/list.rb
88
92
  - lib/python/pickle/instructions/long.rb
@@ -97,6 +101,8 @@ files:
97
101
  - lib/python/pickle/instructions/new_true.rb
98
102
  - lib/python/pickle/instructions/next_buffer.rb
99
103
  - lib/python/pickle/instructions/none.rb
104
+ - lib/python/pickle/instructions/obj.rb
105
+ - lib/python/pickle/instructions/persid.rb
100
106
  - lib/python/pickle/instructions/pop.rb
101
107
  - lib/python/pickle/instructions/pop_mark.rb
102
108
  - lib/python/pickle/instructions/proto.rb
@@ -222,6 +228,12 @@ files:
222
228
  - spec/fixtures/object_v3.pkl
223
229
  - spec/fixtures/object_v4.pkl
224
230
  - spec/fixtures/object_v5.pkl
231
+ - spec/fixtures/set_v0.pkl
232
+ - spec/fixtures/set_v1.pkl
233
+ - spec/fixtures/set_v2.pkl
234
+ - spec/fixtures/set_v3.pkl
235
+ - spec/fixtures/set_v4.pkl
236
+ - spec/fixtures/set_v5.pkl
225
237
  - spec/fixtures/str_v0.pkl
226
238
  - spec/fixtures/str_v1.pkl
227
239
  - spec/fixtures/str_v2.pkl