2022/03/31 今回の気になった bugs.ruby のチケット

今週は正規表現マッチするときのタイムアウトを導入するチケットがマージされました。

[Feature #17837] Add support for Regexp timeouts

  • 正規表現を比較する時にタイムアウトの概念を導入するチケット
  • 色々と議論があったが最終的にはタイムアウトの時間を制御する Regexp.timeout Regexp.timeout= が追加された
  • Regexp.timeoutタイムアウトする時刻(秒)を設定する事で『正規表現を比較する時に設定したタイムアウト秒を越えると例外が発生する』ようになる
    • デフォルトだと nil になっており nil だと例外は発生しない
# デフォルトだと nil
pp Regexp.timeout
# => nil

# この場合はタイムアウトしない
/A(B|C+)+D/ =~ "A" + "C" * 28 + "X"

# タイムアウトする時刻を設定する事ができる
Regexp.timeout = 0.1
pp Regexp.timeout
# => 0.1

# 正規表現の比較に 0.1秒以上かかると例外が発生する
begin
  /A(B|C+)+D/ =~ "A" + "C" * 28 + "X"
rescue => e
  pp e
  # => #<Regexp::TimeoutError: regexp match timeout>
end
  • また Regexp.newRegexp.compile もキーワード引数 timeout: を受け取るようになたt
# デフォルトだと timeout: nil
regexp = Regexp.new("A(B|C+)+D")
regexp =~ "A" + "C" * 28 + "X"

# 正規表現ごとに個別に timeout を設定することができる
regexp2 = Regexp.new("A(B|C+)+D", timeout: 0.5)
begin
  regexp2 =~ "A" + "C" * 28 + "X"
rescue => e
  pp e
  # => #<Regexp::TimeoutError: regexp match timeout>
end

[Feature #12655] Accessing the method visibility

  • メソッドの可視性の情報として『 [:public, :protected, :private, :undefined, :overridden] を取得したい』という要望のチケット
    • looksee という gem でそういう機能を実装している
    • 今はがんばって C拡張で実装しているが RubyAPI でもほしいらしい
  • :undefined の情報を取得する手段として Module#undefined_instance_methods が新しく追加される予定
class Super
  def hoge
  end
end

class Sub
  undef hoge
end

pp Sub.undefined_instance_methods
# => [:hoge]

[Feature #15357] Proc#parameters returns incomplete type information

  • 以下のようにブロックの引数で『オプショナル引数でないのに Proc#parameters:opt になる』のは正しくないんじゃないか、というバグ報告
# これは b がオプショナル引数
pr = proc { |a, b = 2| [a,b] }

# しかし Proc#parameters は a もオプショナル引数として情報を返す
p pr.parameters  # => [[:opt, :a], [:opt, :b]]

# 同様に以下のような場合も b もオプショナル引数として情報が返ってくる
pr = proc { |a = 1, b| [a,b] }
p pr.parameters  # => [[:opt, :a], [:opt, :b]]
  • これは proc の引数は全てオプショナル引数として定義されているためである
# オプショナル引数として定義していない a に引数を渡さなくてもエラーにはならない
pr = proc { |a, b = 2| [a,b] }
p pr.call
# => [nil, 2]

# こちらも同様
pr = proc { |a = 1, b| [a,b] }
p pr.call
# => [1, nil]
  • なので報告されたコード自体は意図する挙動になっている
  • ちなみに lambda の場合は必須引数として返ってくる
pr = lambda{|a, b=2| [a,b] }
p pr.parameters  # => [[:req, :a], [:opt, :b]]

pr = lambda{|a=1, b| [a,b] }
p pr.parameters  # => [[:opt, :a], [:req, :b]]
  • また、このように lambda であるようにパラメータ情報を受け取る手段として Proc#parameterslambda: false キーワード引数が追加された
    • これは既に Ruby 3.2 で対応済み
pr = proc{|a, b=2| [a,b] }
p pr.parameters                # => [[:opt, :a], [:opt, :b]]
p pr.parameters(lambda: true)  # => [[:req, :a], [:opt, :b]]

pr = proc{|a=1, b| [a,b] }
p pr.parameters                # => [[:opt, :a], [:opt, :b]]
p pr.parameters(lambda: true)  # => [[:opt, :a], [:req, :b]]