Hooks, aspect-oriented programming, and design by contract
Gustavo Niemeyer
niemeyer at conectiva.com
Tue Jan 22 15:19:25 EST 2002
> Yes... you're right. I forgot about the __init__ stuff. Here's a fixed
> version (note the changes in ContractInstance.__init__) that will
> probably do the job.
Oops... forgot about parameters. Here is a last one including parameters
support for __init__().
I-should-publish-this-somewhere-ly y'rs
--
Gustavo Niemeyer
[ 2AAC 7928 0FBF 0299 5EB5 60E2 2253 B29A 6664 3A0C ]
-------------- next part --------------
from types import FunctionType
CONTRACT_NONE = 0
CONTRACT_PRE = 1
CONTRACT_POST = 2
CONTRACT_INV = 3
ContractLevel = CONTRACT_INV
# Trick to work with 2.1 and 2.2
if not __builtins__.__dict__.get("object"):
class object: pass
class ContractMeta(object):
def __init__(self, name, bases, namespace):
self.__name__ = name
self.__bases__ = bases
self.__namespace__ = namespace
def __call__(self, *args, **kw):
return ContractInstance(self, *args, **kw)
class ContractInstance:
def __init__(self, klass, *args, **kw):
self.__klass__ = klass
init = klass.__namespace__.get("__init__")
if init:
init(self, *args, **kw)
def __getattr__(self, name):
namespace = self.__klass__.__namespace__
try:
attr = namespace[name]
except KeyError:
raise AttributeError, name
if type(attr) is FunctionType:
inv = namespace.get("invariant")
if name == "invariant":
return ContractSpecialMethod(attr, self, None, ContractInvError)
if name[-4:] == "_pre":
return ContractSpecialMethod(attr, self, inv, ContractPreError)
if name[-5:] == "_post":
return ContractSpecialMethod(attr, self, inv, ContractPostError)
pre = namespace.get(name+"_pre")
post = namespace.get(name+"_post")
return ContractMethod(name, attr, self, inv, pre, post)
return attr
class ContractSpecialMethod:
"""Handle calling of pre/post conditions and invariant."""
def __init__(self, function, instance, invariant, exception):
self.__function = function
self.__instance = instance
self.__invariant = invariant
self.__exception = exception
def __call__(self, *parm, **kw):
if self.__invariant:
try:
self.__invariant(self.__instance)
except AssertionError, msg:
raise ContractInvError(msg)
try:
self.__function(*(self.__instance,)+parm, **kw)
except AssertionError, msg:
raise self.__exception(msg)
class ContractMethod:
"""Handle calling of usual methods."""
def __init__(self, name, function, instance, inv, pre, post):
self.__name = name
self.__function = function
self.__instance = instance
self.__inv = inv
self.__pre = pre
self.__post = post
def __call__(self, *parm, **kw):
namespace = self.__instance.__klass__.__namespace__
if self.__inv:
try:
self.__inv(self.__instance)
except AssertionError, msg:
raise ContractInvError(msg)
if self.__pre:
try:
self.__pre(*(self.__instance,)+parm, **kw)
except AssertionError, msg:
raise ContractPreError(msg)
result = self.__function(*(self.__instance,)+parm, **kw)
if self.__post:
try:
self.__post(*(self.__instance, result)+parm, **kw)
except AssertionError, msg:
raise ContractPostError(msg)
if self.__inv:
try:
self.__inv(self.__instance)
except AssertionError, msg:
raise ContractInvError(msg)
return result
class ContractError(AssertionError): pass
class ContractPreError(ContractError): pass
class ContractPostError(ContractError): pass
class ContractInvError(ContractError): pass
Contract = ContractMeta("Contract", (), {})
if __name__ == "__main__":
# Usage example
class A(Contract):
def __init__(self, x):
self.x = x
def invariant(self):
assert self.x == 0
print "invariant()"
def a(self):
self.x = 1
print "a()"
def a_pre(self):
print "a_pre()"
def a_post(self, result):
print "a_post()"
a = A(0)
a.a()
# vim:ts=4:sw=4
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 240 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/python-list/attachments/20020122/7a570bd6/attachment.sig>
More information about the Python-list
mailing list