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