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.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +27 -0
  4. data/.gitignore +5 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +14 -0
  8. data/Gemfile +15 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +149 -0
  11. data/Rakefile +13 -0
  12. data/gemspec.yml +25 -0
  13. data/lib/python/pickle/byte_array.rb +40 -0
  14. data/lib/python/pickle/deserializer.rb +595 -0
  15. data/lib/python/pickle/exceptions.rb +12 -0
  16. data/lib/python/pickle/instruction.rb +52 -0
  17. data/lib/python/pickle/instructions/add_items.rb +26 -0
  18. data/lib/python/pickle/instructions/append.rb +24 -0
  19. data/lib/python/pickle/instructions/appends.rb +26 -0
  20. data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
  21. data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
  22. data/lib/python/pickle/instructions/bin_float.rb +29 -0
  23. data/lib/python/pickle/instructions/bin_get.rb +27 -0
  24. data/lib/python/pickle/instructions/bin_int1.rb +29 -0
  25. data/lib/python/pickle/instructions/bin_put.rb +29 -0
  26. data/lib/python/pickle/instructions/bin_string.rb +32 -0
  27. data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
  28. data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
  29. data/lib/python/pickle/instructions/build.rb +24 -0
  30. data/lib/python/pickle/instructions/byte_array8.rb +32 -0
  31. data/lib/python/pickle/instructions/dict.rb +17 -0
  32. data/lib/python/pickle/instructions/dup.rb +24 -0
  33. data/lib/python/pickle/instructions/empty_dict.rb +26 -0
  34. data/lib/python/pickle/instructions/empty_list.rb +26 -0
  35. data/lib/python/pickle/instructions/empty_set.rb +26 -0
  36. data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
  37. data/lib/python/pickle/instructions/ext1.rb +29 -0
  38. data/lib/python/pickle/instructions/ext2.rb +29 -0
  39. data/lib/python/pickle/instructions/ext4.rb +29 -0
  40. data/lib/python/pickle/instructions/float.rb +24 -0
  41. data/lib/python/pickle/instructions/frame.rb +29 -0
  42. data/lib/python/pickle/instructions/frozen_set.rb +26 -0
  43. data/lib/python/pickle/instructions/get.rb +27 -0
  44. data/lib/python/pickle/instructions/global.rb +62 -0
  45. data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
  46. data/lib/python/pickle/instructions/has_value.rb +50 -0
  47. data/lib/python/pickle/instructions/int.rb +24 -0
  48. data/lib/python/pickle/instructions/list.rb +24 -0
  49. data/lib/python/pickle/instructions/long.rb +24 -0
  50. data/lib/python/pickle/instructions/long1.rb +32 -0
  51. data/lib/python/pickle/instructions/long4.rb +32 -0
  52. data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
  53. data/lib/python/pickle/instructions/mark.rb +24 -0
  54. data/lib/python/pickle/instructions/memoize.rb +26 -0
  55. data/lib/python/pickle/instructions/new_false.rb +24 -0
  56. data/lib/python/pickle/instructions/new_obj.rb +26 -0
  57. data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
  58. data/lib/python/pickle/instructions/new_true.rb +24 -0
  59. data/lib/python/pickle/instructions/next_buffer.rb +26 -0
  60. data/lib/python/pickle/instructions/none.rb +24 -0
  61. data/lib/python/pickle/instructions/pop.rb +24 -0
  62. data/lib/python/pickle/instructions/pop_mark.rb +24 -0
  63. data/lib/python/pickle/instructions/proto.rb +29 -0
  64. data/lib/python/pickle/instructions/put.rb +24 -0
  65. data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
  66. data/lib/python/pickle/instructions/reduce.rb +24 -0
  67. data/lib/python/pickle/instructions/set_item.rb +24 -0
  68. data/lib/python/pickle/instructions/set_items.rb +26 -0
  69. data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
  70. data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
  71. data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
  72. data/lib/python/pickle/instructions/stack_global.rb +26 -0
  73. data/lib/python/pickle/instructions/stop.rb +24 -0
  74. data/lib/python/pickle/instructions/string.rb +24 -0
  75. data/lib/python/pickle/instructions/tuple.rb +24 -0
  76. data/lib/python/pickle/instructions/tuple1.rb +24 -0
  77. data/lib/python/pickle/instructions/tuple2.rb +24 -0
  78. data/lib/python/pickle/instructions/tuple3.rb +24 -0
  79. data/lib/python/pickle/protocol.rb +56 -0
  80. data/lib/python/pickle/protocol0.rb +399 -0
  81. data/lib/python/pickle/protocol1.rb +183 -0
  82. data/lib/python/pickle/protocol2.rb +229 -0
  83. data/lib/python/pickle/protocol3.rb +163 -0
  84. data/lib/python/pickle/protocol4.rb +285 -0
  85. data/lib/python/pickle/protocol5.rb +218 -0
  86. data/lib/python/pickle/py_class.rb +75 -0
  87. data/lib/python/pickle/py_object.rb +141 -0
  88. data/lib/python/pickle/tuple.rb +19 -0
  89. data/lib/python/pickle/version.rb +6 -0
  90. data/lib/python/pickle.rb +226 -0
  91. data/python-pickle.gemspec +62 -0
  92. data/spec/byte_array_spec.rb +54 -0
  93. data/spec/deserializer_spec.rb +1201 -0
  94. data/spec/fixtures/ascii_str_v3.pkl +0 -0
  95. data/spec/fixtures/ascii_str_v4.pkl +0 -0
  96. data/spec/fixtures/ascii_str_v5.pkl +0 -0
  97. data/spec/fixtures/bin_str_v0.pkl +3 -0
  98. data/spec/fixtures/bin_str_v1.pkl +0 -0
  99. data/spec/fixtures/bin_str_v2.pkl +0 -0
  100. data/spec/fixtures/bin_str_v3.pkl +0 -0
  101. data/spec/fixtures/bin_str_v4.pkl +0 -0
  102. data/spec/fixtures/bin_str_v5.pkl +0 -0
  103. data/spec/fixtures/bytearray_v0.pkl +10 -0
  104. data/spec/fixtures/bytearray_v1.pkl +0 -0
  105. data/spec/fixtures/bytearray_v2.pkl +0 -0
  106. data/spec/fixtures/bytearray_v3.pkl +0 -0
  107. data/spec/fixtures/bytearray_v4.pkl +0 -0
  108. data/spec/fixtures/bytearray_v5.pkl +0 -0
  109. data/spec/fixtures/class_v0.pkl +4 -0
  110. data/spec/fixtures/class_v1.pkl +0 -0
  111. data/spec/fixtures/class_v2.pkl +0 -0
  112. data/spec/fixtures/class_v3.pkl +0 -0
  113. data/spec/fixtures/class_v4.pkl +0 -0
  114. data/spec/fixtures/class_v5.pkl +0 -0
  115. data/spec/fixtures/dict_v0.pkl +6 -0
  116. data/spec/fixtures/dict_v1.pkl +0 -0
  117. data/spec/fixtures/dict_v2.pkl +0 -0
  118. data/spec/fixtures/dict_v3.pkl +0 -0
  119. data/spec/fixtures/dict_v4.pkl +0 -0
  120. data/spec/fixtures/dict_v5.pkl +0 -0
  121. data/spec/fixtures/escaped_str_v0.pkl +3 -0
  122. data/spec/fixtures/escaped_str_v1.pkl +0 -0
  123. data/spec/fixtures/escaped_str_v2.pkl +0 -0
  124. data/spec/fixtures/false_v0.pkl +2 -0
  125. data/spec/fixtures/false_v1.pkl +2 -0
  126. data/spec/fixtures/false_v2.pkl +1 -0
  127. data/spec/fixtures/false_v3.pkl +1 -0
  128. data/spec/fixtures/false_v4.pkl +1 -0
  129. data/spec/fixtures/false_v5.pkl +1 -0
  130. data/spec/fixtures/float_v0.pkl +2 -0
  131. data/spec/fixtures/float_v1.pkl +1 -0
  132. data/spec/fixtures/float_v2.pkl +1 -0
  133. data/spec/fixtures/float_v3.pkl +1 -0
  134. data/spec/fixtures/float_v4.pkl +0 -0
  135. data/spec/fixtures/float_v5.pkl +0 -0
  136. data/spec/fixtures/function_v0.pkl +4 -0
  137. data/spec/fixtures/function_v1.pkl +0 -0
  138. data/spec/fixtures/function_v2.pkl +0 -0
  139. data/spec/fixtures/function_v3.pkl +0 -0
  140. data/spec/fixtures/function_v4.pkl +0 -0
  141. data/spec/fixtures/function_v5.pkl +0 -0
  142. data/spec/fixtures/hex_str_v0.pkl +3 -0
  143. data/spec/fixtures/hex_str_v1.pkl +0 -0
  144. data/spec/fixtures/hex_str_v2.pkl +0 -0
  145. data/spec/fixtures/int_v0.pkl +2 -0
  146. data/spec/fixtures/int_v1.pkl +1 -0
  147. data/spec/fixtures/int_v2.pkl +1 -0
  148. data/spec/fixtures/int_v3.pkl +1 -0
  149. data/spec/fixtures/int_v4.pkl +1 -0
  150. data/spec/fixtures/int_v5.pkl +1 -0
  151. data/spec/fixtures/list_v0.pkl +7 -0
  152. data/spec/fixtures/list_v1.pkl +0 -0
  153. data/spec/fixtures/list_v2.pkl +0 -0
  154. data/spec/fixtures/list_v3.pkl +0 -0
  155. data/spec/fixtures/list_v4.pkl +0 -0
  156. data/spec/fixtures/list_v5.pkl +0 -0
  157. data/spec/fixtures/long_v0.pkl +2 -0
  158. data/spec/fixtures/long_v1.pkl +2 -0
  159. data/spec/fixtures/long_v2.pkl +0 -0
  160. data/spec/fixtures/long_v3.pkl +0 -0
  161. data/spec/fixtures/long_v4.pkl +0 -0
  162. data/spec/fixtures/long_v5.pkl +0 -0
  163. data/spec/fixtures/nested_dict_v0.pkl +12 -0
  164. data/spec/fixtures/nested_dict_v1.pkl +0 -0
  165. data/spec/fixtures/nested_dict_v2.pkl +0 -0
  166. data/spec/fixtures/nested_dict_v3.pkl +0 -0
  167. data/spec/fixtures/nested_dict_v4.pkl +0 -0
  168. data/spec/fixtures/nested_dict_v5.pkl +0 -0
  169. data/spec/fixtures/nested_list_v0.pkl +9 -0
  170. data/spec/fixtures/nested_list_v1.pkl +0 -0
  171. data/spec/fixtures/nested_list_v2.pkl +0 -0
  172. data/spec/fixtures/nested_list_v3.pkl +0 -0
  173. data/spec/fixtures/nested_list_v4.pkl +0 -0
  174. data/spec/fixtures/nested_list_v5.pkl +0 -0
  175. data/spec/fixtures/none_v0.pkl +1 -0
  176. data/spec/fixtures/none_v1.pkl +1 -0
  177. data/spec/fixtures/none_v2.pkl +1 -0
  178. data/spec/fixtures/none_v3.pkl +1 -0
  179. data/spec/fixtures/none_v4.pkl +1 -0
  180. data/spec/fixtures/none_v5.pkl +1 -0
  181. data/spec/fixtures/object_v0.pkl +19 -0
  182. data/spec/fixtures/object_v1.pkl +0 -0
  183. data/spec/fixtures/object_v2.pkl +0 -0
  184. data/spec/fixtures/object_v3.pkl +0 -0
  185. data/spec/fixtures/object_v4.pkl +0 -0
  186. data/spec/fixtures/object_v5.pkl +0 -0
  187. data/spec/fixtures/str_v0.pkl +3 -0
  188. data/spec/fixtures/str_v1.pkl +0 -0
  189. data/spec/fixtures/str_v2.pkl +0 -0
  190. data/spec/fixtures/str_v3.pkl +0 -0
  191. data/spec/fixtures/str_v4.pkl +0 -0
  192. data/spec/fixtures/str_v5.pkl +0 -0
  193. data/spec/fixtures/true_v0.pkl +2 -0
  194. data/spec/fixtures/true_v1.pkl +2 -0
  195. data/spec/fixtures/true_v2.pkl +1 -0
  196. data/spec/fixtures/true_v3.pkl +1 -0
  197. data/spec/fixtures/true_v4.pkl +1 -0
  198. data/spec/fixtures/true_v5.pkl +1 -0
  199. data/spec/fixtures/unicode_str_v0.pkl +3 -0
  200. data/spec/fixtures/unicode_str_v1.pkl +0 -0
  201. data/spec/fixtures/unicode_str_v2.pkl +0 -0
  202. data/spec/fixtures/unicode_str_v3.pkl +0 -0
  203. data/spec/fixtures/unicode_str_v4.pkl +0 -0
  204. data/spec/fixtures/unicode_str_v5.pkl +0 -0
  205. data/spec/generate_pickles2.py +41 -0
  206. data/spec/generate_pickles3.py +40 -0
  207. data/spec/integration/load/protocol0_spec.rb +258 -0
  208. data/spec/integration/load/protocol1_spec.rb +258 -0
  209. data/spec/integration/load/protocol2_spec.rb +258 -0
  210. data/spec/integration/load/protocol3_spec.rb +258 -0
  211. data/spec/integration/load/protocol4_spec.rb +258 -0
  212. data/spec/integration/load/protocol5_spec.rb +258 -0
  213. data/spec/integration/parse/protocol0_spec.rb +467 -0
  214. data/spec/integration/parse/protocol1_spec.rb +459 -0
  215. data/spec/integration/parse/protocol2_spec.rb +471 -0
  216. data/spec/integration/parse/protocol3_spec.rb +407 -0
  217. data/spec/integration/parse/protocol4_spec.rb +439 -0
  218. data/spec/integration/parse/protocol5_spec.rb +419 -0
  219. data/spec/pickle_spec.rb +163 -0
  220. data/spec/protocol0_read_instruction_examples.rb +211 -0
  221. data/spec/protocol0_spec.rb +445 -0
  222. data/spec/protocol1_read_instruction_examples.rb +156 -0
  223. data/spec/protocol1_spec.rb +59 -0
  224. data/spec/protocol2_read_instruction_examples.rb +135 -0
  225. data/spec/protocol2_spec.rb +128 -0
  226. data/spec/protocol3_read_instruction_examples.rb +29 -0
  227. data/spec/protocol3_spec.rb +32 -0
  228. data/spec/protocol4_read_instruction_examples.rb +142 -0
  229. data/spec/protocol4_spec.rb +58 -0
  230. data/spec/protocol5_spec.rb +68 -0
  231. data/spec/py_class_spec.rb +62 -0
  232. data/spec/py_object_spec.rb +149 -0
  233. data/spec/spec_helper.rb +3 -0
  234. data/spec/tuple_spec.rb +18 -0
  235. metadata +325 -0
@@ -0,0 +1,419 @@
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 ".parse" 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 a Python::Pickle::Instructions::NONE" do
15
+ expect(subject.parse(io)).to eq(
16
+ [
17
+ Python::Pickle::Instructions::Proto.new(5),
18
+ Python::Pickle::Instructions::NONE,
19
+ Python::Pickle::Instructions::STOP
20
+ ]
21
+ )
22
+ end
23
+
24
+ context "and when a block is given" do
25
+ it "must yield each parsed instruction" do
26
+ expect {|b|
27
+ subject.parse(io,&b)
28
+ }.to yield_successive_args(
29
+ Python::Pickle::Instructions::Proto.new(5),
30
+ Python::Pickle::Instructions::NONE,
31
+ Python::Pickle::Instructions::STOP
32
+ )
33
+ end
34
+ end
35
+ end
36
+
37
+ context "and it contains a serialized True" do
38
+ let(:file) { File.join(fixtures_dir,'true_v5.pkl') }
39
+
40
+ it "must return a Python::Pickle::Instructions::Int containing a true value" do
41
+ expect(subject.parse(io)).to eq(
42
+ [
43
+ Python::Pickle::Instructions::Proto.new(5),
44
+ Python::Pickle::Instructions::NEWTRUE,
45
+ Python::Pickle::Instructions::STOP
46
+ ]
47
+ )
48
+ end
49
+
50
+ context "and when a block is given" do
51
+ it "must yield each parsed instruction" do
52
+ expect {|b|
53
+ subject.parse(io,&b)
54
+ }.to yield_successive_args(
55
+ Python::Pickle::Instructions::Proto.new(5),
56
+ Python::Pickle::Instructions::NEWTRUE,
57
+ Python::Pickle::Instructions::STOP
58
+ )
59
+ end
60
+ end
61
+ end
62
+
63
+ context "and it contains a serialized False" do
64
+ let(:file) { File.join(fixtures_dir,'false_v5.pkl') }
65
+
66
+ it "must return a Python::Pickle::Instructions::Int with a false value" do
67
+ expect(subject.parse(io)).to eq(
68
+ [
69
+ Python::Pickle::Instructions::Proto.new(5),
70
+ Python::Pickle::Instructions::NEWFALSE,
71
+ Python::Pickle::Instructions::STOP
72
+ ]
73
+ )
74
+ end
75
+
76
+ context "and when a block is given" do
77
+ it "must yield each parsed instruction" do
78
+ expect {|b|
79
+ subject.parse(io,&b)
80
+ }.to yield_successive_args(
81
+ Python::Pickle::Instructions::Proto.new(5),
82
+ Python::Pickle::Instructions::NEWFALSE,
83
+ Python::Pickle::Instructions::STOP
84
+ )
85
+ end
86
+ end
87
+ end
88
+
89
+ context "and it contains a serialized integer" do
90
+ let(:file) { File.join(fixtures_dir,'int_v5.pkl') }
91
+
92
+ it "must return a Python::Pickle::Instructions::BinInt1 with the decoded number" do
93
+ expect(subject.parse(io)).to eq(
94
+ [
95
+ Python::Pickle::Instructions::Proto.new(5),
96
+ Python::Pickle::Instructions::BinInt1.new(42),
97
+ Python::Pickle::Instructions::STOP
98
+ ]
99
+ )
100
+ end
101
+
102
+ context "and when a block is given" do
103
+ it "must yield each parsed instruction" do
104
+ expect {|b|
105
+ subject.parse(io,&b)
106
+ }.to yield_successive_args(
107
+ Python::Pickle::Instructions::Proto.new(5),
108
+ Python::Pickle::Instructions::BinInt1.new(42),
109
+ Python::Pickle::Instructions::STOP
110
+ )
111
+ end
112
+ end
113
+ end
114
+
115
+ context "and it contains a long integer" do
116
+ let(:file) { File.join(fixtures_dir,'long_v5.pkl') }
117
+
118
+ it "must return a Python::Pickle::Instructions::Long with the decoded number" do
119
+ expect(subject.parse(io)).to eq(
120
+ [
121
+ Python::Pickle::Instructions::Proto.new(5),
122
+ Python::Pickle::Instructions::Frame.new(12),
123
+ Python::Pickle::Instructions::Long1.new(9,18446744073709551615),
124
+ Python::Pickle::Instructions::STOP
125
+ ]
126
+ )
127
+ end
128
+
129
+ context "and when a block is given" do
130
+ it "must yield each parsed instruction" do
131
+ expect {|b|
132
+ subject.parse(io,&b)
133
+ }.to yield_successive_args(
134
+ Python::Pickle::Instructions::Proto.new(5),
135
+ Python::Pickle::Instructions::Frame.new(12),
136
+ Python::Pickle::Instructions::Long1.new(9,18446744073709551615),
137
+ Python::Pickle::Instructions::STOP
138
+ )
139
+ end
140
+ end
141
+ end
142
+
143
+ context "and it contains a serialized floating-point number" do
144
+ let(:float) { 3.141592653589793 }
145
+ let(:file) { File.join(fixtures_dir,'float_v5.pkl') }
146
+
147
+ it "must return a Python::Pickle::Instructions::BinFloat with the decoded number" do
148
+ expect(subject.parse(io)).to eq(
149
+ [
150
+ Python::Pickle::Instructions::Proto.new(5),
151
+ Python::Pickle::Instructions::Frame.new(10),
152
+ Python::Pickle::Instructions::BinFloat.new(float),
153
+ Python::Pickle::Instructions::STOP
154
+ ]
155
+ )
156
+ end
157
+
158
+ context "and when a block is given" do
159
+ it "must yield each parsed instruction" do
160
+ expect {|b|
161
+ subject.parse(io,&b)
162
+ }.to yield_successive_args(
163
+ Python::Pickle::Instructions::Proto.new(5),
164
+ Python::Pickle::Instructions::Frame.new(10),
165
+ Python::Pickle::Instructions::BinFloat.new(float),
166
+ Python::Pickle::Instructions::STOP
167
+ )
168
+ end
169
+ end
170
+ end
171
+
172
+ context "and it contains a plain string" do
173
+ let(:string) { "ABC" }
174
+ let(:length) { string.bytesize }
175
+ let(:file) { File.join(fixtures_dir,'str_v5.pkl') }
176
+
177
+ it "must return a Python::Pickle::Instructions::ShortBinUnicode with the decoded number" do
178
+ expect(subject.parse(io)).to eq(
179
+ [
180
+ Python::Pickle::Instructions::Proto.new(5),
181
+ Python::Pickle::Instructions::Frame.new(7),
182
+ Python::Pickle::Instructions::ShortBinUnicode.new(length,string),
183
+ Python::Pickle::Instructions::MEMOIZE,
184
+ Python::Pickle::Instructions::STOP
185
+ ]
186
+ )
187
+ end
188
+
189
+ context "and when a block is given" do
190
+ it "must yield each parsed instruction" do
191
+ expect {|b|
192
+ subject.parse(io,&b)
193
+ }.to yield_successive_args(
194
+ Python::Pickle::Instructions::Proto.new(5),
195
+ Python::Pickle::Instructions::Frame.new(7),
196
+ Python::Pickle::Instructions::ShortBinUnicode.new(length,string),
197
+ Python::Pickle::Instructions::MEMOIZE,
198
+ Python::Pickle::Instructions::STOP
199
+ )
200
+ end
201
+ end
202
+ end
203
+
204
+ context "and it contains a UTF string" do
205
+ let(:string) { "ABC\u265E\u265F\u{1F600}" }
206
+ let(:length) { string.bytesize }
207
+ let(:file) { File.join(fixtures_dir,'unicode_str_v5.pkl') }
208
+
209
+ it "must return a Python::Pickle::Instructions::ShortBinUnicode with the decoded number" do
210
+ expect(subject.parse(io)).to eq(
211
+ [
212
+ Python::Pickle::Instructions::Proto.new(5),
213
+ Python::Pickle::Instructions::Frame.new(17),
214
+ Python::Pickle::Instructions::ShortBinUnicode.new(length,string),
215
+ Python::Pickle::Instructions::MEMOIZE,
216
+ Python::Pickle::Instructions::STOP
217
+ ]
218
+ )
219
+ end
220
+
221
+ context "and when a block is given" do
222
+ it "must yield each parsed instruction" do
223
+ expect {|b|
224
+ subject.parse(io,&b)
225
+ }.to yield_successive_args(
226
+ Python::Pickle::Instructions::Proto.new(5),
227
+ Python::Pickle::Instructions::Frame.new(17),
228
+ Python::Pickle::Instructions::ShortBinUnicode.new(length,string),
229
+ Python::Pickle::Instructions::MEMOIZE,
230
+ Python::Pickle::Instructions::STOP
231
+ )
232
+ end
233
+ end
234
+ end
235
+
236
+ context "and it contains a list type" do
237
+ let(:file) { File.join(fixtures_dir,'list_v5.pkl') }
238
+
239
+ it "must return an Array of parsed instructions" do
240
+ expect(subject.parse(io)).to eq(
241
+ [
242
+ Python::Pickle::Instructions::Proto.new(5),
243
+ Python::Pickle::Instructions::Frame.new(16),
244
+ Python::Pickle::Instructions::EMPTY_LIST,
245
+ Python::Pickle::Instructions::MEMOIZE,
246
+ Python::Pickle::Instructions::MARK,
247
+ Python::Pickle::Instructions::NONE,
248
+ Python::Pickle::Instructions::NEWTRUE,
249
+ Python::Pickle::Instructions::NEWFALSE,
250
+ Python::Pickle::Instructions::BinInt1.new(42),
251
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"ABC"),
252
+ Python::Pickle::Instructions::MEMOIZE,
253
+ Python::Pickle::Instructions::APPENDS,
254
+ Python::Pickle::Instructions::STOP
255
+ ]
256
+ )
257
+ end
258
+
259
+ context "and when a block is given" do
260
+ it "must yield each parsed instruction" do
261
+ expect {|b|
262
+ subject.parse(io,&b)
263
+ }.to yield_successive_args(
264
+ Python::Pickle::Instructions::Proto.new(5),
265
+ Python::Pickle::Instructions::Frame.new(16),
266
+ Python::Pickle::Instructions::EMPTY_LIST,
267
+ Python::Pickle::Instructions::MEMOIZE,
268
+ Python::Pickle::Instructions::MARK,
269
+ Python::Pickle::Instructions::NONE,
270
+ Python::Pickle::Instructions::NEWTRUE,
271
+ Python::Pickle::Instructions::NEWFALSE,
272
+ Python::Pickle::Instructions::BinInt1.new(42),
273
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"ABC"),
274
+ Python::Pickle::Instructions::MEMOIZE,
275
+ Python::Pickle::Instructions::APPENDS,
276
+ Python::Pickle::Instructions::STOP
277
+ )
278
+ end
279
+ end
280
+ end
281
+
282
+ context "and it contains a dict type" do
283
+ let(:file) { File.join(fixtures_dir,'dict_v5.pkl') }
284
+
285
+ it "must return an Array of parsed instructions" do
286
+ expect(subject.parse(io)).to eq(
287
+ [
288
+ Python::Pickle::Instructions::Proto.new(5),
289
+ Python::Pickle::Instructions::Frame.new(16),
290
+ Python::Pickle::Instructions::EMPTY_DICT,
291
+ Python::Pickle::Instructions::MEMOIZE,
292
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"foo"),
293
+ Python::Pickle::Instructions::MEMOIZE,
294
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"bar"),
295
+ Python::Pickle::Instructions::MEMOIZE,
296
+ Python::Pickle::Instructions::SETITEM,
297
+ Python::Pickle::Instructions::STOP
298
+ ]
299
+ )
300
+ end
301
+
302
+ context "and when a block is given" do
303
+ it "must yield each parsed instruction" do
304
+ expect {|b|
305
+ subject.parse(io,&b)
306
+ }.to yield_successive_args(
307
+ Python::Pickle::Instructions::Proto.new(5),
308
+ Python::Pickle::Instructions::Frame.new(16),
309
+ Python::Pickle::Instructions::EMPTY_DICT,
310
+ Python::Pickle::Instructions::MEMOIZE,
311
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"foo"),
312
+ Python::Pickle::Instructions::MEMOIZE,
313
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"bar"),
314
+ Python::Pickle::Instructions::MEMOIZE,
315
+ Python::Pickle::Instructions::SETITEM,
316
+ Python::Pickle::Instructions::STOP
317
+ )
318
+ end
319
+ end
320
+ end
321
+
322
+ context "and it contains an object type" do
323
+ let(:file) { File.join(fixtures_dir,'object_v5.pkl') }
324
+
325
+ it "must return an Array of parsed instructions" do
326
+ expect(subject.parse(io)).to eq(
327
+ [
328
+ Python::Pickle::Instructions::Proto.new(5),
329
+ Python::Pickle::Instructions::Frame.new(44),
330
+ Python::Pickle::Instructions::ShortBinUnicode.new(8,'__main__'),
331
+ Python::Pickle::Instructions::MEMOIZE,
332
+ Python::Pickle::Instructions::ShortBinUnicode.new(7,'MyClass'),
333
+ Python::Pickle::Instructions::MEMOIZE,
334
+ Python::Pickle::Instructions::STACK_GLOBAL,
335
+ Python::Pickle::Instructions::MEMOIZE,
336
+ Python::Pickle::Instructions::EMPTY_TUPLE,
337
+ Python::Pickle::Instructions::NEWOBJ,
338
+ Python::Pickle::Instructions::MEMOIZE,
339
+ Python::Pickle::Instructions::EMPTY_DICT,
340
+ Python::Pickle::Instructions::MEMOIZE,
341
+ Python::Pickle::Instructions::MARK,
342
+ Python::Pickle::Instructions::ShortBinUnicode.new(1,'x'),
343
+ Python::Pickle::Instructions::MEMOIZE,
344
+ Python::Pickle::Instructions::BinInt1.new(65),
345
+ Python::Pickle::Instructions::ShortBinUnicode.new(1,'y'),
346
+ Python::Pickle::Instructions::MEMOIZE,
347
+ Python::Pickle::Instructions::BinInt1.new(66),
348
+ Python::Pickle::Instructions::SETITEMS,
349
+ Python::Pickle::Instructions::BUILD,
350
+ Python::Pickle::Instructions::STOP
351
+ ]
352
+ )
353
+ end
354
+
355
+ context "and when a block is given" do
356
+ it "must yield each parsed instruction" do
357
+ expect {|b|
358
+ subject.parse(io,&b)
359
+ }.to yield_successive_args(
360
+ Python::Pickle::Instructions::Proto.new(5),
361
+ Python::Pickle::Instructions::Frame.new(44),
362
+ Python::Pickle::Instructions::ShortBinUnicode.new(8,'__main__'),
363
+ Python::Pickle::Instructions::MEMOIZE,
364
+ Python::Pickle::Instructions::ShortBinUnicode.new(7,'MyClass'),
365
+ Python::Pickle::Instructions::MEMOIZE,
366
+ Python::Pickle::Instructions::STACK_GLOBAL,
367
+ Python::Pickle::Instructions::MEMOIZE,
368
+ Python::Pickle::Instructions::EMPTY_TUPLE,
369
+ Python::Pickle::Instructions::NEWOBJ,
370
+ Python::Pickle::Instructions::MEMOIZE,
371
+ Python::Pickle::Instructions::EMPTY_DICT,
372
+ Python::Pickle::Instructions::MEMOIZE,
373
+ Python::Pickle::Instructions::MARK,
374
+ Python::Pickle::Instructions::ShortBinUnicode.new(1,'x'),
375
+ Python::Pickle::Instructions::MEMOIZE,
376
+ Python::Pickle::Instructions::BinInt1.new(65),
377
+ Python::Pickle::Instructions::ShortBinUnicode.new(1,'y'),
378
+ Python::Pickle::Instructions::MEMOIZE,
379
+ Python::Pickle::Instructions::BinInt1.new(66),
380
+ Python::Pickle::Instructions::SETITEMS,
381
+ Python::Pickle::Instructions::BUILD,
382
+ Python::Pickle::Instructions::STOP
383
+ )
384
+ end
385
+ end
386
+ end
387
+
388
+ context "and it contains a bytearray type" do
389
+ let(:file) { File.join(fixtures_dir,'bytearray_v5.pkl') }
390
+
391
+ it "must return an Array of parsed instructions" do
392
+ expect(subject.parse(io)).to eq(
393
+ [
394
+ Python::Pickle::Instructions::Proto.new(5),
395
+ Python::Pickle::Instructions::Frame.new(14),
396
+ Python::Pickle::Instructions::ByteArray8.new(3,'ABC'),
397
+ Python::Pickle::Instructions::MEMOIZE,
398
+ Python::Pickle::Instructions::STOP
399
+ ]
400
+ )
401
+ end
402
+
403
+ context "and when a block is given" do
404
+ it "must yield each parsed instruction" do
405
+ expect {|b|
406
+ subject.parse(io,&b)
407
+ }.to yield_successive_args(
408
+ Python::Pickle::Instructions::Proto.new(5),
409
+ Python::Pickle::Instructions::Frame.new(14),
410
+ Python::Pickle::Instructions::ByteArray8.new(3,'ABC'),
411
+ Python::Pickle::Instructions::MEMOIZE,
412
+ Python::Pickle::Instructions::STOP
413
+ )
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end
@@ -0,0 +1,163 @@
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 ".parse" do
8
+ let(:path) { File.join(fixtures_dir,'dict_v4.pkl') }
9
+ let(:file) { File.open(path,'rb') }
10
+
11
+ it "must return an Array of parsed instructions" do
12
+ expect(subject.parse(file)).to eq(
13
+ [
14
+ Python::Pickle::Instructions::Proto.new(4),
15
+ Python::Pickle::Instructions::Frame.new(16),
16
+ Python::Pickle::Instructions::EMPTY_DICT,
17
+ Python::Pickle::Instructions::MEMOIZE,
18
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"foo"),
19
+ Python::Pickle::Instructions::MEMOIZE,
20
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"bar"),
21
+ Python::Pickle::Instructions::MEMOIZE,
22
+ Python::Pickle::Instructions::SETITEM,
23
+ Python::Pickle::Instructions::STOP
24
+ ]
25
+ )
26
+ end
27
+
28
+ context "and when a block is given" do
29
+ it "must yield each parsed instruction" do
30
+ expect {|b|
31
+ subject.parse(file,&b)
32
+ }.to yield_successive_args(
33
+ Python::Pickle::Instructions::Proto.new(4),
34
+ Python::Pickle::Instructions::Frame.new(16),
35
+ Python::Pickle::Instructions::EMPTY_DICT,
36
+ Python::Pickle::Instructions::MEMOIZE,
37
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"foo"),
38
+ Python::Pickle::Instructions::MEMOIZE,
39
+ Python::Pickle::Instructions::ShortBinUnicode.new(3,"bar"),
40
+ Python::Pickle::Instructions::MEMOIZE,
41
+ Python::Pickle::Instructions::SETITEM,
42
+ Python::Pickle::Instructions::STOP
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ describe ".load" do
49
+ let(:path) { File.join(fixtures_dir,'dict_v4.pkl') }
50
+ let(:data) { File.binread(path) }
51
+
52
+ it "must deserialize the Python Pickle data in the given file" do
53
+ expect(subject.load(data)).to eq({"foo" => "bar"})
54
+ end
55
+ end
56
+
57
+ describe ".load_file" do
58
+ let(:path) { File.join(fixtures_dir,'dict_v4.pkl') }
59
+
60
+ it "must deserialize the Python Pickle data in the given file" do
61
+ expect(subject.load_file(path)).to eq({"foo" => "bar"})
62
+ end
63
+ end
64
+
65
+ describe ".dump" do
66
+ end
67
+
68
+ describe ".infer_protocol_version" do
69
+ context "when given a Pickle protocol 0 stream" do
70
+ let(:file) { File.join(fixtures_dir,'dict_v0.pkl') }
71
+ let(:io) { File.open(file) }
72
+
73
+ it "must return 0" do
74
+ expect(subject.infer_protocol_version(io)).to eq(0)
75
+ end
76
+
77
+ it "must unread the read bytes" do
78
+ subject.infer_protocol_version(io)
79
+
80
+ expect(io.pos).to eq(0)
81
+ end
82
+ end
83
+
84
+ context "when given a Pickle protocol 1 stream" do
85
+ let(:file) { File.join(fixtures_dir,'dict_v1.pkl') }
86
+ let(:io) { File.open(file) }
87
+
88
+ it "must return 1" do
89
+ expect(subject.infer_protocol_version(io)).to eq(1)
90
+ end
91
+
92
+ it "must unread the read bytes" do
93
+ subject.infer_protocol_version(io)
94
+
95
+ expect(io.pos).to eq(0)
96
+ end
97
+ end
98
+
99
+ context "when given a Pickle protocol 2 stream" do
100
+ let(:file) { File.join(fixtures_dir,'dict_v2.pkl') }
101
+ let(:io) { File.open(file) }
102
+
103
+ it "must return 2" do
104
+ expect(subject.infer_protocol_version(io)).to eq(2)
105
+ end
106
+
107
+ it "must unread the read bytes" do
108
+ subject.infer_protocol_version(io)
109
+
110
+ expect(io.pos).to eq(0)
111
+ expect(io.read(2)).to eq("\x80\x02".b)
112
+ end
113
+ end
114
+
115
+ context "when given a Pickle protocol 3 stream" do
116
+ let(:file) { File.join(fixtures_dir,'dict_v3.pkl') }
117
+ let(:io) { File.open(file) }
118
+
119
+ it "must return 3" do
120
+ expect(subject.infer_protocol_version(io)).to eq(3)
121
+ end
122
+
123
+ it "must unread the read bytes" do
124
+ subject.infer_protocol_version(io)
125
+
126
+ expect(io.pos).to eq(0)
127
+ expect(io.read(2)).to eq("\x80\x03".b)
128
+ end
129
+ end
130
+
131
+ context "when given a Pickle protocol 4 stream" do
132
+ let(:file) { File.join(fixtures_dir,'dict_v4.pkl') }
133
+ let(:io) { File.open(file) }
134
+
135
+ it "must return 4" do
136
+ expect(subject.infer_protocol_version(io)).to eq(4)
137
+ end
138
+
139
+ it "must unread the read bytes" do
140
+ subject.infer_protocol_version(io)
141
+
142
+ expect(io.pos).to eq(0)
143
+ expect(io.read(2)).to eq("\x80\x04".b)
144
+ end
145
+ end
146
+
147
+ context "when given a Pickle protocol 5 stream" do
148
+ let(:file) { File.join(fixtures_dir,'dict_v5.pkl') }
149
+ let(:io) { File.open(file) }
150
+
151
+ it "must return 5" do
152
+ expect(subject.infer_protocol_version(io)).to eq(5)
153
+ end
154
+
155
+ it "must unread the read bytes" do
156
+ subject.infer_protocol_version(io)
157
+
158
+ expect(io.pos).to eq(0)
159
+ expect(io.read(2)).to eq("\x80\x05".b)
160
+ end
161
+ end
162
+ end
163
+ end