AndreaCensi / contracts

PyContracts is a Python package that allows to declare constraints on function parameters and return values. Contracts can be specified using Python3 annotations, or inside a docstring. PyContracts supports a basic type system, variables binding, arithmetic constraints, and has several specialized contracts and an extension API.
http://andreacensi.github.io/contracts/
Other
398 stars 62 forks source link

Scoped variable RValue #27

Closed ChrisBeaumont closed 9 years ago

ChrisBeaumont commented 9 years ago

Still a work in progress, but curious what you think about this approach. If this looks worth pursuing to you, the remaining TODO items would be:

AndreaCensi commented 9 years ago

All, right, looks like it can be implemented in a cleaner way than I thought, apart from the naughty callstack examination part.

Regarding the glyph, "!" is logical NOT in C, so let's avoid that. Maybe "$var"?

Regarding the exception NameError, I believe it would be better to have all exceptions derive from ContractException.

Need to add "pytest" to tests_require in setup.py.

And now for the hard part: the tests fail for Python 3:

...
File "/home/shippable/build_ve/python/3.2/lib/python3.2/site-packages/pyparsing.py", line 1046, in _parseCache
  value = self._parseNoCache( instring, loc, doActions, callPreParse )
File "/home/shippable/build_ve/python/3.2/lib/python3.2/site-packages/pyparsing.py", line 1015, in _parseNoCache
  tokens = fn( instring, tokensStart, retTokens )
File "/home/shippable/build_ve/python/3.2/lib/python3.2/site-packages/pyparsing.py", line 779, in wrapper
  ret = func(*args[limit[0]:])
File "/home/shippable/workspace/src/github.com/AndreaCensi/contracts/src/contracts/library/variables.py", line 159, in parse_action
  val = cls._lookup_from_calling_scope(tokens[0])
File "/home/shippable/workspace/src/github.com/AndreaCensi/contracts/src/contracts/library/variables.py", line 148, in _lookup_from_calling_scope
  f = s()
File "/home/shippable/workspace/src/github.com/AndreaCensi/contracts/src/contracts/library/variables.py", line 132, in find_decorate
  idx = fcodes.index(decorate.func_code)
AttributeError: 'function' object has no attribute 'func_code'
ChrisBeaumont commented 9 years ago

Looks like func_code was renamed to __code__ in py3 https://docs.python.org/3.0/whatsnew/3.0.html#operators-and-special-methods

I can definitely restructure to avoid pytest -- looks like you're using nose?

AndreaCensi commented 9 years ago

Yes, the tests use nose currently.​

ChrisBeaumont commented 9 years ago

Hmm I tried changing ! to $, and I'm getting some errors I don't understand:

In [1]: from contracts import parse

In [2]: z = 5

In [3]: parse('$z')
---------------------------------------------------------------------------
ContractSyntaxError                       Traceback (most recent call last)
<ipython-input-3-f514e8760b0b> in <module>()
----> 1 parse('$z')

/Users/beaumont/contracts/src/contracts/main.py in parse_flexible_spec(spec)
    328         return spec
    329     elif isinstance(spec, str):
--> 330         return parse_contract_string(spec)
    331     elif can_be_used_as_a_type(spec):
    332         from .library import CheckType

/Users/beaumont/contracts/src/contracts/main.py in parse_contract_string(string)
     91         where = Where(string, line=e.lineno, column=e.col)
     92         msg = '%s' % e
---> 93         raise ContractSyntaxError(msg, where=where)
     94 
     95 

ContractSyntaxError: Expected "(" (at char 1), (line:1, col:2)

 line  1 >$z
           ^
           |
           here or nearby

Is $ a reserved/special value by any chance? The syntax definition is

scoped_variables = (S('$') + Word(alphanums + '_'))

AndreaCensi commented 9 years ago

Oh, yes, I already used "$" for something else. The syntax "$(...)" evaluates the content of the parentheses within a separate context.

See the definition in separate_context.py:

 sepcon = (Group(Literal('$') - Literal('(') -

I think the issue is the "-" --- try changing that in

 sepcon = (Group(Literal('$') + Literal('(') -
AndreaCensi commented 9 years ago

The construct "token1 - token2" in PyParsing means that you always expect token2 after token1 --- which is the error you are getting.

ChrisBeaumont commented 9 years ago

Thanks!

AndreaCensi commented 9 years ago

All right, it's merged in master --- before documenting it I will look for occasions to use it in my repos and think if there is some obvious problem.