Please send questions to st10@humboldt.edu .
Projected Random Notes
CIS 480 - Python - Lect 14
11-29-05

*   when Python detects an error at runtime --- it raises an exception

    *   (it can ALSO raise exceptions for other reasons --- but this is
        one we've definitely seen frequently before now!!)

    *   if an exception is not "caught", Python's default exception handling
        behavior kicks in ---

	STOP the program, PRINT an error message

*   and note that exceptions can be used for more than just error handling:

    *   (ref: Learning Python, p. 394)

    *   event notification

        *   might use an exception to handle notification about a "valid"
            condition (not necessarily an error)

        *   (e.g., a search routine might raise an exception on failure
            to find something, rather than returning some integer or
            boolean code, etc.)

    *   special-case handling

        *   what if a condition happens really rarely, and handling it
            would require nasty code convolution?

	    Perhaps: raise an exception in such a case, and let exception
            handlers handle it instead...

    *   termination actions

        *   we'll see that the try/finally statement gives us a way to
            guarantee that required closing-time operations will get done,
            even if exceptions do or do not occur;

    *   unusual control flows

        *   "because exceptions are a sort of high-level "go-to",
            you can use them as the basis for implementing exotic
            control flows." [p. 394, "Learning Python"]

	    For example: "although backtracking [a la Prolog?]
	    is not part of [Python] itself, it can be implemented
	    in Python with exceptions and a bit of support logic to
	    unwind assignments"

*   SO --- Python raises built-in exceptions such as TypeError,
    SyntaxError, IndexError, and MANY more;

    *   When Python raises/triggers a built-in exception,

        IF your code doesn't catch it,

	it reaches the top level of the program,
	the default exception handler is invoked,
	and the standard error message is printed,

	(including the NAME of the exception raised,
	and a STACK TRACE - a list of the lines and functions
        active when the exception occurred.)

*   what if you'd like an action BESIDES this default when a 
    particular exception is raised?

    You can use a try statement;

    you "WRAP" (or put) the statements that MIGHT cause an
    exception to be raised in a try statement

    One example:

    def catcher():
        try:
            fetcher(x, 4)    # statements go IN try statement block
                             #    if they MIGHT throw an exception
			     #    that I'd like to catch/handle
        except IndexError:   # I'm saying I'd like the following
			     #    to happen if an IndexError exception
			     #    is raised while executing function call
			     #	  fetcher
	    print 'got exception'

	print 'continuing'

*   raise (and assert) let you raise user-defined OR built-in exceptions
    at YOUR option;

    raise <an_exception>

*   finally

    *   can be used to indicate actions you want done whether
        an exception is thrown within the accompanying try statement
        or not!

    *   CANNOT be used in the same try stateemnt as except and else...!
        (else is coming soon...)

*   a few more variations and additional clauses...

    *   except (name3, name4, name5):

        run the statements in this block if ANY of name3, name4, name5
        are raised in the accompanying try-statement

    *   except name3, data:

        run the statements in this block if name3 is raised in the
        accompanying try-statement, AND get the extra data 

    *   except:

        run the accompanying statements in this block if an exception
        was raised in the accompanying try-statement (and wasn't already
        caught by a previous except...)

    *   else:

        run the statements in this block if NO exception was raisede in
        the accompanying try-statement

    *   raise name2, data

	pass the extra data along with exception name2 to catcher, also

    *   raise	       

	re-raise the most recent exception

*    and what is assert?

     ...a more convenient way to test an assertion and raise an
     exception if that assertion fails (than plain raise...)

     assert test1, data     # data part optional

     *   if test1 evaluates to False, Python will raise an
         AssertionError, and if additional data is given,
         that will be passed on to the catcher, also

    *    works LIKE (p. 408, Learning Python):

        if __debug__:
            if not test1:
                raise AssertionError, data

*   Lect 14 in-lecture exercise:

    1. Write a statement creating a simple string exception
       of your choice

    2. write a statement that would raise that exception
       from #1, passing along additional information of your choice

    3. write a try statement that will encompass your statement
       from #2, but then catch that exception and print that
       if has been caught, and print the additional data (if any)
       passed along