1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Be more careful exception handling in Readline [Fixes #632, #605]

Firstly wrap the entire completion_proc in a begin/rescue/end so that
even if something we're not expecting goes wrong, we don't fail.

Secondly, only throw the user out of pry if reading from readline fails
five times in a row. This avoids a transient error (like a time-out, or
a failed tab completion) from kicking you out of pry; but also avoids
the infinite looping problem that can happen if the error is not transient.
This commit is contained in:
Conrad Irwin 2012-07-04 01:30:27 -07:00
parent 94fbfabfc6
commit b5cb8b142f
2 changed files with 115 additions and 125 deletions

View file

@ -43,6 +43,7 @@ class Pry
# @param [Array<String>] commands The array of Pry commands. # @param [Array<String>] commands The array of Pry commands.
def self.build_completion_proc(target, commands=[""]) def self.build_completion_proc(target, commands=[""])
proc do |input| proc do |input|
begin
bind = target bind = target
case input case input
@ -92,12 +93,8 @@ class Pry
# Constant or class methods # Constant or class methods
receiver = $1 receiver = $1
message = Regexp.quote($2) message = Regexp.quote($2)
begin
candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind) candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind) candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
rescue RescuableException
candidates = []
end
candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e} candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
when /^(:[^:.]+)\.([^.]*)$/ when /^(:[^:.]+)\.([^.]*)$/
@ -113,11 +110,7 @@ class Pry
receiver = $1 receiver = $1
message = Regexp.quote($5) message = Regexp.quote($5)
begin
candidates = eval(receiver, bind).methods.collect{|m| m.to_s} candidates = eval(receiver, bind).methods.collect{|m| m.to_s}
rescue RescuableException
candidates = []
end
select_message(receiver, message, candidates) select_message(receiver, message, candidates)
when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/
@ -125,11 +118,7 @@ class Pry
receiver = $1 receiver = $1
message = Regexp.quote($2) message = Regexp.quote($2)
begin
candidates = eval(receiver, bind).methods.collect{|m| m.to_s} candidates = eval(receiver, bind).methods.collect{|m| m.to_s}
rescue RescuableException
candidates = []
end
select_message(receiver, message, candidates) select_message(receiver, message, candidates)
when /^(\$[^.]*)$/ when /^(\$[^.]*)$/
@ -148,20 +137,12 @@ class Pry
if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
# foo.func and foo is local var. OR # foo.func and foo is local var. OR
# Foo::Bar.func # Foo::Bar.func
begin
candidates = eval("#{receiver}.methods", bind).collect{|m| m.to_s} candidates = eval("#{receiver}.methods", bind).collect{|m| m.to_s}
rescue RescuableException
candidates = []
end
else else
# func1.func2 # func1.func2
candidates = [] candidates = []
ObjectSpace.each_object(Module){|m| ObjectSpace.each_object(Module){|m|
begin
name = m.name.to_s name = m.name.to_s
rescue RescuableException
name = ""
end
next if name != "IRB::Context" and next if name != "IRB::Context" and
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name /^(IRB|SLex|RubyLex|RubyToken)/ =~ name
@ -198,6 +179,9 @@ class Pry
(candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/) (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/)
end end
rescue RescuableException
[]
end
end end
end end

View file

@ -539,6 +539,7 @@ class Pry
# Manage switching of input objects on encountering EOFErrors # Manage switching of input objects on encountering EOFErrors
def handle_read_errors def handle_read_errors
should_retry = true should_retry = true
exception_count = 0
begin begin
yield yield
rescue EOFError rescue EOFError
@ -565,6 +566,11 @@ class Pry
# anything about it. # anything about it.
rescue RescuableException => e rescue RescuableException => e
puts "Error: #{e.message}" puts "Error: #{e.message}"
output.puts e.backtrace
exception_count += 1
if exception_count < 5
retry
end
puts "FATAL: Pry failed to get user input using `#{input}`." puts "FATAL: Pry failed to get user input using `#{input}`."
puts "To fix this you may be able to pass input and output file descriptors to pry directly. e.g." puts "To fix this you may be able to pass input and output file descriptors to pry directly. e.g."
puts " Pry.config.input = STDIN" puts " Pry.config.input = STDIN"