Skip to content

Instantly share code, notes, and snippets.

@zacharyvoase
Created February 10, 2012 01:15
Show Gist options
  • Save zacharyvoase/1785070 to your computer and use it in GitHub Desktop.
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.
"""
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