From 687dc1c574b5962fbda4d16e6670bb9035c5c67f Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Mon, 17 Nov 2014 22:54:52 -0500 Subject: [PATCH 01/10] before revert on backport.py --- src/contracts/backported.py | 13 ++++++++++--- src/contracts/main.py | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/contracts/backported.py b/src/contracts/backported.py index 1642cb8..c0154d0 100644 --- a/src/contracts/backported.py +++ b/src/contracts/backported.py @@ -8,11 +8,13 @@ from collections import namedtuple FullArgSpec = namedtuple('FullArgSpec', 'args varargs varkw defaults' ' kwonlyargs kwonlydefaults annotations') - from inspect import getargspec as _getargspec + from inspect import getargspec as _getargspec, isfunction def getargspec(function): # print 'hasattr im_func', hasattr(function, 'im_func') - if hasattr(function, 'im_func'): + if (hasattr(function, 'im_func') or \ + (hasattr(function,'__call__') and \ + hasattr(function.__call__,'im_func'))): # print('this is a special function : %s' % function) # For methods or classmethods drop the first # argument from the returned list because @@ -21,7 +23,12 @@ def getargspec(function): # inspect.getargspec() returns for methods. # NB: We use im_func so we work with # instancemethod objects also. - x = _getargspec(function.im_func) + + if hasattr(function, 'im_func'): + x = _getargspec(function.im_func) + else: + x = _getargspec(function.__call__.im_func) + new_args = x.args[1:] spec = ArgSpec(args=new_args, varargs=x.varargs, keywords=x.keywords, defaults=x.defaults) diff --git a/src/contracts/main.py b/src/contracts/main.py index 51b992a..c7ae9bc 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -309,9 +309,11 @@ def write_contract_as_rst(c): contracts_checker.__module__ = function_.__module__ # TODO: is using functools.wraps better? - from decorator import decorator + # from decorator import decorator + from functools import partial,wraps - wrapper = decorator(contracts_checker, function_) + # wrapper = decorator(contracts_checker,function_) + wrapper = wraps(function_)(partial(contracts_checker,function_)) wrapper.__doc__ = new_docs wrapper.__name__ = function_.__name__ From 5f550a182273bcc949f7f131c6d1e5e32767cf6d Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 00:07:02 -0500 Subject: [PATCH 02/10] Added support for callable objects and bound methods --- src/contracts/backported.py | 13 +++---------- src/contracts/main.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/contracts/backported.py b/src/contracts/backported.py index c0154d0..1642cb8 100644 --- a/src/contracts/backported.py +++ b/src/contracts/backported.py @@ -8,13 +8,11 @@ from collections import namedtuple FullArgSpec = namedtuple('FullArgSpec', 'args varargs varkw defaults' ' kwonlyargs kwonlydefaults annotations') - from inspect import getargspec as _getargspec, isfunction + from inspect import getargspec as _getargspec def getargspec(function): # print 'hasattr im_func', hasattr(function, 'im_func') - if (hasattr(function, 'im_func') or \ - (hasattr(function,'__call__') and \ - hasattr(function.__call__,'im_func'))): + if hasattr(function, 'im_func'): # print('this is a special function : %s' % function) # For methods or classmethods drop the first # argument from the returned list because @@ -23,12 +21,7 @@ def getargspec(function): # inspect.getargspec() returns for methods. # NB: We use im_func so we work with # instancemethod objects also. - - if hasattr(function, 'im_func'): - x = _getargspec(function.im_func) - else: - x = _getargspec(function.__call__.im_func) - + x = _getargspec(function.im_func) new_args = x.args[1:] spec = ArgSpec(args=new_args, varargs=x.varargs, keywords=x.keywords, defaults=x.defaults) diff --git a/src/contracts/main.py b/src/contracts/main.py index c7ae9bc..de73039 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -160,6 +160,14 @@ def contracts_decorate(function_, modify_docstring=True, **kwargs): The decorator :py:func:`decorate` calls this function internally. """ + if hasattr(function_,'__call__') and hasattr(function_.__call__,'im_func'): + """ For classes that implement __call__ replace the object with + a bound __call__. + """ + class_name=function_.__class__.__name__ + function_=function_.__call__ + function_.__dict__['__name__']=class_name +'.__call__' + if isinstance(function_, classmethod): msg = """ The function is a classmethod; PyContracts cannot decorate a classmethod. @@ -308,11 +316,8 @@ def write_contract_as_rst(c): contracts_checker.__name__ = 'checker-for-%s' % function_.__name__ contracts_checker.__module__ = function_.__module__ - # TODO: is using functools.wraps better? - # from decorator import decorator from functools import partial,wraps - # wrapper = decorator(contracts_checker,function_) wrapper = wraps(function_)(partial(contracts_checker,function_)) wrapper.__doc__ = new_docs From 442c92ff4d8f2626ea6e21f67f6d94d8c380d1dc Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 04:12:14 -0500 Subject: [PATCH 03/10] ContractAtribute added to main --- src/contracts/__init__.py | 2 +- src/contracts/main.py | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/contracts/__init__.py b/src/contracts/__init__.py index 4c21c1a..34ec42e 100644 --- a/src/contracts/__init__.py +++ b/src/contracts/__init__.py @@ -15,7 +15,7 @@ from .main import (check, fail, check_multiple, contract_decorator, contracts_decorate as decorate, - parse_flexible_spec as parse) + parse_flexible_spec as parse, ContractAttribute) # Just make them appear as belonging to the "contracts" Module diff --git a/src/contracts/main.py b/src/contracts/main.py index de73039..cd3f701 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -724,4 +724,26 @@ def can_accept_self_plus_one_argument(callable_thing): return True - +class ContractAttribute(object): + """ A function descriptor for object attributes that enforces a + contract check on whatever static function the attribute is set to. + """ + + def __init__(self, contract_instance): + self.contract=contract_instance + + def __get__(self, instance, owner): + if instance.__dict__.get('__type_checked__')==None: + instance.__type_checked__={} + + assert instance.__type_checked__.get(self)!=None, \ + "Function not set yet." + + return instance.__type_checked__.get(self) + + def __set__(self, instance, func): + if instance.__dict__.get('__type_checked__')==None: + instance.__type_checked__={} + + instance.__type_checked__[self]=self.__dict__['contract'](func) + From f42c80d907f0ef2540e124663472c0723e7d98ab Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 13:44:49 -0500 Subject: [PATCH 04/10] Added doc string --- src/contracts/main.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/contracts/main.py b/src/contracts/main.py index cd3f701..c6f6616 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -727,6 +727,22 @@ def can_accept_self_plus_one_argument(callable_thing): class ContractAttribute(object): """ A function descriptor for object attributes that enforces a contract check on whatever static function the attribute is set to. + Usage example: + + class spam(object): + f=ContractAttribute(contract(arg='float,>0')) + + eggs=spam() + + from math import log, exp + eggs.f=lambda (arg): log(arg) + + print "eggs.f(e)=" + str(eggs.f(exp(1.0))) + + try: + print "eggs.f=" + str(eggs.f(-1.0)) + except ContractNotRespected as detail: + print detail """ def __init__(self, contract_instance): From f4d6a26e49acad1f6368d0cabbd77e76f2ff75d3 Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 14:11:31 -0500 Subject: [PATCH 05/10] Changed the name of the private attribute used to store wrapped values to __contracts__ --- src/contracts/main.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/contracts/main.py b/src/contracts/main.py index c6f6616..0427dc2 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -749,17 +749,18 @@ def __init__(self, contract_instance): self.contract=contract_instance def __get__(self, instance, owner): - if instance.__dict__.get('__type_checked__')==None: - instance.__type_checked__={} + if instance.__dict__.get('__contracts__')==None: + instance.__contracts__={} - assert instance.__type_checked__.get(self)!=None, \ + assert instance.__contracts__.get(self)!=None, \ "Function not set yet." - return instance.__type_checked__.get(self) + return instance.__contracts__.get(self) + # return the method without binding it to the instance. def __set__(self, instance, func): - if instance.__dict__.get('__type_checked__')==None: - instance.__type_checked__={} + if instance.__dict__.get('__contracts__')==None: + instance.__contracts__={} - instance.__type_checked__[self]=self.__dict__['contract'](func) - + instance.__contracts__[self]=self.__dict__['contract'](func) + # use dict to accsess the unbound contract From da0b7e4f1edb8ed9fa22c74ec26eba7464ca3e15 Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 14:32:21 -0500 Subject: [PATCH 06/10] a bit more info in doc string --- src/contracts/main.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/contracts/main.py b/src/contracts/main.py index 0427dc2..4185367 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -726,7 +726,15 @@ def can_accept_self_plus_one_argument(callable_thing): class ContractAttribute(object): """ A function descriptor for object attributes that enforces a - contract check on whatever static function the attribute is set to. + contract check on whatever function the attribute is set + to. The function would not be passed the self argument because + that breaks encapsulation. You can still actively pass self if + you want (just add it to the contract). Setting up an attribute + like this is useful when an API expressed as an object + requires a client function to work with and you want to both + communicate expectations with regards to that function and + check them. + Usage example: class spam(object): From 1e52dfde163d3a0a8cb781cc92452012c93eaf3c Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 19:03:08 -0500 Subject: [PATCH 07/10] Added support for scalar attributes --- src/contracts/main.py | 107 ++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/src/contracts/main.py b/src/contracts/main.py index 4185367..dfc66b5 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -725,50 +725,99 @@ def can_accept_self_plus_one_argument(callable_thing): class ContractAttribute(object): - """ A function descriptor for object attributes that enforces a - contract check on whatever function the attribute is set - to. The function would not be passed the self argument because - that breaks encapsulation. You can still actively pass self if - you want (just add it to the contract). Setting up an attribute - like this is useful when an API expressed as an object - requires a client function to work with and you want to both - communicate expectations with regards to that function and - check them. + """ A descriptor for object attributes that enforces a contract + check on whatever object or function the attribute is set + to. For a function, the function would not be passed the self + argument (because that breaks encapsulation - you can still + actively pass self if you want). Setting up an attribute like + this is useful when an API that is expressed as an object + requires a client function to work with. In such cases you + want to both communicate expectations with regards to the + needed function and verify that they are met. Usage example: - class spam(object): - f=ContractAttribute(contract(arg='float,>0')) - - eggs=spam() - - from math import log, exp - eggs.f=lambda (arg): log(arg) +from contracts import ContractAttribute, contract, ContractNotRespected - print "eggs.f(e)=" + str(eggs.f(exp(1.0))) - - try: - print "eggs.f=" + str(eggs.f(-1.0)) - except ContractNotRespected as detail: - print detail +class spam(object): + f=ContractAttribute(contract(arg='float,>0')) #for functions + x=ContractAttribute('float,>0') #for scalars using string + y=ContractAttribute(float) #for scalars using type + +eggs=spam() + +from math import log, exp, pi +eggs.f=lambda (arg): log(arg) + +print "eggs.f(e)=" + str(eggs.f(exp(1.0))) + +try: + print "eggs.f=" + str(eggs.f(-1.0)) +except ContractNotRespected as detail: + print detail + +print "Attempting eggs.x=pi" +eggs.x=pi +print "eggs.x=" + str(eggs.x) + +print "Attempting eggs.x=-pi" +try: + eggs.x=-pi +except ContractNotRespected as detail: + print detail + +print "Attempting eggs.y=2*pi" +eggs.y=2*pi +print "eggs.y=" + str(eggs.y) +print "eggs.x=" + str(eggs.x) + +print "Attempting eggs.y=3" +try: + eggs.y=3 +except ContractNotRespected as detail: + print str(detail)[:180] """ + def _check(self,check_string,value): + check(check_string, value) + return value + def __init__(self, contract_instance): - self.contract=contract_instance + + from functools import partial + from types import FunctionType, StringType + + if isinstance(contract_instance,FunctionType): + # If it is a contract store the contract + self.contract=contract_instance + + elif isinstance(contract_instance,StringType): + # If it is a string send it to check and store the partial + self.contract=partial(self._check,contract_instance) + + else: + # Assume it is a type. Get the name of the type, send + # it to check and store the result. + self.contract=partial(self._check,contract_instance.__name__) + def __get__(self, instance, owner): if instance.__dict__.get('__contracts__')==None: instance.__contracts__={} assert instance.__contracts__.get(self)!=None, \ - "Function not set yet." + "Attribute not set yet." return instance.__contracts__.get(self) - # return the method without binding it to the instance. + # return the stored data. - def __set__(self, instance, func): + def __set__(self, instance, value): if instance.__dict__.get('__contracts__')==None: instance.__contracts__={} - - instance.__contracts__[self]=self.__dict__['contract'](func) - # use dict to accsess the unbound contract + + instance.__contracts__[self]=self.__dict__['contract'](value) + # Use __dict__ to accsess the contract without binding to + # self. Apply the contract/check. Store the result with the + # descriptor as the key. + + From 9501baca629c27723e6f99fe9732fcc35682ae48 Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Tue, 18 Nov 2014 19:44:22 -0500 Subject: [PATCH 08/10] minor doc string edit --- src/contracts/main.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/contracts/main.py b/src/contracts/main.py index dfc66b5..c06aa2c 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -725,15 +725,15 @@ def can_accept_self_plus_one_argument(callable_thing): class ContractAttribute(object): - """ A descriptor for object attributes that enforces a contract - check on whatever object or function the attribute is set - to. For a function, the function would not be passed the self - argument (because that breaks encapsulation - you can still - actively pass self if you want). Setting up an attribute like - this is useful when an API that is expressed as an object - requires a client function to work with. In such cases you - want to both communicate expectations with regards to the - needed function and verify that they are met. + """ ContractAttribute is a descriptor for object attributes that + enforces a contract check on whatever object or function the + attribute is set to. For a function, the function would not be + passed the self argument (because that breaks encapsulation - + you can still actively pass self if you want). Setting up an + attribute like this is useful when an API that is expressed as + an object requires a client function to work with. In such + cases you want to both communicate expectations with regards + to the needed function and verify that they are met. Usage example: From 238c8b4cd10ac48efe1a944089c45e02f782d3e5 Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Fri, 21 Nov 2014 13:47:13 -0500 Subject: [PATCH 09/10] Disable flag support, more pyhthonic style, doc string edit --- src/contracts/main.py | 54 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/contracts/main.py b/src/contracts/main.py index c06aa2c..9277672 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -726,14 +726,19 @@ def can_accept_self_plus_one_argument(callable_thing): class ContractAttribute(object): """ ContractAttribute is a descriptor for object attributes that - enforces a contract check on whatever object or function the - attribute is set to. For a function, the function would not be + enforces a contract check on any object or function the + attribute is set to. + + For a function attribute the function will get decorated with + the contract, so that it will check its inputs and outputs + when it is called. When called the function would not be passed the self argument (because that breaks encapsulation - - you can still actively pass self if you want). Setting up an - attribute like this is useful when an API that is expressed as - an object requires a client function to work with. In such - cases you want to both communicate expectations with regards - to the needed function and verify that they are met. + you can still actively pass self if you want, just add it to + the contract). Setting up a function attribute in this way is + useful when an API that is expressed as an object requires a + client function to work with. In such cases you want to both + communicate expectations with regards to the needed function + and verify that they are met. Usage example: @@ -775,8 +780,9 @@ class spam(object): try: eggs.y=3 except ContractNotRespected as detail: - print str(detail)[:180] - """ + print str(detail)[:180] + + """ def _check(self,check_string,value): check(check_string, value) @@ -784,6 +790,9 @@ def _check(self,check_string,value): def __init__(self, contract_instance): + if all_disabled(): + return + from functools import partial from types import FunctionType, StringType @@ -802,22 +811,25 @@ def __init__(self, contract_instance): def __get__(self, instance, owner): - if instance.__dict__.get('__contracts__')==None: - instance.__contracts__={} - - assert instance.__contracts__.get(self)!=None, \ - "Attribute not set yet." - + if not hasattr(instance, '__contracts__'): + instance.__contracts__ = {} + + if self not in instance.__contracts__: + raise AttributeError("Attribute not set yet.") + return instance.__contracts__.get(self) # return the stored data. def __set__(self, instance, value): - if instance.__dict__.get('__contracts__')==None: - instance.__contracts__={} + if not hasattr(instance, '__contracts__'): + instance.__contracts__ = {} - instance.__contracts__[self]=self.__dict__['contract'](value) - # Use __dict__ to accsess the contract without binding to - # self. Apply the contract/check. Store the result with the - # descriptor as the key. + if all_disabled(): + instance.__contracts__[self]=value + else: + instance.__contracts__[self]=self.__dict__['contract'](value) + # Use __dict__ to accsess the contract without binding to + # self. Apply the contract/check. Store the result with the + # descriptor as the key. From 9b971cde9c16c6518170c19c6fe29f43aebdc7bb Mon Sep 17 00:00:00 2001 From: Yaniv Ben-Ami Date: Fri, 21 Nov 2014 19:03:14 -0500 Subject: [PATCH 10/10] striping out support for functions, shorter doc string, rename to Attribute --- src/contracts/__init__.py | 2 +- src/contracts/main.py | 93 ++++++++------------------------------- 2 files changed, 19 insertions(+), 76 deletions(-) diff --git a/src/contracts/__init__.py b/src/contracts/__init__.py index 34ec42e..d9d6b26 100644 --- a/src/contracts/__init__.py +++ b/src/contracts/__init__.py @@ -15,7 +15,7 @@ from .main import (check, fail, check_multiple, contract_decorator, contracts_decorate as decorate, - parse_flexible_spec as parse, ContractAttribute) + parse_flexible_spec as parse, Attribute) # Just make them appear as belonging to the "contracts" Module diff --git a/src/contracts/main.py b/src/contracts/main.py index 9277672..bf8dd0a 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -724,91 +724,37 @@ def can_accept_self_plus_one_argument(callable_thing): return True -class ContractAttribute(object): - """ ContractAttribute is a descriptor for object attributes that - enforces a contract check on any object or function the - attribute is set to. - - For a function attribute the function will get decorated with - the contract, so that it will check its inputs and outputs - when it is called. When called the function would not be - passed the self argument (because that breaks encapsulation - - you can still actively pass self if you want, just add it to - the contract). Setting up a function attribute in this way is - useful when an API that is expressed as an object requires a - client function to work with. In such cases you want to both - communicate expectations with regards to the needed function - and verify that they are met. +class Attribute(object): + """ Attribute is a descriptor for object attributes that + enforces a check on any object the attribute is set + to. Usage example: -from contracts import ContractAttribute, contract, ContractNotRespected +from contracts import Attribute, ContractNotRespected class spam(object): - f=ContractAttribute(contract(arg='float,>0')) #for functions - x=ContractAttribute('float,>0') #for scalars using string - y=ContractAttribute(float) #for scalars using type + x=Attribute('float,>0') eggs=spam() -from math import log, exp, pi -eggs.f=lambda (arg): log(arg) - -print "eggs.f(e)=" + str(eggs.f(exp(1.0))) - -try: - print "eggs.f=" + str(eggs.f(-1.0)) -except ContractNotRespected as detail: - print detail - -print "Attempting eggs.x=pi" -eggs.x=pi +print "Attempting eggs.x=1.0" +eggs.x=1.0 print "eggs.x=" + str(eggs.x) -print "Attempting eggs.x=-pi" +print "Attempting eggs.x=-1.0" try: - eggs.x=-pi + eggs.x=-1.0 except ContractNotRespected as detail: - print detail - -print "Attempting eggs.y=2*pi" -eggs.y=2*pi -print "eggs.y=" + str(eggs.y) -print "eggs.x=" + str(eggs.x) - -print "Attempting eggs.y=3" -try: - eggs.y=3 -except ContractNotRespected as detail: - print str(detail)[:180] - - """ - - def _check(self,check_string,value): - check(check_string, value) - return value + print detail + """ - def __init__(self, contract_instance): + def __init__(self, check_string): if all_disabled(): return - - from functools import partial - from types import FunctionType, StringType - - if isinstance(contract_instance,FunctionType): - # If it is a contract store the contract - self.contract=contract_instance - - elif isinstance(contract_instance,StringType): - # If it is a string send it to check and store the partial - self.contract=partial(self._check,contract_instance) - - else: - # Assume it is a type. Get the name of the type, send - # it to check and store the result. - self.contract=partial(self._check,contract_instance.__name__) + self.check_string=check_string def __get__(self, instance, owner): if not hasattr(instance, '__contracts__'): @@ -824,12 +770,9 @@ def __set__(self, instance, value): if not hasattr(instance, '__contracts__'): instance.__contracts__ = {} - if all_disabled(): - instance.__contracts__[self]=value - else: - instance.__contracts__[self]=self.__dict__['contract'](value) - # Use __dict__ to accsess the contract without binding to - # self. Apply the contract/check. Store the result with the - # descriptor as the key. + if not all_disabled(): + check(self.check_string,value) + instance.__contracts__[self]=value +