issue #72: WIP
parent
e8ab8d9352
commit
0e98cd4590
@ -0,0 +1,127 @@
|
||||
# Copyright 2017, David Wilson
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
This defines :func:`match` that produces dynamic subclasses of a magical type
|
||||
whose :func:`isinstance` returns :data:`True` if the instance being checked is
|
||||
a :class:`mitogen.core.CallError` whose original exception type matches a
|
||||
parent class in the hierarchy of the the supplied Exception type.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
import mitogen.core
|
||||
|
||||
|
||||
#: Map Exception class -> Matcher class.
|
||||
_matcher_by_cls = {}
|
||||
|
||||
|
||||
def get_matching_classes(cls):
|
||||
"""
|
||||
Given a class, return a list containing it and any base classes, operating
|
||||
recursively such that the returned list contains the entire hierarchy of
|
||||
`cls`.
|
||||
"""
|
||||
classes = [cls]
|
||||
for subcls in cls.__bases__:
|
||||
classes.extend(get_matching_classes(subcls))
|
||||
return classes
|
||||
|
||||
|
||||
class MatcherMeta(type):
|
||||
def __subclasscheck__(matcher_cls, cls):
|
||||
print('lol2')
|
||||
return (
|
||||
issubclass(cls, mitogen.core.CallError) and
|
||||
(cls.type_name in matcher_cls.type_names)
|
||||
)
|
||||
|
||||
def __instancecheck__(matcher_cls, e):
|
||||
print('lol')
|
||||
return MatcherMeta.__subclasscheck__(matcher_cls, type(e))
|
||||
|
||||
|
||||
__metaclass__ = MatcherMeta
|
||||
|
||||
|
||||
class Matcher:
|
||||
"""
|
||||
This class serves only as a placeholder for its relationship with
|
||||
:meth:`MatcherMeta.__instancecheck__` where the magic happens. A
|
||||
dynamically generated Matcher subclass is returned by :func:`match`, to
|
||||
served only for use with isinstance() internally by the Python exception
|
||||
handling implementation::
|
||||
|
||||
# Create a dynamic subclass of Matcher to match mitogen.core.CallError
|
||||
# instances according to the type of the original exception.
|
||||
matcher_cls = mitogen.error.match(ValueError)
|
||||
try:
|
||||
context.call(func_raising_some_exc)
|
||||
|
||||
# Here Python calls type(matcher_class).__instancecheck__(e):
|
||||
except matcher_cls as e:
|
||||
# e remains bound to the CallError as before.
|
||||
pass
|
||||
"""
|
||||
#: Overridden by subclasses generated by :func:`match`.
|
||||
classes = frozenset([])
|
||||
|
||||
|
||||
def match(target_cls):
|
||||
"""
|
||||
Return a magic for use in :keyword:`except` statements that matches any
|
||||
:class:`mitogen.core.CallError` whose original exception type was
|
||||
`target_cls` or one of its base classes::
|
||||
|
||||
try:
|
||||
context.call(func_raising_some_exc)
|
||||
except mitogen.error.match(ValueError) as e:
|
||||
# handle ValueError.
|
||||
pass
|
||||
|
||||
:param type target_cls:
|
||||
Target class to match.
|
||||
|
||||
:returns:
|
||||
:class:`Matcher` subclass.
|
||||
"""
|
||||
try:
|
||||
return _matcher_by_cls[target_cls]
|
||||
except KeyError:
|
||||
name = '%s{%s}' % (
|
||||
mitogen.core.qualname(Matcher),
|
||||
mitogen.core.qualname(target_cls),
|
||||
)
|
||||
matcher_cls = type(name, (Matcher,), {
|
||||
'type_names': frozenset(
|
||||
mitogen.core.qualname(cls)
|
||||
for cls in get_matching_classes(target_cls)
|
||||
)
|
||||
})
|
||||
_matcher_by_cls[target_cls] = matcher_cls
|
||||
return matcher_cls
|
Loading…
Reference in New Issue