Created
February 10, 2012 01:15
-
-
Save zacharyvoase/1785070 to your computer and use it in GitHub Desktop.
Can't believe how hacky this is. Can't believe it kinda works.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Dynamic pattern matching in except clauses. | |
You can use `ABCMeta` and `__subclasshook__` to implement pattern matching in | |
except clauses. For this demo we're doing two things: first, we're creating | |
exception classes dynamically by overridding `__getattr__` on a metaclass, | |
which itself inherits from `abc.ABCMeta`. This allows us to do this: | |
>>> Errno | |
<class 'dynerror.Errno'> | |
>>> print Errno.match_errno | |
None | |
>>> Errno.EBADF | |
<class 'dynerror.Errno.EBADF'> | |
>>> print Errno.EBADF.match_errno | |
9 | |
The `__subclasshook__` classmethod on `Errno` implements subclass checking | |
dynamically: | |
>>> error = OSError(9, 'Bad file descriptor') | |
`Errno` is a superclass of any exception with an `errno` attribute; however, in | |
order to verify this we have to raise the exception (otherwise | |
`__subclasshook__` only has access to the type, rather than the instance): | |
>>> try: | |
... raise error | |
... except Errno: | |
... print "It's a subclass of Errno" | |
It's a subclass of Errno | |
`Errno.EBADF` is a superclass of any exception with an `errno` of 9, but not | |
any other exception: | |
>>> try: | |
... raise error | |
... except Errno.EBADF: | |
... print "It's a subclass of Errno.EBADF" | |
It's a subclass of Errno.EBADF | |
>>> try: | |
... raise OSError(32, 'Broken pipe') | |
... except Errno.EBADF: | |
... print "You'll never see this" | |
... except Errno.EPIPE: | |
... print "But you will see this" | |
But you will see this | |
""" | |
import __builtin__ | |
import abc | |
import errno | |
import sys | |
class Errno(__builtin__.EnvironmentError): | |
match_errno = None | |
class __metaclass__(abc.ABCMeta): | |
def __getattr__(cls, attr): | |
if attr.isupper() and attr in vars(errno): | |
return cls.matcher(attr, vars(errno)[attr]) | |
raise AttributeError(attr) | |
@classmethod | |
def __subclasshook__(cls, exc_type): | |
exc_type, exc_info, traceback = sys.exc_info() | |
exc_errno = getattr(exc_info, 'errno', None) | |
if cls.match_errno is None: | |
return exc_errno is not None | |
elif exc_errno is not None: | |
return exc_errno == cls.match_errno | |
return False | |
@classmethod | |
def matcher(cls, error_symbol, error_number): | |
# Return a dynamically-created subclass of the current class with the | |
# `match_errno` attribute set. | |
return type(cls.__name__ + "." + error_symbol, | |
(cls,), | |
{'match_errno': error_number, | |
'__module__': cls.__module__}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment