Python契约式设计 - AOP风格的实现

来源:互联网 发布:mac队麦基一号 编辑:程序博客网 时间:2024/06/05 16:51

###############################################################################
#
# This example shows how to implement DBC with an Python implementation of AOP
# - aspects.py.
#
# Note this implementation is not completely done, and will be developed
# further.
#
###############################################################################

import aspects

###############################################################################

class AbstractContractChecker:
    """
    Base class of all contract-checker classes.
    """
    pass

###############################################################################

class DefaultContractChecker(AbstractContractChecker):
    """
    Default contract checker.
    """

    # the contract
    _contract = None

    def __init__(self, contract):
        """"""
        # contract should be an instance of AbstractContract
        assert isinstance(contract, AbstractContract)

        # save contract
        self._contract = contract

        # attach contract-checking methods to wrapper
        for c in self._contract.get_method_checkers():
            aspects.with_wrap(self._check_contracts, c.get('method'))

    def _check_contracts(self, *args, **kwargs):
        """Check contracts."""
        # get current class instance
        inst = self._get_instance(args)

        # get wrapped method
        wrapped = yield aspects.get_wrapped

        # get contract-checker definition for the current method
        d = self._get_checker_def(wrapped)

        # check class invariants before the method is executed
        self._check_invariant(d, inst, args, kwargs, None)

        # check preconditions
        pre_checker = d.get('pre_checker')
        if pre_checker is not None:
            pre_checker(inst, args, kwargs)

        # continue the execution if preconditions satisfy
        retval = yield aspects.proceed

        # check postconditions
        post_checker = d.get('post_checker')
        if post_checker is not None:
            post_checker(inst, args, kwargs, None, retval)

        # check class invariants after the method is executed
        self._check_invariant(d, inst, args, kwargs, None)

        yield aspects.return_stop

    def _check_invariant(self, d, inst, args, kwargs, old):
        """Check class invariant holds."""
        inv_checker = self._contract.get_invariant_checker()
        if d.get('check_inv') and inv_checker is not None:
            inv_checker(inst, args, kwargs, old)

    def _get_checker_def(self, wrapped):
        """Return contract-checker definition for wrapped method."""
        retval = None

        for c in self._contract.get_method_checkers():
            if str(c.get('method')) == str(wrapped):
                retval = c
                break

        return retval

    def _get_instance(self, args):
        """Return current class instance."""
        return args[0]

###############################################################################

class ConditionNotHoldError:
    """
    Exception indicating condition does not hold.
    """
    pass

###############################################################################

class AbstractContract:
    """
    Base class of all contract classes.
    """

    def check(self, condition, message = 'Condition does not hold.'):
        """Check specified condition holds."""
        if not condition:
            raise ConditionNotHoldError

    def get_invariant_checker(self):
        """Return the method used to check class invariants."""
        pass

    def get_method_checkers(self):
        """Return a list of contract checkers."""
        pass

###############################################################################

原创粉丝点击