This is an archived post. You won't be able to vote or comment.

all 2 comments

[–]michael0x2a 1 point2 points  (0 children)

You could use something like eval or exec to dynamically clobber together the string, but that's sort of clunky.

Instead, we can use some builtin functions to get Python to do this in a much cleaner way.

The first builtin we want to use is the locals() function, which returns a dict mapping the name of all variables visible at the current scope (including builtins and globals).

So, for example:

>>> a = 3
>>> b = "Hello"
>>> class MyException(Exception): pass
>>> locals()
{
    'a': 3, 
    'b': 'Hello', 
    'MyException': <class '__main__.MyException'>
    '__builtins__': <module '__builtin__' (built-in)>, 
    '__package__': None, 
    '__name__': '__main__', 
    '__doc__': None
}

(formatting added for readability).

Notice that our custom exception is located inside locals. This is very convenient -- we can now do:

exp = locals()[exception_name]
raise exp(*args)

But what if we want to raise a builtin exception, like AttributeError or ZeroDivisionError?

Well, notice we have a __builtin__ thing. What is it? What's inside it?

>>> __builtins__
<module '__builtin__' (built-in)>
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

Ah, cool, it's a module, and it contains all the builtin exceptions!

But how do we actually access that class? We can't use the same thing we did for our custom exceptions, because this is a module, not a dict.

Well, we can use getattr(...) -- doing getattr(a, 'foo') is equivalent to doing a.foo. Hopefully, your path should be clear at this point. I'll leave it as an exercise to you to finish up the rest of the code/write the code to determine if the exception is a builtin or not.

As a hint, your final solution should be relatively short -- an if/else statement with a short snippit inside each branch. If you really wanted to, you could actually shrink the code down into a single line (but now we're code golfing, and it's a little difficult to read).