Basics of Exceptions

You know exceptions from Java programming. The basic concepts are the same across many languages - including Python. Here's a rough summary of what you need to keep in mind:
  1. All exceptions are ultimately derived from the class BaseException. However, if you create your own exception class, you should derive it from the class Exception.
  2. Instead of "throw" and exception, in Python you "raise" and exception. So:
    raise ArithmeticError()           throw new ArithmeticException()
    \_____________________/           \_____________________________/
     how to "throw" in Python          how to do the analogous thing in Java
  3. Instead of "try ... catch" as in Java, in Python you do "try ... except".
    Python version Java version
    try:
        foobar()
    except ArithmeticError as ae: 
        print(ae)
    try {	    
      foobar();
    } catch(ArithmeticError ae) { 
      System.out.println(ae);
    }
  4. An "except:", i.e. without an exception type, matches any exception that hasn't already been caught by an earlier except clause.

Copy the following code into a python program ex0.py.

TODO Find inputs that, when you run the program, cause:

  1. a "result" to be printed
  2. the "number rejected!" error message to be printed
  3. the "did you follow the directions?" message to be printed

Redoing the exceptions intro problem from IC211

Copy the following code into a python program ex1.py and use chmod to make it executable (so you can run it as ./ex1.py).
Note: sys.argv is a list of the command-line arguments, where sys.argv[0] is the program (e.g. "./ex1.py" in this case).

TODO

  1. Try running the program in the following ways:
    1. ./ex1.py 50,10,90    Shouldn't have any exceptions
    2. ./ex1.py 50,-10,90    Exception type:___________
    3. ./ex1.py 50,one,90    Exception type:___________
    4. ./ex1.py 50,0,90    Exception type:___________
    5. ./ex1.py    Exception type:___________
  2. Without modifying the above code in any way, except to wrap the line
    print(getSF(sys.argv[1].split(",")))
    in a try with except clauses, make the program so that it prints the following output for the following inputs:
    $ ./ex1.py 50,10,90
    20
    $ ./ex1.py 50,-10,90
    Error! there was a problem
    $ ./ex1.py 50,one,90
    Error! there was a problem
    $ ./ex1.py 50,0,90
    Error! no zeros allowed
    $ ./ex1.py
    Usage: ./ex1.py <numlist> 
  3. Why is it problematic to ask for different error messages for something non-numeric (like "one") in the argument list versus trying to take the sqrt of a negative number in the compute function?

Making your own exception

Suppose that we want different error messages for something non-numeric (like "one") in the argument list versus trying to take the sqrt of a negative number in the compute function. Like this:
$ ./ex1.py 50,-10,90
Error in compute function!
$ ./ex1.py 50,one,90
Error! non-numeric in argument list
Make it happen! However, you are *not* allowed to print anything out in the compute function! Instead, create your own exception class, and raise an exception in compute() if you try to take the sqrt of a negative. You can catch this exception back in the main block of your code with the other catches to print the appropriate message.

Challenge!

Make the program produce different error messages for improper comma-separation vs values that aren't integers. Like this:
$ ./sol3.py ,34,12
Error! not a comma-separated list
$ ./sol3.py 34,,12
Error! not a comma-separated list
$ ./sol3.py 34,12,
Error! not a comma-separated list
$ ./sol3.py 34,sdf,16
Error! element 'sdf' not an integer

Important! It should still be the case that error messages are only printed in except-clauses for the try-block in the main block at the end.

  1. Define a function arg2list(arg) that takes a string that is a comma-separated list (like the argument we are expecting) and splits it into an array of strings, which are the values (no commas!). Then change the existing line print(getSF(arg2list(sys.argv[1]))) into:
    I suggest the regular expression:
    ^[^,]+(,[^,]+)*$
    1. The ^ matches the beginning of the string.
    2. The $ matches the and of the string.
    3. [^,] matches anything *but* a comma.
    print(getSF(arg2list(sys.argv[1])))
    arg2list should check for proper comma-separation, but *not* for the values being numbers! After all, we might want to use arg2list in other situations where comma-separated lists are a thing. I suggest using regular expressions! (see note).
  2. If getSF( ) can't convert an element to an int, it is somehow going to have to throw an exception that carries with it the information about what the value was that couldn't be converted. The hack is to extract this from the string representation of the existing ValueError. That's really a hack! The nice way is to make your own exception with the extra info, but you might have to change getSF so it uses a loop rather than a list comprehension.

Christopher W Brown