<div dir="auto">Hi James,<div dir="auto">Just a PS to the previous syntax:</div><div dir="auto"><br></div><div dir="auto">with contracts:</div><div dir="auto">    with preconditions:</div><div dir="auto">        assert arg1 < arg2</div><div dir="auto"><br></div><div dir="auto">    with snapshot as S:</div><div dir="auto">        S.var1 = some_func(arg1)</div><div dir="auto">        with postconditions, \</div><div dir="auto">             result: </div><div dir="auto">                 # result would be annotated with "# type:" if return type is annotated.</div><div dir="auto">                  assert arg1 < S.var1 < arg2</div><div dir="auto"><br></div><div dir="auto">For classes:</div><div dir="auto">class SomeClass:</div><div dir="auto">    with invariants,</div><div dir="auto">        selfie as self: # type: SomeClass</div><div dir="auto">             assert 0 < self.x < sqrt(self.x)</div><div dir="auto"><br></div><div dir="auto">The advantage: no variable shadowing, valid python code, autocomplete works in Pycharm, even mypy could be made to work. "With contracts" makes it easier and less error prone to group preconditions and postconditions. The converter would check that there is no "with contracts" in the body of the function except in the first statement and the same for class invariants. </div><div dir="auto"><br></div><div dir="auto">icontract.dummies would provide these dummy context managers (all of them would raise exceptions on enter so that the code can not run by accident). The converter would add/remove these imports automatically.</div><div dir="auto"><br></div><div dir="auto">Cheers,</div><div dir="auto">Marko</div><div dir="auto">             </div></div><br><div class="gmail_quote"><div dir="ltr">Le sam. 29 sept. 2018 à 17:55, Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com">marko.ristin@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto">Hi James,<div dir="auto">What about a tool that we discussed, to convert contracts back and forth to readable form on IDe save/load with the following syntax:</div><div dir="auto"><br></div><div dir="auto">def some_func(arg1:int, arg2:int)-> int:</div><div dir="auto"># typing on the phone so no indent</div><div dir="auto">With requiring:</div><div dir="auto">    Assert arg1 < arg2, "some message"</div><div dir="auto">With snapshotting:</div><div dir="auto">    Var1= some_func(arg1)</div><div dir="auto"> </div><div dir="auto">    With ensuring:</div><div dir="auto">         If some_enabling_condition:</div><div dir="auto">            Assert arg1 + arg2 < var1</div><div dir="auto"><br></div><div dir="auto">If no snapshot, with ensuring is dedented. Only simple assignments allowed in snapshots,  only asserts and ifs allowed in require/ensure blocks. Result is reserved for the result of the function.</div><div dir="auto"><br></div><div dir="auto">No statements allowed in require/ensure.</div><div dir="auto"><br></div><div dir="auto">The same with class invariants.</div><div dir="auto"><br></div><div dir="auto">Works with ast and autocomplete in pycharm.</div><div dir="auto"><br></div><div dir="auto">Sorry for the hasty message :)</div><div dir="auto">Marko</div><div dir="auto"><br></div><div dir="auto"><br></div></div><br><div class="gmail_quote"><div dir="ltr">Le sam. 29 sept. 2018 à 07:36, Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com" target="_blank" rel="noreferrer">marko.ristin@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto">Hi James,<div dir="auto">I'm a bit short on time today, and would need some more time and attention to understand the proposal you wrote. I'll try to come back to you tomorrow. </div><div dir="auto"><br></div><div dir="auto">In any case, I need to refactor icontract's decorators to use conditions like lambda P: and lambda P, result: first before adding snapshot functionality. </div><div dir="auto"><br></div><div dir="auto">What about having @snapshot_with and @snapshot? @Snapshot_with does what you propose and @snapshot expects a lambda P, identifier: ?</div><div dir="auto"><br></div><div dir="auto">After the refactoring, maybe the same could be done for defining contracts as well? (Requires and requires_that?)</div><div dir="auto"><br></div><div dir="auto">If the documentation is clear, I'd expect the user to be able to distinguish the two. The first approach is shorter, and uses magic, but fails in some rare situations. The other method is more verbose, but always works.</div><div dir="auto"><br></div><div dir="auto">Cheers,</div><div dir="auto">Marko</div></div><br><div class="gmail_quote"><div dir="ltr">Le sam. 29 sept. 2018 à 00:35, James Lu <<a href="mailto:jamtlu@gmail.com" rel="noreferrer noreferrer" target="_blank">jamtlu@gmail.com</a>> a écrit :<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr">I am fine with your proposed syntax. It’s certainly lucid. Perhaps it would be a good idea to get people accustomed to “non-magic” syntax.</div><div dir="ltr"><br></div><div dir="ltr"><blockquote type="cite"><span style="background-color:rgba(255,255,255,0)">I still have a feeling that most developers would like to store the state in many different custom ways. </span><br></blockquote><div>Please explain. (Expressions like thunk(all)(a == b for a, b in P.arg.meth()) would be valid.)</div><div><blockquote type="cite"><span style="background-color:rgba(255,255,255,0)">I'm thinking mostly about all the edge cases which we would not be able to cover (and how complex that would be to cover them).</span><br></blockquote><div><div><br></div><div>Except for a > b > c being one flat expression with 5 members, it seems fairly easy to recreate an AST, which can then be compiled down to a code object. The code object can be fun with a custom “locals()”</div><div><br></div><div>Below is my concept code for such a P object.</div><div><br></div><div><p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">from ast import *</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures"># not done: enforce Singleton property on EmptySymbolType</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">class EmptySymbolType(object): ...</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">EmptySymbol = EmptySymbolType() # empty symbols are placeholders</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">class MockP(object):</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    # "^" is xor</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    @icontract.pre(lambda symbol, astnode: (symbol is None) ^ (astnode is None))</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    def __init__(self, symbol=None, value=EmptySymbol, astnode=None, initsymtable=(,)):</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        self.symtable = dict(initsymtable)</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        if symbol:</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">            self.expr = Expr(value=Name(id=symbol, ctx=Load()))</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">            self.symtable = {symbol: value}</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        else:</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">            self.expr = astnode    </span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        self.frozen = False</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    def __add__(self, other):</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        wrapped = MockP.wrap_value(other)</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        return MockP(astnode=Expr(value=BinOp(self.expr, Add(), wrapped.expr), initsymtable={**self.symtable, **wrapped.symtable})</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    def compile(self): ...</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    def freeze(self):</span></p><p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        # frozen objects wouldn’t have an overrided getattr, allowing for icontract to manipulate the MockP object using its public interface</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">        self.frozen = True</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    @classmethod</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">    def wrap_value(cls, obj):</span></p><p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">       # create a MockP object from a value. Generate a random identifier and set that as the key in symtable, the AST node is the name of that identifier, retrieving its value through simple expression evaluation.</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">       ...</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier;min-height:14px"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures"></span><br></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">thunk = MockP.wrap_value</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures">P = MockP('P')</span></p>
<p style="margin:0px;font-stretch:normal;font-size:14px;line-height:normal;font-family:Courier"><span style="font-size:14pt;font-variant-ligatures:no-common-ligatures"># elsewhere: ensure P is only accessed via valid “dot attribute access” inside @snapshot so contracts fail early, or don’t and allow Magic like __dict__ to occur on P.</span></p></div><div><div dir="ltr"><br>On Sep 27, 2018, at 9:49 PM, Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">marko.ristin@gmail.com</a>> wrote:<br><br></div><blockquote type="cite"><div dir="ltr"><div dir="ltr"><div><div>Hi James,<br><br></div>I still have a feeling that most developers would like to store the state in many different custom ways. I see also thunk and snapshot with wrapper objects to be much more complicated to implement and maintain; I'm thinking mostly about all the edge cases which we would not be able to cover (and how complex that would be to cover them). Then the linters need also to work around such wrappers... It might also scare users off since it looks like too much magic. Another concern I also have is that it's probably very hard to integrate these wrappers with mypy later -- but I don't really have a clue about that, only my gut feeling?<br><br></div>What about we accepted to repeat "lambda P, " prefix, and have something like this:<br><br><div>@snapshot(<br>  lambda P, some_name: len(P.some_property),<br></div><div>  lambda P, another_name: hash(P.another_property)<br></div><div>)<br><br></div><div>It's not too verbose for me and you can still explain in three-four sentences what happens below the hub in the library's docs.  A pycharm/pydev/vim/emacs plugins could hide the verbose parts. <br><br>I performed a small experiment to test how this solution plays with pylint and it seems OK that arguments are not used in lambdas.<br></div><div><br></div><div>Cheers,<br></div><div>Marko<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, 27 Sep 2018 at 12:27, James Lu <<a href="mailto:jamtlu@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">jamtlu@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr">Why couldn’t we record the operations  done to a special object and replay them?<div><br></div><div><blockquote type="cite"><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="auto"><blockquote type="cite"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><font color="#000000"><span style="background-color:rgba(255,255,255,0)">Actually, I think there is probably no way around a decorator that captures/snapshots the data before the function call with a lambda (or even a separate function). "Old" construct, if we are to parse it somehow from the condition function, would limit us only to shallow copies (and be complex to implement as soon as we are capturing out-of-argument values such as globals <i>etc.)</i>. Moreove, what if we don't need shallow copies? I could imagine a dozen of cases where shallow copy is not what the programmer wants: for example, s/he might need to make deep copies, hash or otherwise transform the input data to hold only part of it instead of copying (<i>e.g., </i>so as to allow equality check without a double copy of the data, or capture only the value of certain property transformed in some way).</span></font></div></div></div></div></div></div></blockquote></div></blockquote></div></div></blockquote></div><div><br><div>from icontract import snapshot, P, thunk<br><div>@snapshot(some_identifier=P.self.some_method(P.some_argument.some_attr))</div><div><br></div><div>P is an object of our own type, let’s call the type MockP. MockP returns new MockP objects when any operation is done to it. MockP * MockP = MockP. MockP.attr = MockP. MockP objects remember all the operations done to them, and allow the owner of a MockP object to re-apply the same operations </div><div><br></div><div>“thunk” converts a function or object or class to a MockP object, storing the function or object for when the operation is done. </div><div><br></div><div>thunk(function)(<MockP expression>)</div><div><br></div><div>Of course, you could also thunk objects like so: thunk(3) * P.number. (Though it might be better to keep the 3 after P.number in this case so P.number’s __mult__ would be invoked before 3’s __mult__ is invokes.</div><div><br></div><div><br></div><div>In most cases, you’d save any operations that can be done on a copy of the data as generated by @snapshot in @postcondiion. thunk is for rare scenarios where 1) it’s hard to capture the state, for example an object that manages network state (or database connectivity etc) and whose stage can only be read by an external classmethod  2) you want to avoid using copy.deepcopy.</div><div><br></div><div>I’m sure there’s some way to override isinstance through a meta class or dunder subclasshook.</div><div><br></div><div>I suppose this mocking method could be a shorthand for when you don’t need the full power of a lambda. It’s arguably more succinct and readable, though YMMV.</div><div><br></div><div>I look forward to reading your opinion on this and any ideas you might have.</div><div><br></div><div><div dir="ltr">On Sep 26, 2018, at 3:56 PM, James Lu <<a href="mailto:jamtlu@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">jamtlu@gmail.com</a>> wrote:<br><br></div><blockquote type="cite"><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div dir="ltr"><span></span></div><div dir="ltr"><div>Hi Marko,</div><div><br></div><div><div><span style="background-color:rgba(255,255,255,0)"></span></div><blockquote type="cite"><div><span style="background-color:rgba(255,255,255,0)">Actually, following on #A4, you could also write those as multiple decorators:<br></span></div><span style="background-color:rgba(255,255,255,0)">@snpashot(lambda _, some_identifier: some_func(_, some_argument.some_attr)<br>@snpashot(lambda _, other_identifier: other_func(_.self))</span></blockquote></div><div>Yes, though if we’re talking syntax using kwargs would probably be better.</div><div>Using “P” instead of “_”: (I agree that _ smells of ignored arguments)</div><div><br></div>@snapshot(some_identifier=lambda P: ..., some_identifier2=lambda P: ...)</div><div dir="ltr"><br></div><div dir="ltr">Kwargs has the advantage that you can extend multiple lines without repeating @snapshot, though many lines of @capture would probably be more intuitive since each decorator captures one variable.</div><div dir="ltr"><div><br><div dir="ltr"><blockquote type="cite"><div dir="ltr"><div dir="ltr"><font color="#000000"><span style="background-color:rgba(255,255,255,0)">Why uppercase "P" and not lowercase (uppercase implies a constant for me)?</span></font></div></div></blockquote></div><div dir="ltr">To me, the capital letters are more prominent and explicit- easier to see when reading code. It also implies its a constant for you- you shouldn’t be modifying it, because then you’d be interfering with the function itself.</div><div dir="ltr"><br></div><div dir="ltr">Side node: maybe it would be good to have an @icontract.nomutate (probably use a different name, maybe @icontract.readonly) that makes sure a method doesn’t mutate its own __dict__ (and maybe the __dict__ of the members of its __dict__). It wouldn’t be necessary to put the decorator on every read only function, just the ones your worried might mutate.</div><div dir="ltr"><br></div><div dir="ltr">Maybe a @icontract.nomutate(param=“paramname”) that ensures the __dict__ of all members of the param name have the same equality or identity before and after. The semantics would need to be worked out.</div><div dir="ltr"><br>On Sep 26, 2018, at 8:58 AM, Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">marko.ristin@gmail.com</a>> wrote:<br><br></div><blockquote type="cite"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div><div>Hi James,<br><br></div>Actually, following on #A4, you could also write those as multiple decorators:<br></div>@snpashot(lambda _, some_identifier: some_func(_, some_argument.some_attr)<br>@snpashot(lambda _, other_identifier: other_func(_.self))<br><br></div><div>Am I correct?<br><br></div><div>"_" looks a bit hard to read for me (implying ignored arguments).<br><br></div><div>Why uppercase "P" and not lowercase (uppercase implies a constant for me)? Then "O" for "old" and "P" for parameters in a condition:<br></div><div>@post(lambda O, P: ...)<br>?<br><br></div><div>It also has the nice property that it follows both the temporal and the alphabet order :)<br> </div></div><br><div class="gmail_quote"><div dir="ltr">On Wed, 26 Sep 2018 at 14:30, James Lu <<a href="mailto:jamtlu@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">jamtlu@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div>I still prefer snapshot, though capture is a good name too. We could use generator syntax and inspect the argument names.</div><div><br></div><div>Instead of “a”, perhaps use “_”. Or maybe use “A.”, for arguments. Some people might prefer “P” for parameters, since parameters sometimes means the value received while the argument means the value passed.</div><div><br></div><div>(#A1)</div><div><br></div><div>from icontract import snapshot, __</div>@snapshot(some_func(_.some_argument.some_attr) for some_identifier, _ in __)<div><br></div><div>Or (#A2)</div><div><br></div><div><span style="background-color:rgba(255,255,255,0)">@snapshot(some_func(some_argument.some_attr) for some_identifier, _, some_argument in __)</span></div><div><br><div dir="ltr">—</div><div dir="ltr">Or (#A3)</div><div dir="ltr"><br></div><div dir="ltr"><span style="background-color:rgba(255,255,255,0)">@snapshot(lambda some_argument,_,some_identifier: some_func(some_argument.some_attr))</span></div><div dir="ltr"><br></div><div dir="ltr">Or (#A4)</div><div dir="ltr"><br></div><div dir="ltr"><div dir="ltr"><span style="background-color:rgba(255,255,255,0)">@snapshot(lambda _,some_identifier: some_func(_.some_argument.some_attr))</span></div><div dir="ltr"><span style="background-color:rgba(255,255,255,0)">@snapshot(lambda _,some_identifier, other_identifier: some_func(_.some_argument.some_attr), other_func(_.self))</span></div><div dir="ltr"><span style="background-color:rgba(255,255,255,0)"><br></span></div><div dir="ltr"><span style="background-color:rgba(255,255,255,0)">I like #A4 the most because it’s fairly DRY and avoids the extra punctuation of </span></div><div dir="ltr"><span style="background-color:rgba(255,255,255,0)"><br></span></div><div dir="ltr"><pre><pre><font face="UICTFontTextStyleBody"><span style="white-space:normal;background-color:rgba(255,255,255,0)">@capture(<span style="font-weight:bold">lambda </span>a: {<span style="font-weight:bold">"some_identifier"</span>: some_func(a.some_argument.some_attr)})</span></font></pre></pre></div><div><span style="background-color:rgba(255,255,255,0)"><br></span></div>On Sep 26, 2018, at 12:23 AM, Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">marko.ristin@gmail.com</a>> wrote:<br><br></div><blockquote type="cite"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr">Hi,<br><br></div><div>Franklin wrote:<br></div><div dir="ltr"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The name "before" is a confusing name. It's not just something that<br>
happens before. It's really a pre-`let`, adding names to the scope of<br>
things after it, but with values taken before the function call. Based<br>
on that description, other possible names are `prelet`, `letbefore`,<br>
`predef`, `defpre`, `beforescope`. Better a name that is clearly<br>
confusing than one that is obvious but misleading.</blockquote><div><br></div><div>James wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I suggest that instead of “@<span class="m_8288624511820023947m_3334402052241244111m_476539388105357884m_4945669642492881947m_5435661742938828063gmail-il">before</span>” it’s “@<span class="m_8288624511820023947m_3334402052241244111m_476539388105357884m_4945669642492881947m_5435661742938828063gmail-il">snapshot</span>” and instead of “<span class="m_8288624511820023947m_3334402052241244111m_476539388105357884m_4945669642492881947m_5435661742938828063gmail-il">old</span>” it’s “<span class="m_8288624511820023947m_3334402052241244111m_476539388105357884m_4945669642492881947m_5435661742938828063gmail-il">snapshot</span>”.</blockquote></div><div><br></div><div>I like "snapshot", it's a bit clearer than prefixing/postfixing verbs with "pre" which might be misread (<i>e.g., </i>"prelet" has a meaning in Slavic languages and could be subconsciously misread, "predef" implies to me a pre-<i>definition</i> rather than prior-to-definition , "beforescope" is very clear for me, but it might be confusing for others as to what it actually refers to ). What about "@capture" (7 letters for captures <i>versus </i>8 for snapshot)? I suppose "@let" would be playing with fire if Python with conflicting new keywords since I assume "let" to be one of the candidates.<br><br></div><div>Actually, I think there is probably no way around a decorator that captures/snapshots the data before the function call with a lambda (or even a separate function). "Old" construct, if we are to parse it somehow from the condition function, would limit us only to shallow copies (and be complex to implement as soon as we are capturing out-of-argument values such as globals <i>etc.)</i>. Moreove, what if we don't need shallow copies? I could imagine a dozen of cases where shallow copy is not what the programmer wants: for example, s/he might need to make deep copies, hash or otherwise transform the input data to hold only part of it instead of copying (<i>e.g., </i>so as to allow equality check without a double copy of the data, or capture only the value of certain property transformed in some way).<br><br></div><div>I'd still go with the dictionary to allow for this extra freedom. We could have a convention: "a" denotes to the current arguments, and "b" denotes the captured values. It might make an interesting hint that we put "b" before "a" in the condition. You could also interpret "b" as "before" and "a" as "after", but also "a" as "arguments".<br><br><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><span style="color:rgb(0,0,178)">@capture</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>a: {<span style="color:rgb(0,128,128);font-weight:bold">"some_identifier"</span>: some_func(a.some_argument.some_attr)})<br><span style="color:rgb(0,0,178)">@post</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>b, a, result: b.some_identifier > result + a.another_argument.another_attr)<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>some_func(<span style="color:rgb(128,128,128)">some_argument: SomeClass</span>, <span style="color:rgb(128,128,128)">another_argument: AnotherClass</span>) -> SomeResult:<br>    ...</pre>"b" can be omitted if it is not used. Under the hub, all the arguments to the condition would be passed by keywords.<br><br></div><div>In case of inheritance, captures would be inherited as well. Hence the library would check at run-time that the returned dictionary with captured values has no identifier that has been already captured, and the linter checks that statically, before running the code. Reading values captured in the parent at the code of the child class might be a bit hard -- but that is case with any inherited methods/properties. In documentation, I'd list all the captures of both ancestor and the current class.<br></div><div><br></div><div>I'm looking forward to reading your opinion on this and alternative suggestions :)<br></div><div>Marko<br></div></div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, 25 Sep 2018 at 18:12, Franklin? Lee <<a href="mailto:leewangzhong%2Bpython@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">leewangzhong+python@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Sun, Sep 23, 2018 at 2:05 AM Marko Ristin-Kaufmann<br>
<<a href="mailto:marko.ristin@gmail.com" rel="noreferrer noreferrer noreferrer" target="_blank">marko.ristin@gmail.com</a>> wrote:<br>
><br>
> Hi,<br>
><br>
> (I'd like to fork from a previous thread, "Pre-conditions and post-conditions", since it got long and we started discussing a couple of different things. Let's discuss in this thread the implementation of a library for design-by-contract and how to push it forward to hopefully add it to the standard library one day.)<br>
><br>
> For those unfamiliar with contracts and current state of the discussion in the previous thread, here's a short summary. The discussion started by me inquiring about the possibility to add design-by-contract concepts into the core language. The idea was rejected by the participants mainly because they thought that the merit of the feature does not merit its costs. This is quite debatable and seems to reflect many a discussion about design-by-contract in general. Please see the other thread, "Why is design-by-contract not widely adopted?" if you are interested in that debate.<br>
><br>
> We (a colleague of mine and I) decided to implement a library to bring design-by-contract to Python since we don't believe that the concept will make it into the core language anytime soon and we needed badly a tool to facilitate our work with a growing code base.<br>
><br>
> The library is available at <a href="http://github.com/Parquery/icontract" rel="noreferrer noreferrer noreferrer noreferrer" target="_blank">http://github.com/Parquery/icontract</a>. The hope is to polish it so that the wider community could use it and once the quality is high enough, make a proposal to add it to the standard Python libraries. We do need a standard library for contracts, otherwise projects with conflicting contract libraries can not integrate (e.g., the contracts can not be inherited between two different contract libraries).<br>
><br>
> So far, the most important bits have been implemented in icontract:<br>
><br>
> Preconditions, postconditions, class invariants<br>
> Inheritance of the contracts (including strengthening and weakening of the inherited contracts)<br>
> Informative violation messages (including information about the values involved in the contract condition)<br>
> Sphinx extension to include contracts in the automatically generated documentation (sphinx-icontract)<br>
> Linter to statically check that the arguments of the conditions are correct (pyicontract-lint)<br>
><br>
> We are successfully using it in our code base and have been quite happy about the implementation so far.<br>
><br>
> There is one bit still missing: accessing "old" values in the postcondition (i.e., shallow copies of the values prior to the execution of the function). This feature is necessary in order to allow us to verify state transitions.<br>
><br>
> For example, consider a new dictionary class that has "get" and "put" methods:<br>
><br>
> from typing import Optional<br>
><br>
> from icontract import post<br>
><br>
> class NovelDict:<br>
>     def length(self)->int:<br>
>         ...<br>
><br>
>     def get(self, key: str) -> Optional[str]:<br>
>         ...<br>
><br>
>     @post(lambda self, key, value: self.get(key) == value)<br>
>     @post(lambda self, key: old(self.get(key)) is None and old(self.length()) + 1 == self.length(),<br>
>           "length increased with a new key")<br>
>     @post(lambda self, key: old(self.get(key)) is not None and old(self.length()) == self.length(),<br>
>           "length stable with an existing key")<br>
>     def put(self, key: str, value: str) -> None:<br>
>         ...<br>
><br>
> How could we possible implement this "old" function?<br>
><br>
> Here is my suggestion. I'd introduce a decorator "before" that would allow you to store whatever values in a dictionary object "old" (i.e. an object whose properties correspond to the key/value pairs). The "old" is then passed to the condition. Here is it in code:<br>
><br>
> # omitted contracts for brevity<br>
> class NovelDict:<br>
>     def length(self)->int:<br>
>         ...<br>
><br>
>     # omitted contracts for brevity<br>
>     def get(self, key: str) -> Optional[str]:<br>
>         ...<br>
><br>
>     @before(lambda self, key: {"length": self.length(), "get": self.get(key)})<br>
>     @post(lambda self, key, value: self.get(key) == value)<br>
>     @post(lambda self, key, old: old.get is None and old.length + 1 == self.length(),<br>
>           "length increased with a new key")<br>
>     @post(lambda self, key, old: old.get is not None and old.length == self.length(),<br>
>           "length stable with an existing key")<br>
>     def put(self, key: str, value: str) -> None:<br>
>         ...<br>
><br>
> The linter would statically check that all attributes accessed in "old" have to be defined in the decorator "before" so that attribute errors would be caught early. The current implementation of the linter is fast enough to be run at save time so such errors should usually not happen with a properly set IDE.<br>
><br>
> "before" decorator would also have "enabled" property, so that you can turn it off (e.g., if you only want to run a postcondition in testing). The "before" decorators can be stacked so that you can also have a more fine-grained control when each one of them is running (some during test, some during test and in production). The linter would enforce that before's "enabled" is a disjunction of all the "enabled"'s of the corresponding postconditions where the old value appears.<br>
><br>
> Is this a sane approach to "old" values? Any alternative approach you would prefer? What about better naming? Is "before" a confusing name?<br>
<br>
The dict can be splatted into the postconditions, so that no special<br>
name is required. This would require either that the lambdas handle<br>
**kws, or that their caller inspect them to see what names they take.<br>
Perhaps add a function to functools which only passes kwargs that fit.<br>
Then the precondition mechanism can pass `self`, `key`, and `value` as<br>
kwargs instead of args.<br>
<br>
For functions that have *args and **kwargs, it may be necessary to<br>
pass them to the conditions as args and kwargs instead.<br>
<br>
The name "before" is a confusing name. It's not just something that<br>
happens before. It's really a pre-`let`, adding names to the scope of<br>
things after it, but with values taken before the function call. Based<br>
on that description, other possible names are `prelet`, `letbefore`,<br>
`predef`, `defpre`, `beforescope`. Better a name that is clearly<br>
confusing than one that is obvious but misleading.<br>
<br>
By the way, should the first postcondition be `self.get(key) is<br>
value`, checking for identity rather than equality?<br>
</blockquote></div>
</div></blockquote><blockquote type="cite"><div dir="ltr"><span>_______________________________________________</span><br><span>Python-ideas mailing list</span><br><span><a href="mailto:Python-ideas@python.org" rel="noreferrer noreferrer noreferrer" target="_blank">Python-ideas@python.org</a></span><br><span><a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a></span><br><span>Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer noreferrer noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a></span><br></div></blockquote></div></div></blockquote></div>
</div></blockquote></div></div></div></div></div></div></blockquote></div></div></div></div></div></div></div></blockquote></div>
</div></blockquote></div></div></div></div></div></div></div></blockquote></div>
</blockquote></div>
</blockquote></div>