python-pickle 0.1.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 +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +14 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +149 -0
- data/Rakefile +13 -0
- data/gemspec.yml +25 -0
- data/lib/python/pickle/byte_array.rb +40 -0
- data/lib/python/pickle/deserializer.rb +595 -0
- data/lib/python/pickle/exceptions.rb +12 -0
- data/lib/python/pickle/instruction.rb +52 -0
- data/lib/python/pickle/instructions/add_items.rb +26 -0
- data/lib/python/pickle/instructions/append.rb +24 -0
- data/lib/python/pickle/instructions/appends.rb +26 -0
- data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
- data/lib/python/pickle/instructions/bin_float.rb +29 -0
- data/lib/python/pickle/instructions/bin_get.rb +27 -0
- data/lib/python/pickle/instructions/bin_int1.rb +29 -0
- data/lib/python/pickle/instructions/bin_put.rb +29 -0
- data/lib/python/pickle/instructions/bin_string.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
- data/lib/python/pickle/instructions/build.rb +24 -0
- data/lib/python/pickle/instructions/byte_array8.rb +32 -0
- data/lib/python/pickle/instructions/dict.rb +17 -0
- data/lib/python/pickle/instructions/dup.rb +24 -0
- data/lib/python/pickle/instructions/empty_dict.rb +26 -0
- data/lib/python/pickle/instructions/empty_list.rb +26 -0
- data/lib/python/pickle/instructions/empty_set.rb +26 -0
- data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
- data/lib/python/pickle/instructions/ext1.rb +29 -0
- data/lib/python/pickle/instructions/ext2.rb +29 -0
- data/lib/python/pickle/instructions/ext4.rb +29 -0
- data/lib/python/pickle/instructions/float.rb +24 -0
- data/lib/python/pickle/instructions/frame.rb +29 -0
- data/lib/python/pickle/instructions/frozen_set.rb +26 -0
- data/lib/python/pickle/instructions/get.rb +27 -0
- data/lib/python/pickle/instructions/global.rb +62 -0
- data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
- data/lib/python/pickle/instructions/has_value.rb +50 -0
- data/lib/python/pickle/instructions/int.rb +24 -0
- data/lib/python/pickle/instructions/list.rb +24 -0
- data/lib/python/pickle/instructions/long.rb +24 -0
- data/lib/python/pickle/instructions/long1.rb +32 -0
- data/lib/python/pickle/instructions/long4.rb +32 -0
- data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
- data/lib/python/pickle/instructions/mark.rb +24 -0
- data/lib/python/pickle/instructions/memoize.rb +26 -0
- data/lib/python/pickle/instructions/new_false.rb +24 -0
- data/lib/python/pickle/instructions/new_obj.rb +26 -0
- data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
- data/lib/python/pickle/instructions/new_true.rb +24 -0
- data/lib/python/pickle/instructions/next_buffer.rb +26 -0
- data/lib/python/pickle/instructions/none.rb +24 -0
- data/lib/python/pickle/instructions/pop.rb +24 -0
- data/lib/python/pickle/instructions/pop_mark.rb +24 -0
- data/lib/python/pickle/instructions/proto.rb +29 -0
- data/lib/python/pickle/instructions/put.rb +24 -0
- data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
- data/lib/python/pickle/instructions/reduce.rb +24 -0
- data/lib/python/pickle/instructions/set_item.rb +24 -0
- data/lib/python/pickle/instructions/set_items.rb +26 -0
- data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
- data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
- data/lib/python/pickle/instructions/stack_global.rb +26 -0
- data/lib/python/pickle/instructions/stop.rb +24 -0
- data/lib/python/pickle/instructions/string.rb +24 -0
- data/lib/python/pickle/instructions/tuple.rb +24 -0
- data/lib/python/pickle/instructions/tuple1.rb +24 -0
- data/lib/python/pickle/instructions/tuple2.rb +24 -0
- data/lib/python/pickle/instructions/tuple3.rb +24 -0
- data/lib/python/pickle/protocol.rb +56 -0
- data/lib/python/pickle/protocol0.rb +399 -0
- data/lib/python/pickle/protocol1.rb +183 -0
- data/lib/python/pickle/protocol2.rb +229 -0
- data/lib/python/pickle/protocol3.rb +163 -0
- data/lib/python/pickle/protocol4.rb +285 -0
- data/lib/python/pickle/protocol5.rb +218 -0
- data/lib/python/pickle/py_class.rb +75 -0
- data/lib/python/pickle/py_object.rb +141 -0
- data/lib/python/pickle/tuple.rb +19 -0
- data/lib/python/pickle/version.rb +6 -0
- data/lib/python/pickle.rb +226 -0
- data/python-pickle.gemspec +62 -0
- data/spec/byte_array_spec.rb +54 -0
- data/spec/deserializer_spec.rb +1201 -0
- data/spec/fixtures/ascii_str_v3.pkl +0 -0
- data/spec/fixtures/ascii_str_v4.pkl +0 -0
- data/spec/fixtures/ascii_str_v5.pkl +0 -0
- data/spec/fixtures/bin_str_v0.pkl +3 -0
- data/spec/fixtures/bin_str_v1.pkl +0 -0
- data/spec/fixtures/bin_str_v2.pkl +0 -0
- data/spec/fixtures/bin_str_v3.pkl +0 -0
- data/spec/fixtures/bin_str_v4.pkl +0 -0
- data/spec/fixtures/bin_str_v5.pkl +0 -0
- data/spec/fixtures/bytearray_v0.pkl +10 -0
- data/spec/fixtures/bytearray_v1.pkl +0 -0
- data/spec/fixtures/bytearray_v2.pkl +0 -0
- data/spec/fixtures/bytearray_v3.pkl +0 -0
- data/spec/fixtures/bytearray_v4.pkl +0 -0
- data/spec/fixtures/bytearray_v5.pkl +0 -0
- data/spec/fixtures/class_v0.pkl +4 -0
- data/spec/fixtures/class_v1.pkl +0 -0
- data/spec/fixtures/class_v2.pkl +0 -0
- data/spec/fixtures/class_v3.pkl +0 -0
- data/spec/fixtures/class_v4.pkl +0 -0
- data/spec/fixtures/class_v5.pkl +0 -0
- data/spec/fixtures/dict_v0.pkl +6 -0
- data/spec/fixtures/dict_v1.pkl +0 -0
- data/spec/fixtures/dict_v2.pkl +0 -0
- data/spec/fixtures/dict_v3.pkl +0 -0
- data/spec/fixtures/dict_v4.pkl +0 -0
- data/spec/fixtures/dict_v5.pkl +0 -0
- data/spec/fixtures/escaped_str_v0.pkl +3 -0
- data/spec/fixtures/escaped_str_v1.pkl +0 -0
- data/spec/fixtures/escaped_str_v2.pkl +0 -0
- data/spec/fixtures/false_v0.pkl +2 -0
- data/spec/fixtures/false_v1.pkl +2 -0
- data/spec/fixtures/false_v2.pkl +1 -0
- data/spec/fixtures/false_v3.pkl +1 -0
- data/spec/fixtures/false_v4.pkl +1 -0
- data/spec/fixtures/false_v5.pkl +1 -0
- data/spec/fixtures/float_v0.pkl +2 -0
- data/spec/fixtures/float_v1.pkl +1 -0
- data/spec/fixtures/float_v2.pkl +1 -0
- data/spec/fixtures/float_v3.pkl +1 -0
- data/spec/fixtures/float_v4.pkl +0 -0
- data/spec/fixtures/float_v5.pkl +0 -0
- data/spec/fixtures/function_v0.pkl +4 -0
- data/spec/fixtures/function_v1.pkl +0 -0
- data/spec/fixtures/function_v2.pkl +0 -0
- data/spec/fixtures/function_v3.pkl +0 -0
- data/spec/fixtures/function_v4.pkl +0 -0
- data/spec/fixtures/function_v5.pkl +0 -0
- data/spec/fixtures/hex_str_v0.pkl +3 -0
- data/spec/fixtures/hex_str_v1.pkl +0 -0
- data/spec/fixtures/hex_str_v2.pkl +0 -0
- data/spec/fixtures/int_v0.pkl +2 -0
- data/spec/fixtures/int_v1.pkl +1 -0
- data/spec/fixtures/int_v2.pkl +1 -0
- data/spec/fixtures/int_v3.pkl +1 -0
- data/spec/fixtures/int_v4.pkl +1 -0
- data/spec/fixtures/int_v5.pkl +1 -0
- data/spec/fixtures/list_v0.pkl +7 -0
- data/spec/fixtures/list_v1.pkl +0 -0
- data/spec/fixtures/list_v2.pkl +0 -0
- data/spec/fixtures/list_v3.pkl +0 -0
- data/spec/fixtures/list_v4.pkl +0 -0
- data/spec/fixtures/list_v5.pkl +0 -0
- data/spec/fixtures/long_v0.pkl +2 -0
- data/spec/fixtures/long_v1.pkl +2 -0
- data/spec/fixtures/long_v2.pkl +0 -0
- data/spec/fixtures/long_v3.pkl +0 -0
- data/spec/fixtures/long_v4.pkl +0 -0
- data/spec/fixtures/long_v5.pkl +0 -0
- data/spec/fixtures/nested_dict_v0.pkl +12 -0
- data/spec/fixtures/nested_dict_v1.pkl +0 -0
- data/spec/fixtures/nested_dict_v2.pkl +0 -0
- data/spec/fixtures/nested_dict_v3.pkl +0 -0
- data/spec/fixtures/nested_dict_v4.pkl +0 -0
- data/spec/fixtures/nested_dict_v5.pkl +0 -0
- data/spec/fixtures/nested_list_v0.pkl +9 -0
- data/spec/fixtures/nested_list_v1.pkl +0 -0
- data/spec/fixtures/nested_list_v2.pkl +0 -0
- data/spec/fixtures/nested_list_v3.pkl +0 -0
- data/spec/fixtures/nested_list_v4.pkl +0 -0
- data/spec/fixtures/nested_list_v5.pkl +0 -0
- data/spec/fixtures/none_v0.pkl +1 -0
- data/spec/fixtures/none_v1.pkl +1 -0
- data/spec/fixtures/none_v2.pkl +1 -0
- data/spec/fixtures/none_v3.pkl +1 -0
- data/spec/fixtures/none_v4.pkl +1 -0
- data/spec/fixtures/none_v5.pkl +1 -0
- data/spec/fixtures/object_v0.pkl +19 -0
- data/spec/fixtures/object_v1.pkl +0 -0
- data/spec/fixtures/object_v2.pkl +0 -0
- data/spec/fixtures/object_v3.pkl +0 -0
- data/spec/fixtures/object_v4.pkl +0 -0
- data/spec/fixtures/object_v5.pkl +0 -0
- data/spec/fixtures/str_v0.pkl +3 -0
- data/spec/fixtures/str_v1.pkl +0 -0
- data/spec/fixtures/str_v2.pkl +0 -0
- data/spec/fixtures/str_v3.pkl +0 -0
- data/spec/fixtures/str_v4.pkl +0 -0
- data/spec/fixtures/str_v5.pkl +0 -0
- data/spec/fixtures/true_v0.pkl +2 -0
- data/spec/fixtures/true_v1.pkl +2 -0
- data/spec/fixtures/true_v2.pkl +1 -0
- data/spec/fixtures/true_v3.pkl +1 -0
- data/spec/fixtures/true_v4.pkl +1 -0
- data/spec/fixtures/true_v5.pkl +1 -0
- data/spec/fixtures/unicode_str_v0.pkl +3 -0
- data/spec/fixtures/unicode_str_v1.pkl +0 -0
- data/spec/fixtures/unicode_str_v2.pkl +0 -0
- data/spec/fixtures/unicode_str_v3.pkl +0 -0
- data/spec/fixtures/unicode_str_v4.pkl +0 -0
- data/spec/fixtures/unicode_str_v5.pkl +0 -0
- data/spec/generate_pickles2.py +41 -0
- data/spec/generate_pickles3.py +40 -0
- data/spec/integration/load/protocol0_spec.rb +258 -0
- data/spec/integration/load/protocol1_spec.rb +258 -0
- data/spec/integration/load/protocol2_spec.rb +258 -0
- data/spec/integration/load/protocol3_spec.rb +258 -0
- data/spec/integration/load/protocol4_spec.rb +258 -0
- data/spec/integration/load/protocol5_spec.rb +258 -0
- data/spec/integration/parse/protocol0_spec.rb +467 -0
- data/spec/integration/parse/protocol1_spec.rb +459 -0
- data/spec/integration/parse/protocol2_spec.rb +471 -0
- data/spec/integration/parse/protocol3_spec.rb +407 -0
- data/spec/integration/parse/protocol4_spec.rb +439 -0
- data/spec/integration/parse/protocol5_spec.rb +419 -0
- data/spec/pickle_spec.rb +163 -0
- data/spec/protocol0_read_instruction_examples.rb +211 -0
- data/spec/protocol0_spec.rb +445 -0
- data/spec/protocol1_read_instruction_examples.rb +156 -0
- data/spec/protocol1_spec.rb +59 -0
- data/spec/protocol2_read_instruction_examples.rb +135 -0
- data/spec/protocol2_spec.rb +128 -0
- data/spec/protocol3_read_instruction_examples.rb +29 -0
- data/spec/protocol3_spec.rb +32 -0
- data/spec/protocol4_read_instruction_examples.rb +142 -0
- data/spec/protocol4_spec.rb +58 -0
- data/spec/protocol5_spec.rb +68 -0
- data/spec/py_class_spec.rb +62 -0
- data/spec/py_object_spec.rb +149 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tuple_spec.rb +18 -0
- metadata +325 -0
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'python/pickle'
|
3
|
+
|
4
|
+
describe Python::Pickle do
|
5
|
+
let(:fixtures_dir) { File.join(__dir__,'..','..','fixtures') }
|
6
|
+
|
7
|
+
describe ".load" do
|
8
|
+
context "when given a Pickle protocol 4 stream" do
|
9
|
+
let(:io) { File.open(file) }
|
10
|
+
|
11
|
+
context "and it contains a serialized None" do
|
12
|
+
let(:file) { File.join(fixtures_dir,'none_v4.pkl') }
|
13
|
+
|
14
|
+
it "must return nil" do
|
15
|
+
expect(subject.load(io)).to be(nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "and it contains a serialized True" do
|
20
|
+
let(:file) { File.join(fixtures_dir,'true_v4.pkl') }
|
21
|
+
|
22
|
+
it "must return true" do
|
23
|
+
expect(subject.load(io)).to be(true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "and it contains a serialized False" do
|
28
|
+
let(:file) { File.join(fixtures_dir,'false_v4.pkl') }
|
29
|
+
|
30
|
+
it "must return false" do
|
31
|
+
expect(subject.load(io)).to be(false)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "and it contains a serialized integer" do
|
36
|
+
let(:file) { File.join(fixtures_dir,'int_v4.pkl') }
|
37
|
+
|
38
|
+
it "must return an Integer object" do
|
39
|
+
expect(subject.load(io)).to eq(42)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "and it contains a long integer" do
|
44
|
+
let(:file) { File.join(fixtures_dir,'long_v4.pkl') }
|
45
|
+
|
46
|
+
it "must return an Integer object" do
|
47
|
+
expect(subject.load(io)).to eq((2 ** 64) - 1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "and it contains a serialized floating-point number" do
|
52
|
+
let(:float) { 3.141592653589793 }
|
53
|
+
let(:file) { File.join(fixtures_dir,'float_v4.pkl') }
|
54
|
+
|
55
|
+
it "must return a Float object" do
|
56
|
+
expect(subject.load(io)).to eq(float)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "and it contains a plain string" do
|
61
|
+
let(:string) { "ABC" }
|
62
|
+
let(:file) { File.join(fixtures_dir,'str_v4.pkl') }
|
63
|
+
|
64
|
+
it "must return a String object" do
|
65
|
+
expect(subject.load(io)).to eq(string)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "and it contains a UTF string" do
|
69
|
+
let(:string) { "ABC\u265E\u265F\u{1F600}" }
|
70
|
+
let(:file) { File.join(fixtures_dir,'unicode_str_v4.pkl') }
|
71
|
+
|
72
|
+
it "must return a UTF-8 encoded String object" do
|
73
|
+
expect(subject.load(io).encoding).to be(Encoding::UTF_8)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "and it contains a bytearray type" do
|
79
|
+
let(:string) { "ABC" }
|
80
|
+
let(:file) { File.join(fixtures_dir,'bytearray_v4.pkl') }
|
81
|
+
|
82
|
+
it "must return a Python::Pickle::ByteArray object" do
|
83
|
+
expect(subject.load(io)).to eq(Python::Pickle::ByteArray.new(string))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "and it contains a list type" do
|
88
|
+
let(:file) { File.join(fixtures_dir,'list_v4.pkl') }
|
89
|
+
|
90
|
+
it "must return an Array object" do
|
91
|
+
expect(subject.load(io)).to eq(
|
92
|
+
[nil, true, false, 42, "ABC"]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
context "but the list object contains other list objects" do
|
97
|
+
let(:file) { File.join(fixtures_dir,'nested_list_v4.pkl') }
|
98
|
+
|
99
|
+
it "must return a nested Array object" do
|
100
|
+
expect(subject.load(io)).to eq(
|
101
|
+
[1, [2, [3, [4]]]]
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "and it contains a dict type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'dict_v4.pkl') }
|
109
|
+
|
110
|
+
it "must return an Array object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
{"foo" => "bar"}
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
context "but the dict object contains other dict objects" do
|
117
|
+
let(:file) { File.join(fixtures_dir,'nested_dict_v4.pkl') }
|
118
|
+
|
119
|
+
it "must return a nested Hash object" do
|
120
|
+
expect(subject.load(io)).to eq(
|
121
|
+
{"a" => {"b" => {"c" => "d"}}}
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "and it contains a function type" do
|
128
|
+
let(:file) { File.join(fixtures_dir,'function_v4.pkl') }
|
129
|
+
|
130
|
+
context "but there is no constant mapping for the function name" do
|
131
|
+
it "must return a Python::Pickle::PyClass object of the given name" do
|
132
|
+
object = subject.load(io)
|
133
|
+
|
134
|
+
expect(object).to be_kind_of(Python::Pickle::PyClass)
|
135
|
+
expect(object.namespace).to eq('__main__')
|
136
|
+
expect(object.name).to eq('func')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "but there is a constant mapping for the function name" do
|
141
|
+
module TestPickleLoad
|
142
|
+
def self.func
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "must return a mapped Ruby method" do
|
147
|
+
object = subject.load(io, constants: {
|
148
|
+
'__main__' => {
|
149
|
+
'func' => TestPickleLoad.method(:func)
|
150
|
+
}
|
151
|
+
})
|
152
|
+
|
153
|
+
expect(object).to eq(TestPickleLoad.method(:func))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "and it contains a class type" do
|
159
|
+
let(:file) { File.join(fixtures_dir,'class_v4.pkl') }
|
160
|
+
|
161
|
+
context "but there is no constant mapping for the class name" do
|
162
|
+
it "must return a Python::Pickle::PyClass object of the given name" do
|
163
|
+
object = subject.load(io)
|
164
|
+
|
165
|
+
expect(object).to be_kind_of(Python::Pickle::PyClass)
|
166
|
+
expect(object.namespace).to eq('__main__')
|
167
|
+
expect(object.name).to eq('MyClass')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "but there is a constant mapping for the class name" do
|
172
|
+
module TestPickleLoad
|
173
|
+
class MyClass
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "must return a new Ruby object for the mapped class" do
|
178
|
+
object = subject.load(io, constants: {
|
179
|
+
'__main__' => {
|
180
|
+
'MyClass' => TestPickleLoad::MyClass
|
181
|
+
}
|
182
|
+
})
|
183
|
+
|
184
|
+
expect(object).to be(TestPickleLoad::MyClass)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "and it contains an object type" do
|
190
|
+
let(:file) { File.join(fixtures_dir,'object_v1.pkl') }
|
191
|
+
|
192
|
+
context "but there is no constant mapping for the class name" do
|
193
|
+
it "must return a Python::Pickle::PyObject object of the given name" do
|
194
|
+
object = subject.load(io)
|
195
|
+
|
196
|
+
expect(object).to be_kind_of(Python::Pickle::PyObject)
|
197
|
+
expect(object.py_class).to be_kind_of(Python::Pickle::PyClass)
|
198
|
+
expect(object.py_class.namespace).to eq('__main__')
|
199
|
+
expect(object.py_class.name).to eq('MyClass')
|
200
|
+
expect(object.attributes).to eq(
|
201
|
+
{
|
202
|
+
'x' => 0x41,
|
203
|
+
'y' => 0x42
|
204
|
+
}
|
205
|
+
)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "but there is a constant mapping for the class name" do
|
210
|
+
context "and the Ruby class defines a __setstate__ method" do
|
211
|
+
module TestPickleLoad
|
212
|
+
class MyClassWithSetState
|
213
|
+
attr_reader :x
|
214
|
+
attr_reader :y
|
215
|
+
|
216
|
+
def __setstate__(attributes)
|
217
|
+
@x = attributes['x']
|
218
|
+
@y = attributes['y']
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it "must return a new Ruby object for the mapped class" do
|
224
|
+
object = subject.load(io, constants: {
|
225
|
+
'__main__' => {
|
226
|
+
'MyClass' => TestPickleLoad::MyClassWithSetState
|
227
|
+
}
|
228
|
+
})
|
229
|
+
|
230
|
+
expect(object).to be_kind_of(TestPickleLoad::MyClassWithSetState)
|
231
|
+
expect(object.x).to eq(0x41)
|
232
|
+
expect(object.y).to eq(0x42)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "but the Ruby class does not define a __setstate__ method" do
|
237
|
+
module TestPickleLoad
|
238
|
+
class MyClassWithoutSetState
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
it do
|
243
|
+
expect {
|
244
|
+
subject.load(io, constants: {
|
245
|
+
'__main__' => {
|
246
|
+
'MyClass' => TestPickleLoad::MyClassWithoutSetState
|
247
|
+
}
|
248
|
+
})
|
249
|
+
}.to raise_error(Python::Pickle::DeserializationError).with_message(
|
250
|
+
/\Acannot execute BUILD on an object that does not define a __setstate__ method or is not a Hash: #<TestPickleLoad::MyClassWithoutSetState:0x[0-9a-f]+>\z/
|
251
|
+
)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'python/pickle'
|
3
|
+
|
4
|
+
describe Python::Pickle do
|
5
|
+
let(:fixtures_dir) { File.join(__dir__,'..','..','fixtures') }
|
6
|
+
|
7
|
+
describe ".load" do
|
8
|
+
context "when given a Pickle protocol 5 stream" do
|
9
|
+
let(:io) { File.open(file) }
|
10
|
+
|
11
|
+
context "and it contains a serialized None" do
|
12
|
+
let(:file) { File.join(fixtures_dir,'none_v5.pkl') }
|
13
|
+
|
14
|
+
it "must return nil" do
|
15
|
+
expect(subject.load(io)).to be(nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "and it contains a serialized True" do
|
20
|
+
let(:file) { File.join(fixtures_dir,'true_v5.pkl') }
|
21
|
+
|
22
|
+
it "must return true" do
|
23
|
+
expect(subject.load(io)).to be(true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "and it contains a serialized False" do
|
28
|
+
let(:file) { File.join(fixtures_dir,'false_v5.pkl') }
|
29
|
+
|
30
|
+
it "must return false" do
|
31
|
+
expect(subject.load(io)).to be(false)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "and it contains a serialized integer" do
|
36
|
+
let(:file) { File.join(fixtures_dir,'int_v5.pkl') }
|
37
|
+
|
38
|
+
it "must return an Integer object" do
|
39
|
+
expect(subject.load(io)).to eq(42)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "and it contains a long integer" do
|
44
|
+
let(:file) { File.join(fixtures_dir,'long_v5.pkl') }
|
45
|
+
|
46
|
+
it "must return an Integer object" do
|
47
|
+
expect(subject.load(io)).to eq((2 ** 64) - 1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "and it contains a serialized floating-point number" do
|
52
|
+
let(:float) { 3.141592653589793 }
|
53
|
+
let(:file) { File.join(fixtures_dir,'float_v5.pkl') }
|
54
|
+
|
55
|
+
it "must return a Float object" do
|
56
|
+
expect(subject.load(io)).to eq(float)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "and it contains a plain string" do
|
61
|
+
let(:string) { "ABC" }
|
62
|
+
let(:file) { File.join(fixtures_dir,'str_v5.pkl') }
|
63
|
+
|
64
|
+
it "must return a String object" do
|
65
|
+
expect(subject.load(io)).to eq(string)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "and it contains a UTF string" do
|
69
|
+
let(:string) { "ABC\u265E\u265F\u{1F600}" }
|
70
|
+
let(:file) { File.join(fixtures_dir,'unicode_str_v5.pkl') }
|
71
|
+
|
72
|
+
it "must return a UTF-8 encoded String object" do
|
73
|
+
expect(subject.load(io).encoding).to be(Encoding::UTF_8)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "and it contains a bytearray type" do
|
79
|
+
let(:string) { "ABC" }
|
80
|
+
let(:file) { File.join(fixtures_dir,'bytearray_v5.pkl') }
|
81
|
+
|
82
|
+
it "must return a Python::Pickle::ByteArray object" do
|
83
|
+
expect(subject.load(io)).to eq(Python::Pickle::ByteArray.new(string))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "and it contains a list type" do
|
88
|
+
let(:file) { File.join(fixtures_dir,'list_v5.pkl') }
|
89
|
+
|
90
|
+
it "must return an Array object" do
|
91
|
+
expect(subject.load(io)).to eq(
|
92
|
+
[nil, true, false, 42, "ABC"]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
context "but the list object contains other list objects" do
|
97
|
+
let(:file) { File.join(fixtures_dir,'nested_list_v5.pkl') }
|
98
|
+
|
99
|
+
it "must return a nested Array object" do
|
100
|
+
expect(subject.load(io)).to eq(
|
101
|
+
[1, [2, [3, [4]]]]
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "and it contains a dict type" do
|
108
|
+
let(:file) { File.join(fixtures_dir,'dict_v5.pkl') }
|
109
|
+
|
110
|
+
it "must return an Array object" do
|
111
|
+
expect(subject.load(io)).to eq(
|
112
|
+
{"foo" => "bar"}
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
context "but the dict object contains other dict objects" do
|
117
|
+
let(:file) { File.join(fixtures_dir,'nested_dict_v5.pkl') }
|
118
|
+
|
119
|
+
it "must return a nested Hash object" do
|
120
|
+
expect(subject.load(io)).to eq(
|
121
|
+
{"a" => {"b" => {"c" => "d"}}}
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "and it contains a function type" do
|
128
|
+
let(:file) { File.join(fixtures_dir,'function_v5.pkl') }
|
129
|
+
|
130
|
+
context "but there is no constant mapping for the function name" do
|
131
|
+
it "must return a Python::Pickle::PyClass object of the given name" do
|
132
|
+
object = subject.load(io)
|
133
|
+
|
134
|
+
expect(object).to be_kind_of(Python::Pickle::PyClass)
|
135
|
+
expect(object.namespace).to eq('__main__')
|
136
|
+
expect(object.name).to eq('func')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "but there is a constant mapping for the function name" do
|
141
|
+
module TestPickleLoad
|
142
|
+
def self.func
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "must return a mapped Ruby method" do
|
147
|
+
object = subject.load(io, constants: {
|
148
|
+
'__main__' => {
|
149
|
+
'func' => TestPickleLoad.method(:func)
|
150
|
+
}
|
151
|
+
})
|
152
|
+
|
153
|
+
expect(object).to eq(TestPickleLoad.method(:func))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "and it contains a class type" do
|
159
|
+
let(:file) { File.join(fixtures_dir,'class_v5.pkl') }
|
160
|
+
|
161
|
+
context "but there is no constant mapping for the class name" do
|
162
|
+
it "must return a Python::Pickle::PyClass object of the given name" do
|
163
|
+
object = subject.load(io)
|
164
|
+
|
165
|
+
expect(object).to be_kind_of(Python::Pickle::PyClass)
|
166
|
+
expect(object.namespace).to eq('__main__')
|
167
|
+
expect(object.name).to eq('MyClass')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "but there is a constant mapping for the class name" do
|
172
|
+
module TestPickleLoad
|
173
|
+
class MyClass
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "must return a new Ruby object for the mapped class" do
|
178
|
+
object = subject.load(io, constants: {
|
179
|
+
'__main__' => {
|
180
|
+
'MyClass' => TestPickleLoad::MyClass
|
181
|
+
}
|
182
|
+
})
|
183
|
+
|
184
|
+
expect(object).to be(TestPickleLoad::MyClass)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "and it contains an object type" do
|
190
|
+
let(:file) { File.join(fixtures_dir,'object_v1.pkl') }
|
191
|
+
|
192
|
+
context "but there is no constant mapping for the class name" do
|
193
|
+
it "must return a Python::Pickle::PyObject object of the given name" do
|
194
|
+
object = subject.load(io)
|
195
|
+
|
196
|
+
expect(object).to be_kind_of(Python::Pickle::PyObject)
|
197
|
+
expect(object.py_class).to be_kind_of(Python::Pickle::PyClass)
|
198
|
+
expect(object.py_class.namespace).to eq('__main__')
|
199
|
+
expect(object.py_class.name).to eq('MyClass')
|
200
|
+
expect(object.attributes).to eq(
|
201
|
+
{
|
202
|
+
'x' => 0x41,
|
203
|
+
'y' => 0x42
|
204
|
+
}
|
205
|
+
)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "but there is a constant mapping for the class name" do
|
210
|
+
context "and the Ruby class defines a __setstate__ method" do
|
211
|
+
module TestPickleLoad
|
212
|
+
class MyClassWithSetState
|
213
|
+
attr_reader :x
|
214
|
+
attr_reader :y
|
215
|
+
|
216
|
+
def __setstate__(attributes)
|
217
|
+
@x = attributes['x']
|
218
|
+
@y = attributes['y']
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it "must return a new Ruby object for the mapped class" do
|
224
|
+
object = subject.load(io, constants: {
|
225
|
+
'__main__' => {
|
226
|
+
'MyClass' => TestPickleLoad::MyClassWithSetState
|
227
|
+
}
|
228
|
+
})
|
229
|
+
|
230
|
+
expect(object).to be_kind_of(TestPickleLoad::MyClassWithSetState)
|
231
|
+
expect(object.x).to eq(0x41)
|
232
|
+
expect(object.y).to eq(0x42)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "but the Ruby class does not define a __setstate__ method" do
|
237
|
+
module TestPickleLoad
|
238
|
+
class MyClassWithoutSetState
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
it do
|
243
|
+
expect {
|
244
|
+
subject.load(io, constants: {
|
245
|
+
'__main__' => {
|
246
|
+
'MyClass' => TestPickleLoad::MyClassWithoutSetState
|
247
|
+
}
|
248
|
+
})
|
249
|
+
}.to raise_error(Python::Pickle::DeserializationError).with_message(
|
250
|
+
/\Acannot execute BUILD on an object that does not define a __setstate__ method or is not a Hash: #<TestPickleLoad::MyClassWithoutSetState:0x[0-9a-f]+>\z/
|
251
|
+
)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|