psych 3.0.3 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,9 +35,11 @@ module Psych
35
35
 
36
36
  constants.each do |const|
37
37
  konst = const_get const
38
- define_method(const.to_s.downcase) do
39
- load konst
40
- end
38
+ class_eval <<~RUBY
39
+ def #{const.to_s.downcase}
40
+ load #{konst.inspect}
41
+ end
42
+ RUBY
41
43
  end
42
44
 
43
45
  private
@@ -69,7 +71,7 @@ module Psych
69
71
  rescue
70
72
  nil
71
73
  end
72
- }.compact]
74
+ }.compact].freeze
73
75
 
74
76
  class Restricted < ClassLoader
75
77
  def initialize classes, symbols
data/lib/psych/handler.rb CHANGED
@@ -105,7 +105,7 @@ module Psych
105
105
  # - first element
106
106
  # - *ponies
107
107
  #
108
- # &ponies is the achor, *ponies is the alias. In this case, alias is
108
+ # &ponies is the anchor, *ponies is the alias. In this case, alias is
109
109
  # called with "ponies".
110
110
  def alias anchor
111
111
  end
@@ -46,8 +46,8 @@ module Psych
46
46
  # Convert this node to Ruby.
47
47
  #
48
48
  # See also Psych::Visitors::ToRuby
49
- def to_ruby
50
- Visitors::ToRuby.create.accept(self)
49
+ def to_ruby(symbolize_names: false, freeze: false)
50
+ Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze).accept(self)
51
51
  end
52
52
  alias :transform :to_ruby
53
53
 
@@ -14,16 +14,15 @@ module Psych
14
14
  |\.(nan|NaN|NAN)(?# not a number))$/x
15
15
 
16
16
  # Taken from http://yaml.org/type/int.html
17
- INTEGER = /^(?:[-+]?0b[0-1_]+ (?# base 2)
18
- |[-+]?0[0-7_]+ (?# base 8)
19
- |[-+]?(?:0|[1-9][0-9_]*) (?# base 10)
20
- |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x
17
+ INTEGER = /^(?:[-+]?0b[0-1_,]+ (?# base 2)
18
+ |[-+]?0[0-7_,]+ (?# base 8)
19
+ |[-+]?(?:0|[1-9][0-9_,]*) (?# base 10)
20
+ |[-+]?0x[0-9a-fA-F_,]+ (?# base 16))$/x
21
21
 
22
22
  attr_reader :class_loader
23
23
 
24
24
  # Create a new scanner
25
25
  def initialize class_loader
26
- @string_cache = {}
27
26
  @symbol_cache = {}
28
27
  @class_loader = class_loader
29
28
  end
@@ -31,81 +30,70 @@ module Psych
31
30
  # Tokenize +string+ returning the Ruby object
32
31
  def tokenize string
33
32
  return nil if string.empty?
34
- return string if @string_cache.key?(string)
35
33
  return @symbol_cache[string] if @symbol_cache.key?(string)
36
34
 
37
- case string
38
35
  # Check for a String type, being careful not to get caught by hash keys, hex values, and
39
36
  # special floats (e.g., -.inf).
40
- when /^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/, /\n/
41
- if string.length > 5
42
- @string_cache[string] = true
43
- return string
44
- end
37
+ if string.match?(/^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/) || string.match?(/\n/)
38
+ return string if string.length > 5
45
39
 
46
- case string
47
- when /^[^ytonf~]/i
48
- @string_cache[string] = true
40
+ if string.match?(/^[^ytonf~]/i)
49
41
  string
50
- when '~', /^null$/i
42
+ elsif string == '~' || string.match?(/^null$/i)
51
43
  nil
52
- when /^(yes|true|on)$/i
44
+ elsif string.match?(/^(yes|true|on)$/i)
53
45
  true
54
- when /^(no|false|off)$/i
46
+ elsif string.match?(/^(no|false|off)$/i)
55
47
  false
56
48
  else
57
- @string_cache[string] = true
58
49
  string
59
50
  end
60
- when TIME
51
+ elsif string.match?(TIME)
61
52
  begin
62
53
  parse_time string
63
54
  rescue ArgumentError
64
55
  string
65
56
  end
66
- when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/
57
+ elsif string.match?(/^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/)
67
58
  require 'date'
68
59
  begin
69
60
  class_loader.date.strptime(string, '%Y-%m-%d')
70
61
  rescue ArgumentError
71
62
  string
72
63
  end
73
- when /^\.inf$/i
64
+ elsif string.match?(/^\.inf$/i)
74
65
  Float::INFINITY
75
- when /^-\.inf$/i
66
+ elsif string.match?(/^-\.inf$/i)
76
67
  -Float::INFINITY
77
- when /^\.nan$/i
68
+ elsif string.match?(/^\.nan$/i)
78
69
  Float::NAN
79
- when /^:./
70
+ elsif string.match?(/^:./)
80
71
  if string =~ /^:(["'])(.*)\1/
81
72
  @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, ''))
82
73
  else
83
74
  @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, ''))
84
75
  end
85
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/
76
+ elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}$/)
86
77
  i = 0
87
78
  string.split(':').each_with_index do |n,e|
88
79
  i += (n.to_i * 60 ** (e - 2).abs)
89
80
  end
90
81
  i
91
- when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/
82
+ elsif string.match?(/^[-+]?[0-9][0-9_]*(:[0-5]?[0-9]){1,2}\.[0-9_]*$/)
92
83
  i = 0
93
84
  string.split(':').each_with_index do |n,e|
94
85
  i += (n.to_f * 60 ** (e - 2).abs)
95
86
  end
96
87
  i
97
- when FLOAT
98
- if string =~ /\A[-+]?\.\Z/
99
- @string_cache[string] = true
88
+ elsif string.match?(FLOAT)
89
+ if string.match?(/\A[-+]?\.\Z/)
100
90
  string
101
91
  else
102
92
  Float(string.gsub(/[,_]|\.([Ee]|$)/, '\1'))
103
93
  end
94
+ elsif string.match?(INTEGER)
95
+ parse_int string
104
96
  else
105
- int = parse_int string.gsub(/[,_]/, '')
106
- return int if int
107
-
108
- @string_cache[string] = true
109
97
  string
110
98
  end
111
99
  end
@@ -113,8 +101,7 @@ module Psych
113
101
  ###
114
102
  # Parse and return an int from +string+
115
103
  def parse_int string
116
- return unless INTEGER === string
117
- Integer(string)
104
+ Integer(string.gsub(/[,_]/, ''))
118
105
  end
119
106
 
120
107
  ###
@@ -1,9 +1,10 @@
1
+
1
2
  # frozen_string_literal: true
2
3
  module Psych
3
- # The version is Psych you're using
4
- VERSION = '3.0.3'
4
+ # The version of Psych you are using
5
+ VERSION = '3.3.1'
5
6
 
6
7
  if RUBY_ENGINE == 'jruby'
7
- DEFAULT_SNAKEYAML_VERSION = '1.23'.freeze
8
+ DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
8
9
  end
9
10
  end
@@ -12,39 +12,44 @@ module Psych
12
12
  ###
13
13
  # This class walks a YAML AST, converting each node to Ruby
14
14
  class ToRuby < Psych::Visitors::Visitor
15
- def self.create
15
+ def self.create(symbolize_names: false, freeze: false)
16
16
  class_loader = ClassLoader.new
17
17
  scanner = ScalarScanner.new class_loader
18
- new(scanner, class_loader)
18
+ new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze)
19
19
  end
20
20
 
21
21
  attr_reader :class_loader
22
22
 
23
- def initialize ss, class_loader
23
+ def initialize ss, class_loader, symbolize_names: false, freeze: false
24
24
  super()
25
25
  @st = {}
26
26
  @ss = ss
27
+ @load_tags = Psych.load_tags
27
28
  @domain_types = Psych.domain_types
28
29
  @class_loader = class_loader
30
+ @symbolize_names = symbolize_names
31
+ @freeze = freeze
29
32
  end
30
33
 
31
34
  def accept target
32
35
  result = super
33
- return result if @domain_types.empty? || !target.tag
34
36
 
35
- key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
36
- key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
37
+ unless @domain_types.empty? || !target.tag
38
+ key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
39
+ key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
37
40
 
38
- if @domain_types.key? key
39
- value, block = @domain_types[key]
40
- return block.call value, result
41
+ if @domain_types.key? key
42
+ value, block = @domain_types[key]
43
+ result = block.call value, result
44
+ end
41
45
  end
42
46
 
47
+ result = deduplicate(result).freeze if @freeze
43
48
  result
44
49
  end
45
50
 
46
51
  def deserialize o
47
- if klass = resolve_class(Psych.load_tags[o.tag])
52
+ if klass = resolve_class(@load_tags[o.tag])
48
53
  instance = klass.allocate
49
54
 
50
55
  if instance.respond_to?(:init_with)
@@ -124,7 +129,7 @@ module Psych
124
129
  end
125
130
 
126
131
  def visit_Psych_Nodes_Sequence o
127
- if klass = resolve_class(Psych.load_tags[o.tag])
132
+ if klass = resolve_class(@load_tags[o.tag])
128
133
  instance = klass.allocate
129
134
 
130
135
  if instance.respond_to?(:init_with)
@@ -156,8 +161,8 @@ module Psych
156
161
  end
157
162
 
158
163
  def visit_Psych_Nodes_Mapping o
159
- if Psych.load_tags[o.tag]
160
- return revive(resolve_class(Psych.load_tags[o.tag]), o)
164
+ if @load_tags[o.tag]
165
+ return revive(resolve_class(@load_tags[o.tag]), o)
161
166
  end
162
167
  return revive_hash(register(o, {}), o) unless o.tag
163
168
 
@@ -252,6 +257,8 @@ module Psych
252
257
 
253
258
  e = build_exception((resolve_class($1) || class_loader.exception),
254
259
  h.delete('message'))
260
+
261
+ e.set_backtrace h.delete('backtrace') if h.key? 'backtrace'
255
262
  init_with(e, h, o)
256
263
 
257
264
  when '!set', 'tag:yaml.org,2002:set'
@@ -320,6 +327,7 @@ module Psych
320
327
  end
321
328
 
322
329
  private
330
+
323
331
  def register node, object
324
332
  @st[node.anchor] = object if node.anchor
325
333
  object
@@ -331,13 +339,12 @@ module Psych
331
339
  list
332
340
  end
333
341
 
334
- SHOVEL = '<<'
335
- def revive_hash hash, o
342
+ def revive_hash hash, o, tagged= false
336
343
  o.children.each_slice(2) { |k,v|
337
344
  key = accept(k)
338
345
  val = accept(v)
339
346
 
340
- if key == SHOVEL && k.tag != "tag:yaml.org,2002:str"
347
+ if key == '<<' && k.tag != "tag:yaml.org,2002:str"
341
348
  case v
342
349
  when Nodes::Alias, Nodes::Mapping
343
350
  begin
@@ -359,6 +366,12 @@ module Psych
359
366
  hash[key] = val
360
367
  end
361
368
  else
369
+ if !tagged && @symbolize_names
370
+ key = key.to_sym
371
+ elsif !@freeze
372
+ key = deduplicate(key)
373
+ end
374
+
362
375
  hash[key] = val
363
376
  end
364
377
 
@@ -366,12 +379,32 @@ module Psych
366
379
  hash
367
380
  end
368
381
 
382
+ if RUBY_VERSION < '2.7'
383
+ def deduplicate key
384
+ if key.is_a?(String)
385
+ # It is important to untaint the string, otherwise it won't
386
+ # be deduplicated into an fstring, but simply frozen.
387
+ -(key.untaint)
388
+ else
389
+ key
390
+ end
391
+ end
392
+ else
393
+ def deduplicate key
394
+ if key.is_a?(String)
395
+ -key
396
+ else
397
+ key
398
+ end
399
+ end
400
+ end
401
+
369
402
  def merge_key hash, key, val
370
403
  end
371
404
 
372
405
  def revive klass, node
373
406
  s = register(node, klass.allocate)
374
- init_with(s, revive_hash({}, node), node)
407
+ init_with(s, revive_hash({}, node, true), node)
375
408
  end
376
409
 
377
410
  def init_with o, h, node
@@ -8,12 +8,26 @@ module Psych
8
8
 
9
9
  private
10
10
 
11
- DISPATCH = Hash.new do |hash, klass|
12
- hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
11
+ # @api private
12
+ def self.dispatch_cache
13
+ Hash.new do |hash, klass|
14
+ hash[klass] = :"visit_#{klass.name.gsub('::', '_')}"
15
+ end.compare_by_identity
16
+ end
17
+
18
+ if defined?(Ractor)
19
+ def dispatch
20
+ @dispatch_cache ||= (Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache)
21
+ end
22
+ else
23
+ DISPATCH = dispatch_cache
24
+ def dispatch
25
+ DISPATCH
26
+ end
13
27
  end
14
28
 
15
29
  def visit target
16
- send DISPATCH[target.class], target
30
+ send dispatch[target.class], target
17
31
  end
18
32
  end
19
33
  end
@@ -80,7 +80,7 @@ module Psych
80
80
  raise(TypeError, "Can't dump #{target.class}") unless method
81
81
 
82
82
  h[klass] = method
83
- end
83
+ end.compare_by_identity
84
84
  end
85
85
 
86
86
  def start encoding = Nodes::Stream::UTF8
@@ -181,41 +181,11 @@ module Psych
181
181
  end
182
182
 
183
183
  def visit_Exception o
184
- tag = ['!ruby/exception', o.class.name].join ':'
185
-
186
- @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
187
-
188
- {
189
- 'message' => private_iv_get(o, 'mesg'),
190
- 'backtrace' => private_iv_get(o, 'backtrace'),
191
- }.each do |k,v|
192
- next unless v
193
- @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
194
- accept v
195
- end
196
-
197
- dump_ivars o
198
-
199
- @emitter.end_mapping
184
+ dump_exception o, o.message.to_s
200
185
  end
201
186
 
202
187
  def visit_NameError o
203
- tag = ['!ruby/exception', o.class.name].join ':'
204
-
205
- @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
206
-
207
- {
208
- 'message' => o.message.to_s,
209
- 'backtrace' => private_iv_get(o, 'backtrace'),
210
- }.each do |k,v|
211
- next unless v
212
- @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
213
- accept v
214
- end
215
-
216
- dump_ivars o
217
-
218
- @emitter.end_mapping
188
+ dump_exception o, o.message.to_s
219
189
  end
220
190
 
221
191
  def visit_Regexp o
@@ -458,15 +428,6 @@ module Psych
458
428
  node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
459
429
  register(o, node)
460
430
 
461
- # Dump the elements
462
- accept 'elements'
463
- @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
464
- o.each do |k,v|
465
- accept k
466
- accept v
467
- end
468
- @emitter.end_mapping
469
-
470
431
  # Dump the ivars
471
432
  accept 'ivars'
472
433
  @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
@@ -476,6 +437,15 @@ module Psych
476
437
  end
477
438
  @emitter.end_mapping
478
439
 
440
+ # Dump the elements
441
+ accept 'elements'
442
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
443
+ o.each do |k,v|
444
+ accept k
445
+ accept v
446
+ end
447
+ @emitter.end_mapping
448
+
479
449
  @emitter.end_mapping
480
450
  else
481
451
  tag = "!ruby/hash:#{o.class}"
@@ -492,6 +462,24 @@ module Psych
492
462
  def dump_list o
493
463
  end
494
464
 
465
+ def dump_exception o, msg
466
+ tag = ['!ruby/exception', o.class.name].join ':'
467
+
468
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
469
+
470
+ if msg
471
+ @emitter.scalar 'message', nil, nil, true, false, Nodes::Scalar::ANY
472
+ accept msg
473
+ end
474
+
475
+ @emitter.scalar 'backtrace', nil, nil, true, false, Nodes::Scalar::ANY
476
+ accept o.backtrace
477
+
478
+ dump_ivars o
479
+
480
+ @emitter.end_mapping
481
+ end
482
+
495
483
  def format_time time
496
484
  if time.utc?
497
485
  time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")