psych 3.0.3.pre5-java → 3.3.1-java

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.
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
  ###