[ruby/irb] Support IRB.conf[:BACKTRACE_FILTER]
(https://github.com/ruby/irb/pull/917) * Use 'irbtest-' instead if 'irb-' as prefix of test files. Otherwise IRB would mis-recognize exceptions raised in test files as exceptions raised in IRB itself. * Support `IRB.conf[:BACKTRACE_FILTER]`` This config allows users to customize the backtrace of exceptions raised and displayed in IRB sessions. This is useful for filtering out library frames from the backtrace. IRB expects the given value to response to `call` method and return the filtered backtrace. https://github.com/ruby/irb/commit/6f6e87d769
This commit is contained in:
parent
2a978ee047
commit
1000c27db8
40
lib/irb.rb
40
lib/irb.rb
@ -1242,27 +1242,33 @@ module IRB
|
|||||||
irb_bug = true
|
irb_bug = true
|
||||||
else
|
else
|
||||||
irb_bug = false
|
irb_bug = false
|
||||||
# This is mostly to make IRB work nicely with Rails console's backtrace filtering, which patches WorkSpace#filter_backtrace
|
# To support backtrace filtering while utilizing Exception#full_message, we need to clone
|
||||||
# In such use case, we want to filter the exception's backtrace before its displayed through Exception#full_message
|
# the exception to avoid modifying the original exception's backtrace.
|
||||||
# And we clone the exception object in order to avoid mutating the original exception
|
|
||||||
# TODO: introduce better API to expose exception backtrace externally
|
|
||||||
backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
|
|
||||||
exc = exc.clone
|
exc = exc.clone
|
||||||
exc.set_backtrace(backtrace)
|
filtered_backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
|
||||||
|
backtrace_filter = IRB.conf[:BACKTRACE_FILTER]
|
||||||
|
|
||||||
|
if backtrace_filter
|
||||||
|
if backtrace_filter.respond_to?(:call)
|
||||||
|
filtered_backtrace = backtrace_filter.call(filtered_backtrace)
|
||||||
|
else
|
||||||
|
warn "IRB.conf[:BACKTRACE_FILTER] #{backtrace_filter} should respond to `call` method"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exc.set_backtrace(filtered_backtrace)
|
||||||
end
|
end
|
||||||
|
|
||||||
if RUBY_VERSION < '3.0.0'
|
highlight = Color.colorable?
|
||||||
if STDOUT.tty?
|
|
||||||
message = exc.full_message(order: :bottom)
|
order =
|
||||||
order = :bottom
|
if RUBY_VERSION < '3.0.0'
|
||||||
else
|
STDOUT.tty? ? :bottom : :top
|
||||||
message = exc.full_message(order: :top)
|
else # '3.0.0' <= RUBY_VERSION
|
||||||
order = :top
|
:top
|
||||||
end
|
end
|
||||||
else # '3.0.0' <= RUBY_VERSION
|
|
||||||
message = exc.full_message(order: :top)
|
message = exc.full_message(order: order, highlight: highlight)
|
||||||
order = :top
|
|
||||||
end
|
|
||||||
message = convert_invalid_byte_sequence(message, exc.message.encoding)
|
message = convert_invalid_byte_sequence(message, exc.message.encoding)
|
||||||
message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
|
message = encode_with_invalid_byte_sequence(message, IRB.conf[:LC_MESSAGES].encoding) unless message.encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s)
|
||||||
message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
|
message = message.gsub(/((?:^\t.+$\n)+)/) { |m|
|
||||||
|
@ -196,7 +196,7 @@ module TestIRB
|
|||||||
end
|
end
|
||||||
|
|
||||||
def write_ruby(program)
|
def write_ruby(program)
|
||||||
@ruby_file = Tempfile.create(%w{irb- .rb})
|
@ruby_file = Tempfile.create(%w{irbtest- .rb})
|
||||||
@tmpfiles << @ruby_file
|
@tmpfiles << @ruby_file
|
||||||
@ruby_file.write(program)
|
@ruby_file.write(program)
|
||||||
@ruby_file.close
|
@ruby_file.close
|
||||||
|
@ -823,4 +823,95 @@ module TestIRB
|
|||||||
IRB::Irb.new(workspace, TestInputMethod.new)
|
IRB::Irb.new(workspace, TestInputMethod.new)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class BacktraceFilteringTest < TestIRB::IntegrationTestCase
|
||||||
|
def test_backtrace_filtering
|
||||||
|
write_ruby <<~'RUBY'
|
||||||
|
def foo
|
||||||
|
raise "error"
|
||||||
|
end
|
||||||
|
|
||||||
|
def bar
|
||||||
|
foo
|
||||||
|
end
|
||||||
|
|
||||||
|
binding.irb
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
output = run_ruby_file do
|
||||||
|
type "bar"
|
||||||
|
type "exit"
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/irbtest-.*\.rb:2:in (`|'Object#)foo': error \(RuntimeError\)/, output)
|
||||||
|
frame_traces = output.split("\n").select { |line| line.strip.match?(/from /) }.map(&:strip)
|
||||||
|
|
||||||
|
expected_traces = if RUBY_VERSION >= "3.3.0"
|
||||||
|
[
|
||||||
|
/from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/,
|
||||||
|
/from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/,
|
||||||
|
/from <internal:kernel>:\d+:in (`|'Kernel#)loop'/,
|
||||||
|
/from <internal:prelude>:\d+:in (`|'Binding#)irb'/,
|
||||||
|
/from .*\/irbtest-.*.rb:9:in [`']<main>'/
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
/from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/,
|
||||||
|
/from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/,
|
||||||
|
/from <internal:prelude>:\d+:in (`|'Binding#)irb'/,
|
||||||
|
/from .*\/irbtest-.*.rb:9:in [`']<main>'/
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
expected_traces.reverse! if RUBY_VERSION < "3.0.0"
|
||||||
|
|
||||||
|
expected_traces.each_with_index do |expected_trace, index|
|
||||||
|
assert_match(expected_trace, frame_traces[index])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_backtrace_filtering_with_backtrace_filter
|
||||||
|
write_rc <<~'RUBY'
|
||||||
|
class TestBacktraceFilter
|
||||||
|
def self.call(backtrace)
|
||||||
|
backtrace.reject { |line| line.include?("internal") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
IRB.conf[:BACKTRACE_FILTER] = TestBacktraceFilter
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
write_ruby <<~'RUBY'
|
||||||
|
def foo
|
||||||
|
raise "error"
|
||||||
|
end
|
||||||
|
|
||||||
|
def bar
|
||||||
|
foo
|
||||||
|
end
|
||||||
|
|
||||||
|
binding.irb
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
output = run_ruby_file do
|
||||||
|
type "bar"
|
||||||
|
type "exit"
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/irbtest-.*\.rb:2:in (`|'Object#)foo': error \(RuntimeError\)/, output)
|
||||||
|
frame_traces = output.split("\n").select { |line| line.strip.match?(/from /) }.map(&:strip)
|
||||||
|
|
||||||
|
expected_traces = [
|
||||||
|
/from .*\/irbtest-.*.rb:6:in (`|'Object#)bar'/,
|
||||||
|
/from .*\/irbtest-.*.rb\(irb\):1:in [`']<main>'/,
|
||||||
|
/from .*\/irbtest-.*.rb:9:in [`']<main>'/
|
||||||
|
]
|
||||||
|
|
||||||
|
expected_traces.reverse! if RUBY_VERSION < "3.0.0"
|
||||||
|
|
||||||
|
expected_traces.each_with_index do |expected_trace, index|
|
||||||
|
assert_match(expected_trace, frame_traces[index])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user