Open yaniv256 opened 9 years ago
Hi Yaniv, This looks like something very useful. I'm currently traveling for a few days so I will check it out in detail this weekend. A minor comment is that "ContractAttribute" is quite long. What's the smallest meaningful name for that?
I imagine that this depends on other libraries implementing attributes---I don't use this design pattern much so I wouldn't know.
a.
On Tuesday, November 18, 2014, Yaniv Ben-Ami notifications@github.com wrote:
Hi Andrea,
I wrote a descriptor for enforcing contracts. From the doc string:
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 contracts import ContractAttribute, contract, 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
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=piprint "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_piprint "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]
Output:
eggs.f(e)=1.0 Breach for argument 'arg' to
(). Condition -1.0 > 0 not respected checking: >0 for value: Instance of float: -1.0 checking: float,>0 for value: Instance of float: -1.0 Attempting eggs.x=pi eggs.x=3.14159265359 Attempting eggs.x=-pi Condition -3.14159265359 > 0 not respected checking: >0 for value: Instance of float: -3.141592653589793 checking: float,>0 for value: Instance of float: -3.141592653589793 Attempting eggs.y=2*pi eggs.y=6.28318530718 eggs.x=3.14159265359 Attempting eggs.y=3 Could not satisfy any of the 3 clauses in Float|np_scalar_float|np_scalar,array(float). ---- Clause #1: Float | Expected type 'float', got 'int'. | checking: Float for value
You can merge this Pull Request by running
git pull https://github.com/yaniv256/contracts ContractAttribute
Or view, comment on, or merge it at:
https://github.com/AndreaCensi/contracts/pull/30 Commit Summary
- before revert on backport.py
- Added support for callable objects and bound methods
- ContractAtribute added to main
- Added doc string
- Changed the name of the private attribute used to store wrapped values to contracts
- a bit more info in doc string
- Added support for scalar attributes
File Changes
- M src/contracts/init.py https://github.com/AndreaCensi/contracts/pull/30/files#diff-0 (2)
- M src/contracts/main.py https://github.com/AndreaCensi/contracts/pull/30/files#diff-1 (109)
Patch Links:
- https://github.com/AndreaCensi/contracts/pull/30.patch
- https://github.com/AndreaCensi/contracts/pull/30.diff
— Reply to this email directly or view it on GitHub https://github.com/AndreaCensi/contracts/pull/30.
(sent from mobile device, please pardon typos and brevity)
Thanks!
I guess we could go with something like "verify", "guard", "watch" or "attach". It would be nice to have it sound like an original python keyword.
No, I used just functools and types, both from the standard library.
On Tue, Nov 18, 2014 at 8:07 PM, Yaniv Ben-Ami notifications@github.com wrote:
No, I used just functools and types, both from the standard library.
Sorry let me reword my comment: I meant, if there is a popular library implementing descriptors, then the syntax in PyContracts should be similar to it. I will look at the topic when I come back home.
thanks for the contribution!
A.
Andrea Censi | LIDS / MIT | http://censi.mit.edu
BTW, I learned how to do descriptors from Chris Beaumont's excellent writeup (which I found via google).
Chris - I changed your design pattern a bit to hold the data in a dict on the instances, rather than on the descriptors. I hope it is as valid, but it would be good if you review.
@ChrisBeaumont you are the expert with descriptors. What do you think of this implementation?
This looks good to me! This is very similar to how Traits works (http://code.enthought.com/projects/traits/)
I have a few style comments that I'll leave inline.
@AndreaCensi regarding Traits and what to name these:
IPython has a "traitlets" module they use, whose API is similar to traits: http://ipython.org/ipython-doc/dev/api/generated/IPython.utils.traitlets.html
The problem is that both of these libraries are more "MyPy"-style, in that they have many special-purpose classes like Float
, Boolean
to express type info, rather than the DSL-style approach contracts uses. So they don't provide a precedent for whether to call these things Properties
or something else.
I personally like Property
or Attribute
OK. For my own project I need functions, so I'll use my own fork until the arrow notation is ready. But for the pull request I scaled back function support. It's a trivial descriptor now, and should be fairly robust.
I also renamed to Attribute.
I tried to merge this, but the two changes you made to main.py (one involving using partial functions) make tests fail in nonobvious ways.
I'm working on this branch: https://github.com/AndreaCensi/contracts/tree/yaniv256-ContractAttribute You can set Yaniv = False or True alternatively to enable/disable your changes.
Run nosetests contracts
to see what tests fail.
Hi Andrea,
I wrote a descriptor for enforcing contracts. From the doc string:
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:
Output: