Ruby の Psych.safe_load(YAML.safe_load)の引数が Psych v4.0.0 から非互換になる

さてさて、先日 YAML.load が非互換になる話を書きました。
その時に Psych.safe_load をいろいろと触ってたのですがその中で Psych.safe_load の引数もバージョンによって変わっていることに気づいたので書き溜めておきます。
先にまとめだけを書いておくと現状はこういう状況になっています。

  • Psych v3.1.0 (Ruby 2.5) 未満 -> Psych.safe_load はオプション引数だけを受け取る
  • Psych v3.1.0 (Ruby 2.6) 以降 -> Psych.safe_load はオプション引数とキーワード引数を受け取る
    • -w を付けるとオプション引数を使った時に警告が出る
  • Psych v4.0.0 (Ruby は未定) 以降 -> Psych.safe_load はキーワード引数だけを受け取る
    • 非互換になるので注意

ちなみに PsychYAML のバックエンド実装なので YAML.safe_load も同様の問題になります。

Psych v3.1.0 (Ruby 2.5) 未満

Psych v3.1.0 未満では Psych.safe_load はオプション引数だけを受け取ります。

# オプション引数だけを受け取る
def safe_load(yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil, symbolize_names: false)
  # ...
end
require "psych"
require "date"

pp Psych::VERSION  # => "3.2.0"

data = <<~EOS
default: &default
  aaa: 2020-01-1
development:
  <<: *default
EOS

# オプション引数だけを受け取る
pp Psych.safe_load(data, [Date], [], true)

Ruby 2.5 に同梱されている Psych がこれに該当します。

require "psych"

pp RUBY_VERSION     # => "2.5.9"
pp Psych::VERSION   # => "3.0.2"

Psych v3.1.0 (Ruby 2.6) 以降

Psych v3.1.0 からオプション引数だけではなくてキーワード引数も受け取るようになりました。
オプション引数とキーワード引数の両方を渡した場合はオプション引数が優先されます。

# オプション引数とキーワード引数の両方を受け取る
def 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)
  # ...
end
require "psych"
require "date"

pp Psych::VERSION  # => "3.2.0"

data = <<~EOS
default: &default
  aaa: 2020-01-1
development:
  <<: *default
EOS

# オプション引数を受け取る
pp Psych.safe_load(data, [Date], [], true)

# キーワード引数でも受け取れる
pp Psych.safe_load(data, permitted_classes: [Date], aliases: true)


# 両方渡した場合はオプション引数が優先される
# no error
pp Psych.safe_load("2020-01-1", [Date], permitted_classes: [])

# error: Tried to load unspecified class: Date (Psych::DisallowedClass)
pp Psych.safe_load("2020-01-1", [], permitted_classes: [Date])

また -w を付けてオプション引数を渡すと次のように警告が出ます。

require "psych"
require "date"

# warning: Passing permitted_classes with the 2nd argument of Psych.safe_load is deprecated. Use keyword argument like Psych.safe_load(yaml, permitted_classes: ...) instead.
pp Psych.safe_load("2020-01-1", [Date])

Ruby 2.6 以降に同梱されている Psych がこれに該当します。

require "psych"

pp RUBY_VERSION     # => "2.6.7"
pp Psych::VERSION   # => "3.1.0"

Psych v4.0.0 (Ruby の取り込みバージョンは未定) 以降

Psych v4.0.0 からオプション引数は削除されキーワード引数だけを受け取ります。

# キーワード引数だけを受け取る
def safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false)
  # ...
end
require "psych"
require "date"

pp Psych::VERSION  # => "4.0.0"

data = <<~EOS
default: &default
  aaa: 2020-01-1
development:
  <<: *default
EOS

# キーワード引数だけを受け取る
pp Psych.safe_load(data, permitted_classes: [Date], aliases: true)

# オプション引数を渡すとエラー
# error: `safe_load': wrong number of arguments (given 4, expected 1) (ArgumentError)
pp Psych.safe_load(data, [Date], [], true)

このように v4.0.0 から Psych.safe_load は非互換になったのでバージョンを上げる際は注意する必要があります。

まとめ

まとめると Psych.safe_load

  • Psych v3.1.0 (Ruby 2.5) 未満 -> オプション引数だけを受け取る
  • Psych v3.1.0 (Ruby 2.6) 以降 -> オプション引数とキーワード引数を受け取る
    • -w を付けるとオプション引数を使った時に警告が出る
  • Psych v4.0.0 (Ruby は未定) 以降 -> キーワード引数だけを受け取る
    • 非互換になるので注意

v4.0.0 では Psych.load の問題もあるんですが、 Psych.safe_load の変更もあるのでより一層注意する必要があります。