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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +17 -0
  3. data/README.md +4 -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 +71 -39
  22. data/spec/deserializer_spec.rb +359 -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/pickle_spec.rb +61 -0
  38. data/spec/protocol0_read_instruction_examples.rb +44 -0
  39. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea86f43e4b83bacac94635b88e2c9deeb6eb4aaa553f1770e4943554389422fc
4
- data.tar.gz: 973327ecbd8f2c1df7d0ac10f37d76628745e00da9317ab655a9f12644fef441
3
+ metadata.gz: c8fe6820a4b047c2e2eb027c3dbe88b5b8d7e51d201864e9e7b726cb566d687d
4
+ data.tar.gz: b1b66aa3811a277b63f643eb4c2968bab268ab7c22aa061713c1555f8d66162b
5
5
  SHA512:
6
- metadata.gz: 8b95a73ff2ebfba7c9ca97648df7c7b3b6be14df61e0e6f4943e3e263a5f300696e79d8cf57aacc7bd9d572bcf3c00a5ec88162e1dd8cc627617afe01414499d
7
- data.tar.gz: 73a9a106cfa346d3d74835cf9369732f041fdb2faafdb8a030f1e9ec6b58831e8b7c8aee3a05bec687d11d2d4ea9d48a00d4fcf164f0c686eb84c1248b15f056
6
+ metadata.gz: b44f4bddc1cfcbdfc4d7fda78a30b9982c268701109bbb92da050c1c226a006859f3a9dacd872c9362bb8c237723b7d8401dcb28626ad9a2e49b83af3c8c32d1
7
+ data.tar.gz: 8301c1d66a60a6cb7468f32a6f801d3b0ed699d771af7625eba034aafef762815201cb12b9484976248abe84cc32278647991e1cbb76745ebea84994e5ff2f2a
data/ChangeLog.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### 0.2.0 / 2023-02-19
2
+
3
+ * Added missing support for deserializing Python `set` objects.
4
+ * Added missing support for out-of-band buffers.
5
+ * Added partial support for parsing the `PERSID` and `BINPERSID` instructions.
6
+ * Added missing support for deserializing the `INST` and `OBJ` instructions.
7
+ * Added missing support for deserializing the `EMPTY_SET`, `FROZENSET`, and
8
+ `ADDITEMS` Pickle instructions.
9
+ * Added missing support for deserializing the `NEXT_BUFFER` and
10
+ `READONLY_BUFFER` Pickle instructions.
11
+ * Map `__builtin__.set` and `builtins.set` to Ruby's `Set` class.
12
+
13
+ ### 0.1.0 / 2023-02-18
14
+
15
+ * Changed {Python::Pickle.dump} to raise a `NotImplementedError` exception.
16
+ * Fixed a typo in the method signature of {Python::Pickle.dump}.
17
+
1
18
  ### 0.1.0 / 2023-02-18
2
19
 
3
20
  * Initial release:
data/README.md CHANGED
@@ -20,9 +20,12 @@ format.
20
20
  * Supports Pickle protocol 0, protocol 1, protocol 2, protocol 3, protocol 4,
21
21
  and protocol 5.
22
22
  * Can parse both Python 2 and Python 3 Pickled data.
23
- * Supports deserializing Python `tuple` and `bytearray` objects.
23
+ * Supports deserializing Python `None`, `True`, `False`, `int`, `str`, `tuple`,
24
+ `set`, `list`, `bytearray`, and other objects.
25
+ * Supports mapping Python extension codes to Ruby classes.
24
26
  * Supports mapping Python functions to Ruby methods.
25
27
  * Supports mapping Python classes to Ruby classes.
28
+ * Supports out-of-band buffers.
26
29
 
27
30
  ## TODO
28
31
 
@@ -2,63 +2,10 @@ require 'python/pickle/py_class'
2
2
  require 'python/pickle/py_object'
3
3
  require 'python/pickle/tuple'
4
4
  require 'python/pickle/byte_array'
5
+ require 'python/pickle/instructions'
5
6
  require 'python/pickle/exceptions'
6
7
 
7
- require 'python/pickle/instructions/proto'
8
- require 'python/pickle/instructions/frame'
9
- require 'python/pickle/instructions/get'
10
- require 'python/pickle/instructions/bin_get'
11
- require 'python/pickle/instructions/long_bin_get'
12
- require 'python/pickle/instructions/mark'
13
- require 'python/pickle/instructions/pop_mark'
14
- require 'python/pickle/instructions/dup'
15
- require 'python/pickle/instructions/put'
16
- require 'python/pickle/instructions/bin_put'
17
- require 'python/pickle/instructions/pop'
18
- require 'python/pickle/instructions/memoize'
19
- require 'python/pickle/instructions/ext1'
20
- require 'python/pickle/instructions/ext2'
21
- require 'python/pickle/instructions/ext4'
22
- require 'python/pickle/instructions/none'
23
- require 'python/pickle/instructions/new_true'
24
- require 'python/pickle/instructions/new_false'
25
- require 'python/pickle/instructions/float'
26
- require 'python/pickle/instructions/bin_float'
27
- require 'python/pickle/instructions/int'
28
- require 'python/pickle/instructions/bin_int1'
29
- require 'python/pickle/instructions/long'
30
- require 'python/pickle/instructions/long1'
31
- require 'python/pickle/instructions/long4'
32
- require 'python/pickle/instructions/bin_bytes'
33
- require 'python/pickle/instructions/short_bin_bytes'
34
- require 'python/pickle/instructions/bin_bytes8'
35
- require 'python/pickle/instructions/string'
36
- require 'python/pickle/instructions/bin_string'
37
- require 'python/pickle/instructions/short_bin_string'
38
- require 'python/pickle/instructions/bin_unicode'
39
- require 'python/pickle/instructions/short_bin_unicode'
40
- require 'python/pickle/instructions/bin_unicode8'
41
- require 'python/pickle/instructions/byte_array8'
42
- require 'python/pickle/instructions/empty_list'
43
- require 'python/pickle/instructions/empty_tuple'
44
- require 'python/pickle/instructions/tuple'
45
- require 'python/pickle/instructions/empty_dict'
46
- require 'python/pickle/instructions/append'
47
- require 'python/pickle/instructions/appends'
48
- require 'python/pickle/instructions/list'
49
- require 'python/pickle/instructions/tuple1'
50
- require 'python/pickle/instructions/tuple2'
51
- require 'python/pickle/instructions/tuple3'
52
- require 'python/pickle/instructions/dict'
53
- require 'python/pickle/instructions/global'
54
- require 'python/pickle/instructions/stack_global'
55
- require 'python/pickle/instructions/new_obj'
56
- require 'python/pickle/instructions/new_obj_ex'
57
- require 'python/pickle/instructions/reduce'
58
- require 'python/pickle/instructions/build'
59
- require 'python/pickle/instructions/set_item'
60
- require 'python/pickle/instructions/set_items'
61
- require 'python/pickle/instructions/stop'
8
+ require 'set'
62
9
 
63
10
  module Python
64
11
  module Pickle
@@ -94,6 +41,11 @@ module Python
94
41
  # @return [Hash{Integer => Object}]
95
42
  attr_reader :extensions
96
43
 
44
+ # An enumerable list of out-of-band buffers.
45
+ #
46
+ # @return [Enumerator, nil]
47
+ attr_reader :buffers
48
+
97
49
  # The Python `object` class.
98
50
  OBJECT_CLASS = PyClass.new('__builtins__','object')
99
51
 
@@ -107,7 +59,10 @@ module Python
107
59
  # An optional mapping of custom Python constant names to Ruby classes
108
60
  # or methods.
109
61
  #
110
- def initialize(constants: nil, extensions: nil)
62
+ # @param [Enumerable, nil] buffers
63
+ # An enumerable list of out-of-band buffers.
64
+ #
65
+ def initialize(constants: nil, extensions: nil, buffers: nil)
111
66
  @meta_stack = []
112
67
  @stack = []
113
68
  @memo = []
@@ -120,12 +75,14 @@ module Python
120
75
 
121
76
  '__builtin__' => {
122
77
  'object' => OBJECT_CLASS,
78
+ 'set' => Set,
123
79
  'bytearray' => ByteArray
124
80
  },
125
81
 
126
82
  # Python 3.x
127
83
  'builtins' => {
128
84
  'object' => OBJECT_CLASS,
85
+ 'set' => Set,
129
86
  'bytearray' => ByteArray
130
87
  }
131
88
  }
@@ -133,6 +90,8 @@ module Python
133
90
 
134
91
  @extensions = {}
135
92
  @extensions.merge!(extensions) if extensions
93
+
94
+ @buffers = buffers.each if buffers
136
95
  end
137
96
 
138
97
  #
@@ -199,26 +158,33 @@ module Python
199
158
  Instructions::ShortBinUnicode,
200
159
  Instructions::BinUnicode8
201
160
  @stack.push(instruction.value)
202
- when Instructions::ByteArray8 then execute_byte_array8(instruction)
203
- when Instructions::EMPTY_LIST then execute_empty_list
204
- when Instructions::EMPTY_TUPLE then execute_empty_tuple
205
- when Instructions::TUPLE then execute_tuple
206
- when Instructions::EMPTY_DICT then execute_empty_dict
207
- when Instructions::APPEND then execute_append
208
- when Instructions::APPENDS then execute_appends
209
- when Instructions::LIST then execute_list
210
- when Instructions::TUPLE1 then execute_tuple1
211
- when Instructions::TUPLE2 then execute_tuple2
212
- when Instructions::TUPLE3 then execute_tuple3
213
- when Instructions::DICT then execute_dict
214
- when Instructions::Global then execute_global(instruction)
215
- when Instructions::STACK_GLOBAL then execute_stack_global
216
- when Instructions::NEWOBJ then execute_newobj
217
- when Instructions::NEWOBJ_EX then execute_newobj_ex
218
- when Instructions::REDUCE then execute_reduce
219
- when Instructions::BUILD then execute_build
220
- when Instructions::SETITEM then execute_setitem
221
- when Instructions::SETITEMS then execute_setitems
161
+ when Instructions::ByteArray8 then execute_byte_array8(instruction)
162
+ when Instructions::EMPTY_LIST then execute_empty_list
163
+ when Instructions::EMPTY_TUPLE then execute_empty_tuple
164
+ when Instructions::TUPLE then execute_tuple
165
+ when Instructions::EMPTY_DICT then execute_empty_dict
166
+ when Instructions::EMPTY_SET then execute_empty_set
167
+ when Instructions::FROZENSET then execute_frozenset
168
+ when Instructions::APPEND then execute_append
169
+ when Instructions::APPENDS then execute_appends
170
+ when Instructions::ADDITEMS then execute_additems
171
+ when Instructions::LIST then execute_list
172
+ when Instructions::TUPLE1 then execute_tuple1
173
+ when Instructions::TUPLE2 then execute_tuple2
174
+ when Instructions::TUPLE3 then execute_tuple3
175
+ when Instructions::DICT then execute_dict
176
+ when Instructions::Global then execute_global(instruction)
177
+ when Instructions::STACK_GLOBAL then execute_stack_global
178
+ when Instructions::Inst then execute_inst(instruction)
179
+ when Instructions::OBJ then execute_obj
180
+ when Instructions::NEWOBJ then execute_newobj
181
+ when Instructions::NEWOBJ_EX then execute_newobj_ex
182
+ when Instructions::REDUCE then execute_reduce
183
+ when Instructions::BUILD then execute_build
184
+ when Instructions::SETITEM then execute_setitem
185
+ when Instructions::SETITEMS then execute_setitems
186
+ when Instructions::NEXT_BUFFER then execute_next_buffer
187
+ when Instructions::READONLY_BUFFER then execute_readonly_buffer
222
188
  when Instructions::STOP
223
189
  return :halt, @stack.pop
224
190
  else
@@ -361,6 +327,29 @@ module Python
361
327
  @stack.push({})
362
328
  end
363
329
 
330
+ #
331
+ # Executes an `EMPTY_SET` instruction.
332
+ #
333
+ # @since 0.2.0
334
+ #
335
+ def execute_empty_set
336
+ @stack.push(Set.new)
337
+ end
338
+
339
+ #
340
+ # Executes a `FROZENSET` instruction.
341
+ #
342
+ # @since 0.2.0
343
+ #
344
+ def execute_frozenset
345
+ items = pop_meta_stack
346
+
347
+ set = Set.new(items)
348
+ set.freeze
349
+
350
+ @stack.push(set)
351
+ end
352
+
364
353
  #
365
354
  # Executes an `APPEND` instruction.
366
355
  #
@@ -368,11 +357,11 @@ module Python
368
357
  item = @stack.pop
369
358
  list = @stack.last
370
359
 
371
- unless list.kind_of?(Array)
360
+ unless (list.kind_of?(Array) || list.kind_of?(Set))
372
361
  raise(DeserializationError,"cannot append element #{item.inspect} onto a non-Array: #{list.inspect}")
373
362
  end
374
363
 
375
- list.push(item)
364
+ list << item
376
365
  end
377
366
 
378
367
  #
@@ -382,11 +371,34 @@ module Python
382
371
  items = pop_meta_stack
383
372
  list = @stack.last
384
373
 
385
- unless list.kind_of?(Array)
374
+ case list
375
+ when Array
376
+ list.concat(items)
377
+ when Set
378
+ items.each do |item|
379
+ list << item
380
+ end
381
+ else
386
382
  raise(DeserializationError,"cannot append elements #{items.inspect} onto a non-Array: #{list.inspect}")
387
383
  end
384
+ end
385
+
386
+ #
387
+ # Executes a `ADDITEMS` instruction.
388
+ #
389
+ # @since 0.2.0
390
+ #
391
+ def execute_additems
392
+ items = pop_meta_stack
393
+ set = @stack.last
394
+
395
+ unless set.kind_of?(Set)
396
+ raise(DeserializationError,"cannot add items #{items.inspect} to a non-Set object: #{set.inspect}")
397
+ end
388
398
 
389
- list.concat(items)
399
+ items.each do |item|
400
+ set << item
401
+ end
390
402
  end
391
403
 
392
404
  #
@@ -500,6 +512,33 @@ module Python
500
512
  @stack.push(constant)
501
513
  end
502
514
 
515
+ #
516
+ # Executes an `INST` instruction.
517
+ #
518
+ # @since 0.2.0
519
+ #
520
+ def execute_inst(instruction)
521
+ namespace = instruction.namespace
522
+ name = instruction.name
523
+ py_class = resolve_constant(namespace,name)
524
+ args = pop_meta_stack
525
+ py_object = py_class.new(*args)
526
+
527
+ @stack.push(py_object)
528
+ end
529
+
530
+ #
531
+ # Executes an `OBJ` instruction.
532
+ #
533
+ # @since 0.2.0
534
+ #
535
+ def execute_obj
536
+ py_class, *args = pop_meta_stack
537
+ py_object = py_class.new(*args)
538
+
539
+ @stack.push(py_object)
540
+ end
541
+
503
542
  #
504
543
  # Executes a `NEWOBJ` instruction.
505
544
  #
@@ -590,6 +629,29 @@ module Python
590
629
  end
591
630
  end
592
631
 
632
+ #
633
+ # Executes a `NEXT_BUFFER` instruction.
634
+ #
635
+ # @since 0.2.0
636
+ #
637
+ def execute_next_buffer
638
+ unless @buffers
639
+ raise(DeserializationError,"pickle stream includes a NEXT_BUFFER instruction, but no buffers were provided")
640
+ end
641
+
642
+ @stack.push(@buffers.next)
643
+ end
644
+
645
+ #
646
+ # Executes a `READONLY_BUFFER` instruction.
647
+ #
648
+ # @since 0.2.0
649
+ #
650
+ def execute_readonly_buffer
651
+ buffer = @stack.last
652
+ buffer.freeze
653
+ end
654
+
593
655
  end
594
656
  end
595
657
  end
@@ -0,0 +1,31 @@
1
+ require 'python/pickle/instruction'
2
+ require 'python/pickle/instructions/has_value'
3
+
4
+ module Python
5
+ module Pickle
6
+ module Instructions
7
+ #
8
+ # Represents the `BINPERSID` instruction.
9
+ #
10
+ # @note introduced in protocol 0.
11
+ #
12
+ # @since 0.2.0
13
+ #
14
+ class BinPersID < Instruction
15
+
16
+ #
17
+ # Initializes the `BINPERSID` instruction.
18
+ #
19
+ def initialize
20
+ super(:BINPERSID)
21
+ end
22
+
23
+ end
24
+
25
+ # Represents the `BINPERSID` instruction.
26
+ #
27
+ # @since 0.2.0
28
+ BINPERSID = BinPersID.new
29
+ end
30
+ end
31
+ end
@@ -1,59 +1,29 @@
1
1
  require 'python/pickle/instruction'
2
- require 'python/pickle/instructions/has_value'
2
+ require 'python/pickle/instructions/has_namespace_and_name'
3
3
 
4
4
  module Python
5
5
  module Pickle
6
6
  module Instructions
7
+ #
8
+ # Represents the `GLOBAL` instruction.
9
+ #
10
+ # @note introduced in protocol 0.
11
+ #
7
12
  class Global < Instruction
8
13
 
9
- # The global object namespace.
10
- #
11
- # @return [String]
12
- attr_reader :namespace
13
-
14
- # The global object name.
15
- #
16
- # @return [String]
17
- attr_reader :name
14
+ include HasNamespaceAndName
18
15
 
19
16
  #
20
- # Initializes the `GLOBAL` instruction.
17
+ # Initializes a `GLOBAL` instruction.
21
18
  #
22
19
  # @param [String] namespace
23
- # The namespace name for the global object.
20
+ # The namespace name for the constant.
24
21
  #
25
22
  # @param [String] name
26
- # The name of the global object.
23
+ # The name of the constant.
27
24
  #
28
25
  def initialize(namespace,name)
29
- super(:GLOBAL)
30
-
31
- @namespace = namespace
32
- @name = name
33
- end
34
-
35
- #
36
- # Compares the `GLOBAL` instruction to another instruction.
37
- #
38
- # @param [Instruction] other
39
- # The other instruction to compare against.
40
- #
41
- # @return [Boolean]
42
- # Indicates whether the other instruction matches this one.
43
- #
44
- def ==(other)
45
- super(other) && \
46
- (@namespace == other.namespace) && \
47
- (@name == other.name)
48
- end
49
-
50
- #
51
- # Converts the `GLOBAL` instructions to a String.
52
- #
53
- # @return [String]
54
- #
55
- def to_s
56
- "#{super} #{@namespace}.#{@name}"
26
+ super(:GLOBAL,namespace,name)
57
27
  end
58
28
 
59
29
  end
@@ -0,0 +1,61 @@
1
+ module Python
2
+ module Pickle
3
+ module Instructions
4
+ module HasNamespaceAndName
5
+ # The constant's namespace.
6
+ #
7
+ # @return [String]
8
+ attr_reader :namespace
9
+
10
+ # The constant name.
11
+ #
12
+ # @return [String]
13
+ attr_reader :name
14
+
15
+ #
16
+ # Initializes the instruction.
17
+ #
18
+ # @param [Symbol] opcode
19
+ # The instruction's opcode.
20
+ #
21
+ # @param [String] namespace
22
+ # The namespace name for the constant.
23
+ #
24
+ # @param [String] name
25
+ # The name of the constant.
26
+ #
27
+ def initialize(opcode,namespace,name)
28
+ super(opcode)
29
+
30
+ @namespace = namespace
31
+ @name = name
32
+ end
33
+
34
+ #
35
+ # Compares the instruction to another instruction.
36
+ #
37
+ # @param [Instruction] other
38
+ # The other instruction to compare against.
39
+ #
40
+ # @return [Boolean]
41
+ # Indicates whether the other instruction matches this one.
42
+ #
43
+ def ==(other)
44
+ super(other) && \
45
+ (@namespace == other.namespace) && \
46
+ (@name == other.name)
47
+ end
48
+
49
+ #
50
+ # Converts the instructions to a String.
51
+ #
52
+ # @return [String]
53
+ #
54
+ def to_s
55
+ "#{super} #{@namespace}.#{@name}"
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ require 'python/pickle/instruction'
2
+ require 'python/pickle/instructions/has_namespace_and_name'
3
+
4
+ module Python
5
+ module Pickle
6
+ module Instructions
7
+ #
8
+ # Represents the `INST` instruction.
9
+ #
10
+ # @note introduced in protocol 0.
11
+ #
12
+ # @since 0.2.0
13
+ #
14
+ class Inst < Instruction
15
+
16
+ include HasNamespaceAndName
17
+
18
+ #
19
+ # Initializes a `INST` instruction.
20
+ #
21
+ # @param [String] namespace
22
+ # The namespace name for the constant.
23
+ #
24
+ # @param [String] name
25
+ # The name of the constant.
26
+ #
27
+ def initialize(namespace,name)
28
+ super(:INST,namespace,name)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -6,7 +6,9 @@ module Python
6
6
  #
7
7
  # Represents a pickle `NEXT_BUFFER` instruction.
8
8
  #
9
- # @note introduced in protocol 4.
9
+ # @note introduced in protocol 5.
10
+ #
11
+ # @since 0.2.0
10
12
  #
11
13
  class NextBuffer < Instruction
12
14
 
@@ -20,6 +22,8 @@ module Python
20
22
  end
21
23
 
22
24
  # The `NEXT_BUFFER` instruction.
25
+ #
26
+ # @since 0.2.0
23
27
  NEXT_BUFFER = NextBuffer.new
24
28
  end
25
29
  end
@@ -0,0 +1,30 @@
1
+ require 'python/pickle/instruction'
2
+
3
+ module Python
4
+ module Pickle
5
+ module Instructions
6
+ #
7
+ # Represents a pickle `OBJ` instruction.
8
+ #
9
+ # @note introduced in protocol 0.
10
+ #
11
+ # @since 0.2.0
12
+ #
13
+ class Obj < Instruction
14
+
15
+ #
16
+ # Initializes the `OBJ` instruction.
17
+ #
18
+ def initialize
19
+ super(:OBJ)
20
+ end
21
+
22
+ end
23
+
24
+ # The `OBJ` instruction.
25
+ #
26
+ # @since 0.2.0
27
+ OBJ = Obj.new
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ require 'python/pickle/instruction'
2
+ require 'python/pickle/instructions/has_value'
3
+
4
+ module Python
5
+ module Pickle
6
+ module Instructions
7
+ #
8
+ # Represents the `PERSID` instruction.
9
+ #
10
+ # @note introduced in protocol 0.
11
+ #
12
+ # @since 0.2.0
13
+ #
14
+ class PersID < Instruction
15
+
16
+ include HasValue
17
+
18
+ #
19
+ # Initializes the `PERSID` instruction.
20
+ #
21
+ # @param [String] value
22
+ # The `PERSID` instruction's value.
23
+ #
24
+ def initialize(value)
25
+ super(:PERSID,value)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -8,6 +8,8 @@ module Python
8
8
  #
9
9
  # @note introduced in protocol 5.
10
10
  #
11
+ # @since 0.2.0
12
+ #
11
13
  class ReadonlyBuffer < Instruction
12
14
 
13
15
  #
@@ -20,6 +22,8 @@ module Python
20
22
  end
21
23
 
22
24
  # The `READONLY_BUFFER` instruction.
25
+ #
26
+ # @since 0.2.0
23
27
  READONLY_BUFFER = ReadonlyBuffer.new
24
28
  end
25
29
  end