Newbie question: what exceptions are thrown by FTP class

S

Simon Morgan

Hi,

I'm a Ruby newbie, and may have a daft question: how do I determine what
exceptions are thrown by different classes, so I know which ones to
catch etc?

As a starting exercise I'm writiing a program to do some FTPing, so at
the moment I'm specifically interested in the FTP class, but will have a
generic question for other classes.

Is this documented somewhere obvious that I'm misssing?

Thanks in advance.
 
A

Aldric Giacomoni

Simon said:
[H]ow do I determine what exceptions are thrown by different classes, so I know which ones to catch etc?
I'm specifically interested in the FTP class ...

Is this documented somewhere obvious that I'm misssing?

Can you point me towards the documentation for that FTP class?
 
B

Brian Candler

The Net::FTP documentation does tell you some exceptions that it can
raise, and if you really care about the detail you can just look at the
source, since it's all written in Ruby, and at the top you'll find

module Net

# :stopdoc:
class FTPError < StandardError; end
class FTPReplyError < FTPError; end
class FTPTempError < FTPError; end
class FTPPermError < FTPError; end
class FTPProtoError < FTPError; end
# :startdoc:

But often you don't care. Exceptions form a hierarchy, and the ones you
want to catch are usually either under StandardError or RuntimeError
(itself a subclass of StandardError)

A simple "rescue" by itself is the same as "rescue StandardError".

If you want to capture *all* exceptions then rescue Exception, but this
is usually not a good idea as it will also capture fatal errors - e.g.
NoMemoryError, SyntaxError, and Interrupt (ctrl-C)

For Net::FTP I would suggest:

begin
...
rescue IOError, SystemCallError, Net::FTPError
...
end

to catch everything socket-related and any protocol errors raised within
Net::FTP, without catching things which are normally programmer bugs
like NameError or ArgumentError.

Google "ruby exception hierarchy" for more info.
 
S

Simon Morgan

Brian,

Thanks for your reply, it’s very much appreciated.

Really I’m after something along the lines of what Java’s JavaDoc might
give which lists the thrown exceptions and the reasons why e.g.:
http://java.sun.com/javase/6/docs/api/ (I’m from a Java background). Do
you know if this exists or if there is a tool that can generate this
sort of info?

I did try looking at the source however the problem is just the time it
takes to go through everything to find what throws what, and this is
fairly error-prone and time-consuming; e.g. if I call nlst() without a
network connection, the exception is thrown from nlst > retrlines >
voidcmd > putline, which is throwing an error from a call to
TCPSocket.write...

Thanks for the specific info on FTP though, I’ll work with that for now
-- although I guess I may still need to search through all the code to
find the different IOError and SystemCallError instances, to see if I’d
want to treat them differently.

I hope you don’t think I’m being awkward, as I do value your reply and
time you put in.

Regards

Simon
 
B

Brian Candler

Simon said:
Really I’m after something along the lines of what Java’s JavaDoc might
give which lists the thrown exceptions and the reasons why

If the author includes that in the API documentation, then you'll have
it; if the author doesn't, then I'm afraid you won't.

In the case of Net::FTP you can see that the author decided to remove
all of the exception classes from the documentation :)stopdoc:),
presumably to avoid cluttering it up.
or if there is a tool that can generate this
sort of info?

The documentation you are currently looking at is generated via rdoc,
which basically extracts comments and method signatures from the code.
However, Ruby is a very dynamic language and it's not possible to
determine from static source analysis which exceptions may or may not be
thrown.

This isn't just nitpicking. It's quite common in Ruby to pass around and
use objects of 'unrelated' classes at runtime: 'unrelated' in the sense
of not sharing a common ancestor class other than Object. Rather, they
implement the same 'duck-type' interface. In the case of Net::FTP you
might arrange that it uses a SOCKSSocket instead of a Socket, for
example, and SOCKSSocket could have its own set of exceptions that it
raises (SOCKS authentication error, for example).

You could argue that if it raises a different exception under similar
circumstances then that's a duck-type violation - the duck goes honk
instead of quack. Unfortunately, this aspect of duck-typing is often
overlooked.

Anyway: there's no formal "interface" in ruby, which means objects can
implement whatever methods they feel like, and raise whatever exceptions
they feel like, and change their behaviour at runtime whenever they feel
like.

You probably find this dirty. However it's what enables all sorts of
goodness, such as ActiveRecord being able to define model classes at
runtime based on the columns in your database. The advice I give to you
is: relax, and have good unit test coverage.
I did try looking at the source however the problem is just the time it
takes to go through everything to find what throws what

It's pretty obvious in this case. All you have to do is look for classes
of which Exception is an ancestor (or StandardError or RuntimeError),
and they are all bundled together at the top of the file. It's just
convention, rather than rigidly enforced.
and this is
fairly error-prone and time-consuming; e.g. if I call nlst() without a
network connection, the exception is thrown from nlst > retrlines >
voidcmd > putline, which is throwing an error from a call to
TCPSocket.write...

Indeed. Hence my advice to be generic in your rescue strategy.

But *why* would you be calling nlst on a connection which isn't open?
Isn't that a programmer error, and therefore you shouldn't be catching
the exception at all?
-- although I guess I may still need to search through all the code to
find the different IOError and SystemCallError instances, to see if I’d
want to treat them differently.

Those are pretty low-level - for example, Errno::ECONNREFUSED is a
subclass of SystemCallError, so to determine all possible exceptions
when opening a socket you'll really need to look at the documentation
for the unix open(2) call and the possible errno values which it can
set.

It has to be said that Ruby's own core documentation is a long way from
complete.

Regards,

Brian.
 
R

Robert Klemme

2009/12/8 Simon Morgan said:
Really I=92m after something along the lines of what Java=92s JavaDoc mig= ht
give which lists the thrown exceptions and the reasons why e.g.:
http://java.sun.com/javase/6/docs/api/ (I=92m from a Java background). Do
you know if this exists or if there is a tool that can generate this
sort of info?

There can be no tool that works under all circumstances because a)
exceptions are not declared as checked exceptions in Java are and b)
the code of a Ruby class can change any time (including throwing new
exceptions). While that may sound a bit theoretical it explains why
probably nobody has bothered so far to come up with the tool you are
requesting. (Note, even in Java you can create such a situation by
using unchecked exceptions and delegate at some place in code to
instances of different classes all of which do not necessarily have to
be known at compile time.)

You can look at the sources as Brian suggested or try to provoke
different error situations (i.e. using a hostname that does not exist,
using a wrong user, retrieving a file which you know is not on the
server etc.) and see what happens.

More often than not though it is probably sufficient to catch some
basic errors as Brian suggested.

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
S

Simon Morgan

Really I’m after something along the lines of what Java’s JavaDoc might
If the author includes that in the API documentation, then you'll have
it; if the author doesn't, then I'm afraid you won't.

Sigh. I feared that would be the case.

In the case of Net::FTP you can see that the author decided to remove
all of the exception classes from the documentation :)stopdoc:),
presumably to avoid cluttering it up.

Yes I spotted that, kinda frustrating that I have to look at the source
code to find some of the errors raised. Ah well.

Indeed. Hence my advice to be generic in your rescue strategy.

But *why* would you be calling nlst on a connection which isn't open?
Isn't that a programmer error, and therefore you shouldn't be catching
the exception at all?

If the network connection dies between opening and calling nlst(), for
example.

It has to be said that Ruby's own core documentation is a long way from
complete.

Sigh.


Thanks very much for your time Brian, it's very much appreciated, I'll
see how I adapt.
 
B

Brian Candler

Simon said:
If the network connection dies between opening and calling nlst(), for
example.

OK, I see.

In that case you would probably get something like an Errno::EPIPE
(writing to a socket where the read end is closed).

I don't think you really want to enumerate all the possibilities though.
Just rescue SystemCallError which is the parent of all the exceptions in
module Errno.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,965
Messages
2,570,148
Members
46,710
Latest member
FredricRen

Latest Threads

Top