Different kinds of Import Errors

Jeremy C B Nicoll jeremy at omba.demon.co.uk
Sun Dec 9 13:12:57 EST 2007


Steven D'Aprano <steve at REMOVE-THIS-cybersource.com.au> wrote:

> On Sun, 09 Dec 2007 00:25:53 +0000, Jeremy C B Nicoll wrote:
> 
> > >       for app_name in settings.INSTALLED_APPS:
> > >           try:
> > >               __import__(app_name + '.management', {}, {}, [''])
> > >           except ImportError, exc:
> > >               if exc.args[0]!='No module named management':
> > >                   raise
> ...
> > I want to ask a possibly related question.  My impression is
> > that while writing and testing something like the above code one has
> > somehow to know that "ImportError" can be raised, so one can explicitly
> > define how it is to be treated.  How does one find that out?
> 
> Reading the docs. Trial and error. Background knowledge about what the 
> function is doing. There's no perfect solution; unfortunately many 
> functions fail to document what exceptions they can be expected to raise.
> 
> In principle, any exception that isn't documented is a bug. You can't be 
> expected to recover from bugs, all you can do is report it to the 
> function author.

While I've always been an advocate of (in other languages) checking return
codes from things, that's been when generally there's only been a small
number of those documented, or at least they could be split into "it wored
ok", "it almost worked", "minor problem", "huge problem".  So quite often
one could write some fairly generic error handling based only on rc being 0,
or <8, or <20 or something.  That's not to say that within that one couldn't
record the exact rc, error messages that went with them etc and pass them on
(to the caller, say).


Python's system seems messier.


> 
> 
> > While in this case, one might hope that an import action could only fail
> > with ImportError, isn't it possible that - say - one might get an
> > IOError because of a problem on the module search path ... and maybe
> > there are lots of other less obvious exceptions that could be raised?
> 
> The author of a function has a choice to make: 
> 
> (1) expose any exceptions that are raised by his code to the caller; or
> 
> (2) swallow those exceptions and raise one or two known exceptions.
> 
> Neither is the "right" answer in all circumstances. Sometimes you might 
> want to just propagate exceptions, especially if the caller is in a 
> position to do something about them. Sometimes the right solution is to 
> hide those exceptions, and just expose a single MyModuleError exception 
> to callers.
> 
> Sometimes neither is ideal.
> 
> 
> > How does one write a try/except piece of code that works (ie traps
> > whatever exception occurs, though obviously it can't necessarily fix an
> > arbitrary exception) for any exception, even those not known of in
> > advance by the author?
> 
> Generally, you don't. If you can't recover from an exception, there's 
> little point in trapping it. 

The reason I want to trap them is so as not to have a more naive user
suddenly be faced with an unfriendly traceback.  I'd want my program to save
details of what it'd been doing, the exception type etc, do its own cleanup
of whatever it was doing, report it had a problem, and then stop in a
'clean' fashion.  I can look at the details later on... 


> Exceptions are:
> 
> (1) You're writing some sort of library function and you want to hide the 
> internal errors that occur, so you might write:
> 
> def function():
>     try:
>         processing()
>     except (IndexError, NameError):
>         raise MyModuleError("bad things happened")
>     except (IOError, MemoryError):
>         raise MyModuleError("your computer is broken")
> 
> 
> (But notice how much potentially useful information you've thrown away by 
> doing this. Are you sure you want to hide that?)

It seems to me that I'd generally want something like:

 def function():
     try:
         processing()
     except (IndexError, NameError):
         raise MyModuleError("bad things happened")
     except (IOError, MemoryError):
         raise MyModuleError("your computer is broken")
     except (*):
         raise MyModuleError("wallop! :" + details() )

where in the last section I want to trap any exception that wasn't one of
the named/expected ones.  Your 

         raise MyModuleError("blah")

examples suggest that an exception can have just one string of details,
presuably passed back up the line...  Is that the case or can one pass a
list/tuple of symptoms?

I did see a example somewhere something like:

     except (SomeError), s:

in which 's' was then set to a list of - I think - two subsidiary values
which the exception handler could look at.  But I don't know if the
equivalent of 's' for any exception always has just 2 values.  And I
couldn't get anything like:

     except s:

or

     except *, s:

or

     except (), s:

etc to work meaning "match an arbitrary exception but give me access to
whatever symptoms or location-of-error or whatever" info is available.
    


I also find that if the function concerned actually is a function (ie
returns something if it works properly) it's easier to think (as I have in
other languages) of it returning error information the same way, so eg:

  def function2():
     try:
         what = processing()
     except (IndexError, NameError):
         what = "Error: details type 1"
     except (IOError, MemoryError):
         what = "Error: details type 2"
     except (*):
         what = "Error: unexpected: " + details()
     return what

so that then the caller of the function bases their next action on whether
or not the returned value starts "Error:".  That way I know that control
always comes back from that function via that return, rather than either by
a return or an exception.  I don't like functions/procedures with multiple
exit points (though I realise there's a difference between normal return and
exceptional circumstance returns). 



> Hope that helps.

Oh, it does...  Thank-you.

-- 
Jeremy C B Nicoll - my opinions are my own.



More information about the Python-list mailing list