From: Rodrigo Rosenfeld Rosas via ruby-core <ruby-core@ml.ruby-lang.org>
To: Austin Ziegler <halostatue@gmail.com>
Cc: Ruby developers <ruby-core@ml.ruby-lang.org>,
Rodrigo Rosenfeld Rosas <rr.rosas@gmail.com>
Subject: [ruby-core:119640] Re: Behavior of raising from rescue blocks when multiple rescue blocks exist
Date: Tue, 29 Oct 2024 16:22:05 -0300 [thread overview]
Message-ID: <CAGmv+w+1vyj=+wdH7SonuXX7HzpuWETBi-Na7jKWTDz+mPXoBQ@mail.gmail.com> (raw)
In-Reply-To: <CAJ4ekQuDv7xsVH0-UHvmzMB5rFSjXLy5MgWoB5gCcUDBk3jYPQ@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 12307 bytes --]
> rescues happen exactly once per exception handling block.
Awesome, great to know that and for the explanation on why both methods
yield different results. I totally forgot RuntimeError is an StandardError.
Em ter., 29 de out. de 2024 às 15:07, Austin Ziegler <halostatue@gmail.com>
escreveu:
> RuntimeError is a descendant of StandardError, and rescues happen exactly
> once per exception handling block.
>
> That is, your first code example will never hit RuntimeError because it's
> swallowed by the implicit StandardError.
>
> Here's a class tree of Exception from my local irb:
>
> - Exception
> |- ErrorHighlight::Spotter::NonAscii
> |- IRB::Abort
> |- IRB::LoadAbort
> |- NoMemoryError
> |- ScriptError
> | |- LoadError
> | | `- Gem::LoadError
> | | |- Gem::ConflictError
> | | `- Gem::MissingSpecError
> | | `- Gem::MissingSpecVersionError
> | |- NotImplementedError
> | `- SyntaxError
> |- SecurityError
> |- SignalException
> | `- Interrupt
> |- StandardError
> | |- ArgumentError
> | | |- Gem::Requirement::BadRequirementError
> | | |- IO::Buffer::MaskError
> | | `- UncaughtThrowError
> | |- EncodingError
> | | |- Encoding::CompatibilityError
> | | |- Encoding::ConverterNotFoundError
> | | |- Encoding::InvalidByteSequenceError
> | | `- Encoding::UndefinedConversionError
> | |- FiberError
> | |- Fiddle::Error
> | | |- Fiddle::ClearedReferenceError
> | | `- Fiddle::DLError
> | |- Gem::Molinillo::ResolverError
> | | |- Gem::Molinillo::CircularDependencyError
> | | |- Gem::Molinillo::NoSuchDependencyError
> | | `- Gem::Molinillo::VersionConflict
> | |- Gem::TSort::Cyclic
> | |- IOError
> | | |- EOFError
> | | `- IO::TimeoutError
> | |- IRB::CantChangeBinding
> | |- IRB::CantReturnToNormalMode
> | |- IRB::Command::CommandArgumentError
> | |- IRB::IllegalParameter
> | |- IRB::IrbAlreadyDead
> | |- IRB::IrbSwitchedToCurrentThread
> | |- IRB::NoSuchJob
> | |- IRB::RubyLex::TerminateLineInput
> | |- IRB::SourceFinder::EvaluationError
> | |- IRB::UndefinedPromptMode
> | |- IRB::UnrecognizedSwitch
> | |- IndexError
> | | |- KeyError
> | | `- StopIteration
> | | |- ClosedQueueError
> | | `- Ractor::ClosedError
> | |- JSON::JSONError
> | | |- JSON::GeneratorError
> | | |- JSON::MissingUnicodeSupport
> | | `- JSON::ParserError
> | | `- JSON::NestingError
> | | `- JSON::CircularDatastructure
> | |- LocalJumpError
> | |- Math::DomainError
> | |- NameError
> | | `- NoMethodError
> | |- NoMatchingPatternError
> | | `- NoMatchingPatternKeyError
> | |- RangeError
> | | `- FloatDomainError
> | |- RegexpError
> | | `- Regexp::TimeoutError
> | |- Reline::ConfigEncodingConversionError
> | |- Reline::Terminfo::TerminfoError
> | |- Ripper::TokenPattern::Error
> | | |- Ripper::TokenPattern::CompileError
> | | `- Ripper::TokenPattern::MatchError
> | |- RuntimeError
> | | |- FrozenError
> | | |- Gem::Exception
> | | | |- Gem::CommandLineError
> | | | |- Gem::DependencyError
> | | | | |- Gem::DependencyResolutionError
> | | | | `- Gem::UnsatisfiableDependencyError
> | | | |- Gem::DependencyRemovalException
> | | | |- Gem::DocumentError
> | | | |- Gem::EndOfYAMLException
> | | | |- Gem::FilePermissionError
> | | | |- Gem::FormatException
> | | | |- Gem::GemNotFoundException
> | | | | `- Gem::SpecificGemNotFoundException
> | | | |- Gem::GemNotInHomeException
> | | | |- Gem::ImpossibleDependenciesError
> | | | |- Gem::InstallError
> | | | | `- Gem::RuntimeRequirementNotMetError
> | | | |- Gem::InvalidSpecificationException
> | | | |- Gem::OperationNotSupportedError
> | | | |- Gem::RemoteError
> | | | |- Gem::RemoteInstallationCancelled
> | | | |- Gem::RemoteInstallationSkipped
> | | | |- Gem::RemoteSourceException
> | | | |- Gem::RequestSet::Lockfile::ParseError
> | | | |- Gem::RubyVersionMismatch
> | | | |- Gem::UninstallError
> | | | |- Gem::UnknownCommandError
> | | | |- Gem::VerificationError
> | | | `- Gem::WebauthnVerificationError
> | | |- IO::Buffer::AccessError
> | | |- IO::Buffer::AllocationError
> | | |- IO::Buffer::InvalidatedError
> | | |- IO::Buffer::LockedError
> | | |- RDoc::Error
> | | |- Ractor::Error
> | | | |- Ractor::IsolationError
> | | | |- Ractor::MovedError
> | | | |- Ractor::RemoteError
> | | | `- Ractor::UnsafeError
> | | `- Reline::Config::InvalidInputrc
> | |- SystemCallError
> | | |- Errno::E2BIG
> | | |- Errno::EACCES
> | | |- Errno::EADDRINUSE
> | | |- Errno::EADDRNOTAVAIL
> | | |- Errno::EAFNOSUPPORT
> | | |- Errno::EAGAIN
> | | | |- IO::EAGAINWaitReadable
> | | | `- IO::EAGAINWaitWritable
> | | |- Errno::EALREADY
> | | |- Errno::EAUTH
> | | |- Errno::EBADARCH
> | | |- Errno::EBADEXEC
> | | |- Errno::EBADF
> | | |- Errno::EBADMACHO
> | | |- Errno::EBADMSG
> | | |- Errno::EBADRPC
> | | |- Errno::EBUSY
> | | |- Errno::ECANCELED
> | | |- Errno::ECHILD
> | | |- Errno::ECONNABORTED
> | | |- Errno::ECONNREFUSED
> | | |- Errno::ECONNRESET
> | | |- Errno::EDEADLK
> | | |- Errno::EDESTADDRREQ
> | | |- Errno::EDEVERR
> | | |- Errno::EDOM
> | | |- Errno::EDQUOT
> | | |- Errno::EEXIST
> | | |- Errno::EFAULT
> | | |- Errno::EFBIG
> | | |- Errno::EFTYPE
> | | |- Errno::EHOSTDOWN
> | | |- Errno::EHOSTUNREACH
> | | |- Errno::EIDRM
> | | |- Errno::EILSEQ
> | | |- Errno::EINPROGRESS
> | | | |- IO::EINPROGRESSWaitReadable
> | | | `- IO::EINPROGRESSWaitWritable
> | | |- Errno::EINTR
> | | |- Errno::EINVAL
> | | |- Errno::EIO
> | | |- Errno::EISCONN
> | | |- Errno::EISDIR
> | | |- Errno::ELAST
> | | |- Errno::ELOOP
> | | |- Errno::EMFILE
> | | |- Errno::EMLINK
> | | |- Errno::EMSGSIZE
> | | |- Errno::EMULTIHOP
> | | |- Errno::ENAMETOOLONG
> | | |- Errno::ENEEDAUTH
> | | |- Errno::ENETDOWN
> | | |- Errno::ENETRESET
> | | |- Errno::ENETUNREACH
> | | |- Errno::ENFILE
> | | |- Errno::ENOATTR
> | | |- Errno::ENOBUFS
> | | |- Errno::ENODATA
> | | |- Errno::ENODEV
> | | |- Errno::ENOENT
> | | |- Errno::ENOEXEC
> | | |- Errno::ENOLCK
> | | |- Errno::ENOLINK
> | | |- Errno::ENOMEM
> | | |- Errno::ENOMSG
> | | |- Errno::ENOPOLICY
> | | |- Errno::ENOPROTOOPT
> | | |- Errno::ENOSPC
> | | |- Errno::ENOSR
> | | |- Errno::ENOSTR
> | | |- Errno::ENOSYS
> | | |- Errno::ENOTBLK
> | | |- Errno::ENOTCONN
> | | |- Errno::ENOTDIR
> | | |- Errno::ENOTEMPTY
> | | |- Errno::ENOTRECOVERABLE
> | | |- Errno::ENOTSOCK
> | | |- Errno::ENOTSUP
> | | |- Errno::ENOTTY
> | | |- Errno::ENXIO
> | | |- Errno::EOPNOTSUPP
> | | |- Errno::EOVERFLOW
> | | |- Errno::EOWNERDEAD
> | | |- Errno::EPERM
> | | |- Errno::EPFNOSUPPORT
> | | |- Errno::EPIPE
> | | |- Errno::EPROCLIM
> | | |- Errno::EPROCUNAVAIL
> | | |- Errno::EPROGMISMATCH
> | | |- Errno::EPROGUNAVAIL
> | | |- Errno::EPROTO
> | | |- Errno::EPROTONOSUPPORT
> | | |- Errno::EPROTOTYPE
> | | |- Errno::EPWROFF
> | | |- Errno::ERANGE
> | | |- Errno::EREMOTE
> | | |- Errno::EROFS
> | | |- Errno::ERPCMISMATCH
> | | |- Errno::ESHLIBVERS
> | | |- Errno::ESHUTDOWN
> | | |- Errno::ESOCKTNOSUPPORT
> | | |- Errno::ESPIPE
> | | |- Errno::ESRCH
> | | |- Errno::ESTALE
> | | |- Errno::ETIME
> | | |- Errno::ETIMEDOUT
> | | |- Errno::ETOOMANYREFS
> | | |- Errno::ETXTBSY
> | | |- Errno::EUSERS
> | | |- Errno::EXDEV
> | | `- Errno::NOERROR
> | |- ThreadError
> | |- TypeError
> | `- ZeroDivisionError
> |- SystemExit
> | `- Gem::SystemExitException
> |- SystemStackError
> `- fatal
>
> -a
>
> On Tue, Oct 29, 2024 at 8:48 AM Rodrigo Rosenfeld Rosas via ruby-core <
> ruby-core@ml.ruby-lang.org> wrote:
>
>> Hello, I couldn't find any documentation about the subject, so I thought
>> this behavior should be probably documented.
>>
>> Given the following code:
>>
>> def raise_error
>> raise "runtime error message"
>> rescue => e
>> "StandardError: #{e.message}"
>> rescue RuntimeError => e
>> puts "RuntimeError raised: #{e.message}"
>> raise StandardError, "standard error message"
>> end
>>
>> # same, but the order of the rescue blocks are inverted
>> def raise_error2
>> raise "runtime error message"
>> rescue RuntimeError => e
>> puts "RuntimeError raised: #{e.message}"
>> raise StandardError, "standard error message"
>> rescue => e
>> "StandardError: #{e.message}"
>> end
>>
>> p ["raise_error", raise_error ]
>>
>> begin
>> p ["raise_error2", raise_error2]
>> rescue => e
>> puts "raise_error2 raised: #{e.message}"
>> end
>>
>> When we run it, this is the output in Ruby 3.3.5:
>>
>> ["raise_error", "StandardError: runtime error message"]
>>
>> RuntimeError raised: runtime error message
>>
>> raise_error2 raised: standard error message
>>
>>
>> In the first case (raise_error), the code raised from the RuntimeError
>> rescue block is rescued by the StandardError block, but when inverting the
>> order of the rescue blocks (raise_error2) then this won't happen.
>>
>> Is this part of the specs? Is this behavior documented somewhere? Could
>> this behavior differ in different Ruby implementations and versions? Or can
>> we rely on such behavior?
>>
>>
>> This chapter doesn't include such case in its examples:
>>
>> https://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html
>>
>> Or this documentation about Exceptions:
>>
>> https://ruby-doc.org/3.3.5/syntax/exceptions_rdoc.html
>>
>> Is there a recommended way for wrapping exceptions into a particular one
>> and then handling that exception from within the same method rescue blocks?
>> Or is this considered a bad practice?
>>
>>
>> ______________________________________________
>> ruby-core mailing list -- ruby-core@ml.ruby-lang.org
>> To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
>> ruby-core info --
>> https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/
>
>
>
> --
> Austin Ziegler • halostatue@gmail.com • austin@halostatue.ca
> http://www.halostatue.ca/ • http://twitter.com/halostatue
>
[-- Attachment #1.2: Type: text/html, Size: 18426 bytes --]
[-- Attachment #2: Type: text/plain, Size: 254 bytes --]
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/
prev parent reply other threads:[~2024-10-29 19:24 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-29 12:42 [ruby-core:119637] " Rodrigo Rosenfeld Rosas via ruby-core
2024-10-29 18:07 ` [ruby-core:119639] " Austin Ziegler via ruby-core
2024-10-29 19:22 ` Rodrigo Rosenfeld Rosas via ruby-core [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAGmv+w+1vyj=+wdH7SonuXX7HzpuWETBi-Na7jKWTDz+mPXoBQ@mail.gmail.com' \
--to=ruby-core@ml.ruby-lang.org \
--cc=halostatue@gmail.com \
--cc=rr.rosas@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).