psych 3.0.3.pre5-java → 3.3.1-java

Sign up to get free protection for your applications and to get access to all the features.
data/lib/psych.rb CHANGED
@@ -10,11 +10,7 @@ when 'jruby'
10
10
  org.jruby.ext.psych.PsychLibrary.new.load(JRuby.runtime, false)
11
11
  end
12
12
  else
13
- begin
14
- require "#{RUBY_VERSION[/\d+\.\d+/]}/psych.so"
15
- rescue LoadError
16
- require 'psych.so'
17
- end
13
+ require 'psych.so'
18
14
  end
19
15
  require 'psych/nodes'
20
16
  require 'psych/streaming'
@@ -78,12 +74,15 @@ require 'psych/class_loader'
78
74
  #
79
75
  # ==== Reading from a string
80
76
  #
81
- # Psych.load("--- a") # => 'a'
82
- # Psych.load("---\n - a\n - b") # => ['a', 'b']
77
+ # Psych.safe_load("--- a") # => 'a'
78
+ # Psych.safe_load("---\n - a\n - b") # => ['a', 'b']
79
+ # # From a trusted string:
80
+ # Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
83
81
  #
84
82
  # ==== Reading from a file
85
83
  #
86
- # Psych.load_file("database.yml")
84
+ # Psych.safe_load_file("data.yml", permitted_classes: [Date])
85
+ # Psych.load_file("trusted_database.yml")
87
86
  #
88
87
  # ==== Exception handling
89
88
  #
@@ -234,9 +233,9 @@ require 'psych/class_loader'
234
233
 
235
234
  module Psych
236
235
  # The version of libyaml Psych is using
237
- LIBYAML_VERSION = Psych.libyaml_version.join '.'
236
+ LIBYAML_VERSION = Psych.libyaml_version.join('.').freeze
238
237
  # Deprecation guard
239
- NOT_GIVEN = Object.new
238
+ NOT_GIVEN = Object.new.freeze
240
239
  private_constant :NOT_GIVEN
241
240
 
242
241
  ###
@@ -268,17 +267,19 @@ module Psych
268
267
  #
269
268
  # Raises a TypeError when `yaml` parameter is NilClass
270
269
  #
271
- def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false
270
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
271
+ # YAML documents that are supplied via user input. Instead, please use the
272
+ # safe_load method.
273
+ #
274
+ def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false, freeze: false
272
275
  if legacy_filename != NOT_GIVEN
273
- warn 'warning: Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.'
276
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
274
277
  filename = legacy_filename
275
278
  end
276
279
 
277
280
  result = parse(yaml, filename: filename)
278
281
  return fallback unless result
279
- result = result.to_ruby if result
280
- symbolize_names!(result) if symbolize_names
281
- result
282
+ result.to_ruby(symbolize_names: symbolize_names, freeze: freeze)
282
283
  end
283
284
 
284
285
  ###
@@ -294,10 +295,10 @@ module Psych
294
295
  # * Hash
295
296
  #
296
297
  # Recursive data structures are not allowed by default. Arbitrary classes
297
- # can be allowed by adding those classes to the +whitelist_classes+ keyword argument. They are
298
+ # can be allowed by adding those classes to the +permitted_classes+ keyword argument. They are
298
299
  # additive. For example, to allow Date deserialization:
299
300
  #
300
- # Psych.safe_load(yaml, whitelist_classes: [Date])
301
+ # Psych.safe_load(yaml, permitted_classes: [Date])
301
302
  #
302
303
  # Now the Date class can be loaded in addition to the classes listed above.
303
304
  #
@@ -311,7 +312,7 @@ module Psych
311
312
  # Psych.safe_load yaml, aliases: true # => loads the aliases
312
313
  #
313
314
  # A Psych::DisallowedClass exception will be raised if the yaml contains a
314
- # class that isn't in the whitelist.
315
+ # class that isn't in the +permitted_classes+ list.
315
316
  #
316
317
  # A Psych::BadAlias exception will be raised if the yaml contains aliases
317
318
  # but the +aliases+ keyword argument is set to false.
@@ -325,40 +326,39 @@ module Psych
325
326
  # Psych.safe_load("---\n foo: bar") # => {"foo"=>"bar"}
326
327
  # Psych.safe_load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}
327
328
  #
328
- def self.safe_load yaml, legacy_whitelist_classes = NOT_GIVEN, legacy_whitelist_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, whitelist_classes: [], whitelist_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false
329
- if legacy_whitelist_classes != NOT_GIVEN
330
- warn 'warning: Passing whitelist_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, whitelist_classes: ...) instead.'
331
- whitelist_classes = legacy_whitelist_classes
329
+ def self.safe_load yaml, legacy_permitted_classes = NOT_GIVEN, legacy_permitted_symbols = NOT_GIVEN, legacy_aliases = NOT_GIVEN, legacy_filename = NOT_GIVEN, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false
330
+ if legacy_permitted_classes != NOT_GIVEN
331
+ warn_with_uplevel 'Passing permitted_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_classes: ...) instead.', uplevel: 1 if $VERBOSE
332
+ permitted_classes = legacy_permitted_classes
332
333
  end
333
334
 
334
- if legacy_whitelist_symbols != NOT_GIVEN
335
- warn 'warning: Passing whitelist_symbols with the 3rd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, whitelist_symbols: ...) instead.'
336
- whitelist_symbols = legacy_whitelist_symbols
335
+ if legacy_permitted_symbols != NOT_GIVEN
336
+ warn_with_uplevel 'Passing permitted_symbols with the 3rd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_symbols: ...) instead.', uplevel: 1 if $VERBOSE
337
+ permitted_symbols = legacy_permitted_symbols
337
338
  end
338
339
 
339
340
  if legacy_aliases != NOT_GIVEN
340
- warn 'warning: Passing aliases with the 4th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, aliases: ...) instead.'
341
+ warn_with_uplevel 'Passing aliases with the 4th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, aliases: ...) instead.', uplevel: 1 if $VERBOSE
341
342
  aliases = legacy_aliases
342
343
  end
343
344
 
344
345
  if legacy_filename != NOT_GIVEN
345
- warn 'warning: Passing filename with the 5th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, filename: ...) instead.'
346
+ warn_with_uplevel 'Passing filename with the 5th argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
346
347
  filename = legacy_filename
347
348
  end
348
349
 
349
350
  result = parse(yaml, filename: filename)
350
351
  return fallback unless result
351
352
 
352
- class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s),
353
- whitelist_symbols.map(&:to_s))
353
+ class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
354
+ permitted_symbols.map(&:to_s))
354
355
  scanner = ScalarScanner.new class_loader
355
356
  visitor = if aliases
356
- Visitors::ToRuby.new scanner, class_loader
357
+ Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
357
358
  else
358
- Visitors::NoAliasRuby.new scanner, class_loader
359
+ Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
359
360
  end
360
361
  result = visitor.accept result
361
- symbolize_names!(result) if symbolize_names
362
362
  result
363
363
  end
364
364
 
@@ -383,7 +383,7 @@ module Psych
383
383
  # See Psych::Nodes for more information about YAML AST.
384
384
  def self.parse yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: NOT_GIVEN
385
385
  if legacy_filename != NOT_GIVEN
386
- warn 'warning: Passing filename with the 2nd argument of Psych.parse is deprecated. Use keyword argument like Psych.parse(yaml, filename: ...) instead.'
386
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse is deprecated. Use keyword argument like Psych.parse(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
387
387
  filename = legacy_filename
388
388
  end
389
389
 
@@ -392,7 +392,7 @@ module Psych
392
392
  end
393
393
 
394
394
  if fallback != NOT_GIVEN
395
- warn 'warning: Passing the `fallback` keyword argument of Psych.parse is deprecated.'
395
+ warn_with_uplevel 'Passing the `fallback` keyword argument of Psych.parse is deprecated.', uplevel: 1 if $VERBOSE
396
396
  fallback
397
397
  else
398
398
  false
@@ -447,7 +447,7 @@ module Psych
447
447
  # See Psych::Nodes for more information about YAML AST.
448
448
  def self.parse_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, &block
449
449
  if legacy_filename != NOT_GIVEN
450
- warn 'warning: Passing filename with the 2nd argument of Psych.parse_stream is deprecated. Use keyword argument like Psych.parse_stream(yaml, filename: ...) instead.'
450
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.parse_stream is deprecated. Use keyword argument like Psych.parse_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
451
451
  filename = legacy_filename
452
452
  end
453
453
 
@@ -551,18 +551,18 @@ module Psych
551
551
  # end
552
552
  # list # => ['foo', 'bar']
553
553
  #
554
- def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: []
554
+ def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: [], **kwargs
555
555
  if legacy_filename != NOT_GIVEN
556
- warn 'warning: Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.'
556
+ warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
557
557
  filename = legacy_filename
558
558
  end
559
559
 
560
560
  result = if block_given?
561
561
  parse_stream(yaml, filename: filename) do |node|
562
- yield node.to_ruby
562
+ yield node.to_ruby(**kwargs)
563
563
  end
564
564
  else
565
- parse_stream(yaml, filename: filename).children.map(&:to_ruby)
565
+ parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) }
566
566
  end
567
567
 
568
568
  return fallback if result.is_a?(Array) && result.empty?
@@ -573,54 +573,91 @@ module Psych
573
573
  # Load the document contained in +filename+. Returns the yaml contained in
574
574
  # +filename+ as a Ruby object, or if the file is empty, it returns
575
575
  # the specified +fallback+ return value, which defaults to +false+.
576
- def self.load_file filename, fallback: false
576
+ #
577
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
578
+ # YAML documents that are supplied via user input. Instead, please use the
579
+ # safe_load_file method.
580
+ def self.load_file filename, **kwargs
581
+ File.open(filename, 'r:bom|utf-8') { |f|
582
+ self.load f, filename: filename, **kwargs
583
+ }
584
+ end
585
+
586
+ ###
587
+ # Safely loads the document contained in +filename+. Returns the yaml contained in
588
+ # +filename+ as a Ruby object, or if the file is empty, it returns
589
+ # the specified +fallback+ return value, which defaults to +false+.
590
+ # See safe_load for options.
591
+ def self.safe_load_file filename, **kwargs
577
592
  File.open(filename, 'r:bom|utf-8') { |f|
578
- self.load f, filename: filename, fallback: fallback
593
+ self.safe_load f, filename: filename, **kwargs
579
594
  }
580
595
  end
581
596
 
582
597
  # :stopdoc:
583
- @domain_types = {}
584
598
  def self.add_domain_type domain, type_tag, &block
585
599
  key = ['tag', domain, type_tag].join ':'
586
- @domain_types[key] = [key, block]
587
- @domain_types["tag:#{type_tag}"] = [key, block]
600
+ domain_types[key] = [key, block]
601
+ domain_types["tag:#{type_tag}"] = [key, block]
588
602
  end
589
603
 
590
604
  def self.add_builtin_type type_tag, &block
591
605
  domain = 'yaml.org,2002'
592
606
  key = ['tag', domain, type_tag].join ':'
593
- @domain_types[key] = [key, block]
607
+ domain_types[key] = [key, block]
594
608
  end
595
609
 
596
610
  def self.remove_type type_tag
597
- @domain_types.delete type_tag
611
+ domain_types.delete type_tag
598
612
  end
599
613
 
600
- @load_tags = {}
601
- @dump_tags = {}
602
614
  def self.add_tag tag, klass
603
- @load_tags[tag] = klass.name
604
- @dump_tags[klass] = tag
615
+ load_tags[tag] = klass.name
616
+ dump_tags[klass] = tag
605
617
  end
606
618
 
607
- def self.symbolize_names!(result)
608
- case result
609
- when Hash
610
- result.keys.each do |key|
611
- result[key.to_sym] = symbolize_names!(result.delete(key))
612
- end
613
- when Array
614
- result.map! { |r| symbolize_names!(r) }
619
+ # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower.
620
+ def self.warn_with_uplevel(message, uplevel: 1)
621
+ at = parse_caller(caller[uplevel]).join(':')
622
+ warn "#{at}: #{message}"
623
+ end
624
+
625
+ def self.parse_caller(at)
626
+ if /^(.+?):(\d+)(?::in `.*')?/ =~ at
627
+ file = $1
628
+ line = $2.to_i
629
+ [file, line]
615
630
  end
616
- result
617
631
  end
618
- private_class_method :symbolize_names!
632
+ private_class_method :warn_with_uplevel, :parse_caller
619
633
 
620
634
  class << self
621
- attr_accessor :load_tags
622
- attr_accessor :dump_tags
623
- attr_accessor :domain_types
635
+ if defined?(Ractor)
636
+ require 'forwardable'
637
+ extend Forwardable
638
+
639
+ class Config
640
+ attr_accessor :load_tags, :dump_tags, :domain_types
641
+ def initialize
642
+ @load_tags = {}
643
+ @dump_tags = {}
644
+ @domain_types = {}
645
+ end
646
+ end
647
+
648
+ def config
649
+ Ractor.current[:PsychConfig] ||= Config.new
650
+ end
651
+
652
+ def_delegators :config, :load_tags, :dump_tags, :domain_types, :load_tags=, :dump_tags=, :domain_types=
653
+ else
654
+ attr_accessor :load_tags
655
+ attr_accessor :dump_tags
656
+ attr_accessor :domain_types
657
+ end
624
658
  end
659
+ self.load_tags = {}
660
+ self.dump_tags = {}
661
+ self.domain_types = {}
625
662
  # :startdoc:
626
663
  end
@@ -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
@@ -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
  ###