1

Hopefully, this will be my last question in the series (my project is almost over, unless the requirements change).

I'm working on writing some constraints for a class method using PyContract (not PyContracts). In one of my functions, I have an invariant. However, not all the variables that make up this postcondition are input params to the function; some are variables local to the scope of the function itself (loop counters, etc). How/can I capture them in the postcondition clause in my contract?

Here is an MWE of code that I've been working with:

def foo(*args, **kwargs):
    """
        pre:
            # some preconditions
        inv:
            # something that's clearly false
            1 == 2
        post:
            g < arg5
    """

    arg1 = kwargs['arg1']
    arg2 = kwargs['arg2']
    arg3 = kwargs['arg3']
    arg4 = kwargs['arg4']
    arg5 = kwargs['arg5']

    g = 0
    while g < arg5:
        vars = generate_data(arg1)
        best = max(arg2(var)for var in vars)
        if best >= arg3:
            return best
        # do stuff
        g += 1

The problem arises when PyContract tells me that it doesn't know the names g and arg5. I changed arg5 to kwargs['arg5'] in the contract, but PyContract still doesn't know what g is. How/Could I fix this problem?

The Error that I get is:

Traceback (most recent call last):
  File "/Users/ashwin/github/local/Genetic-Framework/Genetic-Framework/Genetic/GA.py", line 245, in <module>
    answer = runTSPGA(*settings, **settings)
  File "<string>", line 3, in __assert_runTSPGA_chk
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/contract.py", line 1135, in call_public_function_all
    return _call_one_all(func, va, ka)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/contract.py", line 1321, in _call_one_all
    func.__assert_post(old, result, *va, **ka)
  File "<string>", line 4, in __assert_runTSPGA_post
NameError: global name 'g' is not defined

Adding g to the postconditions declaration as follows also does not help:

post[kwargs, g]:

Further, it is interesting that the code for the invariant checking doesn't seem to run at all

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
  • Could you add the verbatim error message to your question? It helps for context. –  Oct 24 '12 at 05:01
  • Isn't `kwagrs['agr#']` supposed to be `kwargs['arg#']` ? – Adam Eberlin Oct 24 '12 at 05:03
  • @AdamEberlin: Yes. That's a stupid typo – inspectorG4dget Oct 24 '12 at 05:06
  • @AdamEberlin: I have changed the question to be more faithful to what's actually happening. I misread some of the errors the first time around – inspectorG4dget Oct 24 '12 at 05:22
  • @Michael, your edit was [originally rejected](http://stackoverflow.com/review/suggested-edits/866755), but was resubmitted and [eventually approved](http://stackoverflow.com/review/suggested-edits/866772). Good catch. – Adam Eberlin Oct 24 '12 at 05:26
  • Interesting: even when I remove `pre` and `post` and leave `inv` as the only contract, it doesn't seem to get executed. It's getting pretty late, I'm going to call it a night. Will check-in in the morning – inspectorG4dget Oct 24 '12 at 05:38

1 Answers1

0

The postconditions are being run after the function has completed running, which means that the stack frame containing the function locals (such as arg5 and g) is no longer in scope.

This isn't too surprising for a library implementing contracts though: the whole point is to declare constraints on the interface a function is supposed to implement, while you seem to want to check the internal state of the function that is not made visible in the return value.

Two ways you could move forward are:

  1. Make the function return the state that you want to check with a contract. This might not be appropriate though if the internal state isn't part of the function's interface.

  2. Use something other than PyContracts to enforce your constraints, such as the assert statement, or perhaps a raise AssertionError("while loop should have returned early") statement after the loop.

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
James Henstridge
  • 42,244
  • 6
  • 132
  • 114
  • The invariants are being run at all. I demonstrated this with the example of the clearly wrong invariant `1==2`. What you say makes sense if you were talking about the postconditions – inspectorG4dget Oct 24 '12 at 18:14
  • Well your question was about postconditions, so that's what I put in my answer. As for the addendum about the invariants, the PyContract documentation only mentions invariants w.r.t. modules and classes. Are you sure that syntax is supposed to be supported for functions? And if it is, what would you expect it to provide that preconditions and postconditions don't? – James Henstridge Oct 25 '12 at 03:54
  • I see what you mean. I want to check as invariants, variables that are specific to the genetic algorithm being run. I suppose I'll have to wrap it up into a class to get the invariants to work right – inspectorG4dget Oct 25 '12 at 04:11
  • If you are trying to ensure that the internal state of the function is consistent at certain points while it is running, then assert statements are probably what you want. – James Henstridge Oct 25 '12 at 05:15
  • I thought about assert statements. They would work. Another idea would be to export that part of a function into another function (and use pre/post conditions on that function). A third idea is to wrap the function into a class. This third option seems most attractive to me ATM. – inspectorG4dget Oct 25 '12 at 05:19