<div dir="auto"><div dir="auto">I'm afraid that in reading the examples provided it is difficulties for me not simply to think that EVERY SINGLE ONE of them would be FAR easier to read if it were an `assert` instead.<div dir="auto"><br></div><div dir="auto">The API of the library is a bit noisy, but I think the obstacle it's more in the higher level design for me. Adding many layers of expensive runtime checks and many lines of code in order to assure simple predicates that a glance at the code or unit tests would do better seems wasteful.</div><div dir="auto"><br></div><div dir="auto">I just cannot imagine wanting to write or work on the kind of codebase that is down here. If some people or organizations want to come in this manner, sure a library is great. But I definitely don't want it in the syntax, nor even in the standard library.</div></div><br><div class="gmail_quote"><div dir="ltr">On Sat, Sep 15, 2018, 2:53 AM Marko Ristin-Kaufmann <<a href="mailto:marko.ristin@gmail.com" target="_blank" rel="noreferrer">marko.ristin@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="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Hi,<br></div>Let me make a couple of practical examples from the work-in-progress (<a href="https://github.com/Parquery/pypackagery" rel="noreferrer noreferrer" target="_blank">https://github.com/Parquery/pypackagery</a>, branch mristin/initial-version) to illustrate again the usefulness of the contracts and why they are, in my opinion, superior to assertions and unit tests. <br><br></div><div>What follows is a list of function signatures decorated with contracts from pypackagery library preceded by a human-readable description of the contracts.<br></div><div dir="ltr"><font size="2"><br></font></div><div><font size="2">The invariants tell us what format to expect from the related string properties.<br></font></div><div><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><font size="2"><span style="color:rgb(0,0,178)">@icontract.inv</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>self: self.name.strip() == <a href="http://self.name" rel="noreferrer noreferrer" target="_blank">self.name</a>)<br><span style="color:rgb(0,0,178)">@icontract.inv</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>self: self.line.endswith(<span style="color:rgb(0,128,0);font-weight:bold">"</span><span style="color:rgb(0,0,128);font-weight:bold">\n</span><span style="color:rgb(0,128,0);font-weight:bold">"</span>))<br><span style="color:rgb(0,0,128);font-weight:bold">class </span>Requirement:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""Represent a requirement in requirements.txt."""<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, name: <span style="color:rgb(0,0,128)">str</span>, line: <span style="color:rgb(0,0,128)">str</span>) -> <span style="color:rgb(0,0,128)">None</span>:<br>        <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">        Initialize.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">        </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> name: package name<br></span><span style="color:rgb(128,128,128);font-style:italic">        </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> line: line in the requirements.txt file<br></span><span style="color:rgb(128,128,128);font-style:italic">        """<br></span><span style="color:rgb(128,128,128);font-style:italic">        </span>...</font><br></pre>The postcondition tells us that the resulting map keys the values on their name property.<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)">@icontract.post</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>result: <span style="color:rgb(0,0,128)">all</span>(<a href="http://val.name" rel="noreferrer noreferrer" target="_blank">val.name</a> == key <span style="color:rgb(0,0,128);font-weight:bold">for </span>key, val <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.items()))<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>parse_requirements(text: <span style="color:rgb(0,0,128)">str</span>, filename: <span style="color:rgb(0,0,128)">str </span>= <span style="color:rgb(0,128,0);font-weight:bold">'<unknown>'</span>) -> Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement]:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">    Parse requirements file and return package name -> package requirement as in requirements.txt<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> text: content of the ``requirements.txt``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> filename: where we got the ``requirements.txt`` from (URL or path)<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:return</span><span style="color:rgb(128,128,128);font-style:italic">: name of the requirement (*i.e.* pip package) -> parsed requirement<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br>    ...<br></span></pre><br></div><div>The postcondition ensures that the resulting list contains only unique elements. Mind that if you returned a set, the order would have been lost.<br></div><div><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)">@icontract.post</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>result: <span style="color:rgb(0,0,128)">len</span>(result) == <span style="color:rgb(0,0,128)">len</span>(<span style="color:rgb(0,0,128)">set</span>(result)), <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>missing_requirements(module_to_requirement: Mapping[<span style="color:rgb(0,0,128)">str</span>, <span style="color:rgb(0,0,128)">str</span>],<br>                         requirements: Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement]) -> List[<span style="color:rgb(0,0,128)">str</span>]:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">    List requirements from module_to_requirement missing in the ``requirements``.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> module_to_requirement: parsed ``module_to_requiremnt.tsv``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> requirements: parsed ``requirements.txt``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:return</span><span style="color:rgb(128,128,128);font-style:italic">: list of requirement names<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br>    ...<br></span></pre>Here is a bit more complex example. <br>- The precondition A requires that all the supplied relative paths (rel_paths) are indeed relative (as opposed to absolute). <br>- The postcondition B ensures that the initial set of paths (given in rel_paths) is included in the results.<br></div><div>- The postcondition C ensures that the requirements in the results are the subset of the given requirements.<br></div><div>- The precondition D requires that there are no missing requirements (<i>i.e. </i>that each requirement in the given module_to_requirement is also defined in the given requirements).<br></div><div><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)">@icontract.pre</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>rel_paths: <span style="color:rgb(0,0,128)">all</span>(rel_pth.root == <span style="color:rgb(0,128,0);font-weight:bold">"" </span><span style="color:rgb(0,0,128);font-weight:bold">for </span>rel_pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>rel_paths))  # A<br><span style="color:rgb(0,0,178)">@icontract.post</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>rel_paths, result: <span style="color:rgb(0,0,128)">all</span>(pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.rel_paths <span style="color:rgb(0,0,128);font-weight:bold">for </span>pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>rel_paths),<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW,<br>    <span style="color:rgb(102,0,153)">description</span>=<span style="color:rgb(0,128,0);font-weight:bold">"Initial relative paths included"</span>)  # B<br><span style="color:rgb(0,0,178)">@icontract.post</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>requirements, result: <span style="color:rgb(0,0,128)">all</span>(<a href="http://req.name" rel="noreferrer noreferrer" target="_blank">req.name</a> <span style="color:rgb(0,0,128);font-weight:bold">in </span>requirements <span style="color:rgb(0,0,128);font-weight:bold">for </span>req <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.requirements),<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)  # C<br><span style="color:rgb(0,0,178)">@icontract.pre</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>requirements, module_to_requirement: missing_requirements(module_to_requirement, requirements) == [],<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)  # D<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>collect_dependency_graph(root_dir: pathlib.Path, rel_paths: List[pathlib.Path],<br>                             requirements: Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement],<br>                             module_to_requirement: Mapping[<span style="color:rgb(0,0,128)">str</span>, <span style="color:rgb(0,0,128)">str</span>]) -> Package:<br></pre></div><div><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(128,128,128);font-style:italic">    """<br></span><span style="color:rgb(128,128,128);font-style:italic">    Collect the dependency graph of the initial set of python files from the code base.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> root_dir: root directory of the codebase such as "/home/marko/workspace/pqry/production/src/py"<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> rel_paths: initial set of python files that we want to package. These paths are relative to root_dir.<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> requirements: requirements of the whole code base, mapped by package name<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> module_to_requirement: module to requirement correspondence of the whole code base<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :return</span><span style="color:rgb(128,128,128);font-style:italic">: resolved depedendency graph including the given initial relative paths,<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br></span></pre>I hope these examples convince you (at least a little bit :-)) that contracts are easier and clearer to write than asserts. As noted before in this thread, you can have the same <i>behavior</i> with asserts as 
long as you don't need to inherit the contracts. But the contract 
decorators make it very explicit what conditions should hold <i>without</i>
 having to look into the implementation. Moreover, it is very hard to 
ensure the postconditions with asserts as soon as you have a complex 
control flow <font size="2">since you would need to duplicate the assert at every return statement. (You could implement a context manager that ensures the postconditions, but a context manager is not more readable than decorators and you have to duplicate them as documentation in the docstring).<br></font><br>In my view, contracts are also superior to many kinds of tests. As the contracts are <i>always</i> enforced, they also enforce the correctness throughout the program execution whereas the unit tests and doctests only cover a list of selected cases. Furthermore, writing the contracts in these examples as doctests or unit tests would escape the attention of most less experienced programmers which are not  used to read unit tests as documentation. Finally, these unit tests would be much harder to read than the decorators (<i>e.g.</i>, the unit test would supply invalid arguments and then check for ValueError which is already a much more convoluted piece of code than the preconditions and postconditions as decorators. Such testing code also lives in a file separate from the original implementation making it much harder to locate and maintain). <br><br></div><div>Mind that the contracts <i>do not</i> <i>replace</i> the unit tests or the doctests. The contracts make merely tests obsolete that test that the function or class actually observes the contracts. Design-by-contract helps you skip those tests and focus on the more complex ones that test the behavior. Another positive effect of the contracts is that they make your tests deeper: if you specified the contracts throughout the code base, a test of a function that calls other functions in its implementation will also make sure that all the contracts of that other functions hold. This can be difficult to implement  with standard unit test frameworks.<br></div><div><br></div><div>Another aspect of the design-by-contract, which is IMO ignored quite often, is the educational one. Contracts force the programmer to actually sit down and think <i>formally</i> about the inputs and the outputs (hopefully?) <i>before</i> she starts to implement a function. Since many schools use Python to teach programming  (especially at high school level), I imagine writing contracts of a function to be a very good exercise in formal thinking for the students.<br></div><div><br></div><div>Please let me know what points <i>do not </i>convince you that Python needs contracts (in whatever form -- be it as a standard library, be it as a language construct, be it as a widely adopted and collectively maintained third-party library). I would be very glad to address these points in my next message(s).<br></div><div><br>Cheers,<br></div><div>Marko<br></div></div></div></div></div></div></div></div></div></div></div>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" rel="noreferrer noreferrer" target="_blank">Python-ideas@python.org</a><br>
<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><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer noreferrer noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</blockquote></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sep 15, 2018 2:53 AM, "Marko Ristin-Kaufmann" <<a href="mailto:marko.ristin@gmail.com">marko.ristin@gmail.com</a>> wrote:<br type="attribution"><blockquote class="quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Hi,<br></div>Let me make a couple of practical examples from the work-in-progress (<a href="https://github.com/Parquery/pypackagery" target="_blank" rel="noreferrer">https://github.com/Parquery/pypackagery</a>, branch mristin/initial-version) to illustrate again the usefulness of the contracts and why they are, in my opinion, superior to assertions and unit tests. <br><br></div><div>What follows is a list of function signatures decorated with contracts from pypackagery library preceded by a human-readable description of the contracts.<br></div><div dir="ltr"><font size="2"><br></font></div><div><font size="2">The invariants tell us what format to expect from the related string properties.<br></font></div><div><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><font size="2"><span style="color:rgb(0,0,178)">@icontract.inv</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>self: self.name.strip() == <a href="http://self.name" target="_blank" rel="noreferrer">self.name</a>)<br><span style="color:rgb(0,0,178)">@icontract.inv</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>self: self.line.endswith(<span style="color:rgb(0,128,0);font-weight:bold">"</span><span style="color:rgb(0,0,128);font-weight:bold">\n</span><span style="color:rgb(0,128,0);font-weight:bold">"</span>))<br><span style="color:rgb(0,0,128);font-weight:bold">class </span>Requirement:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""Represent a requirement in requirements.txt."""<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, name: <span style="color:rgb(0,0,128)">str</span>, line: <span style="color:rgb(0,0,128)">str</span>) -> <span style="color:rgb(0,0,128)">None</span>:<br>        <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">        Initialize.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">        </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> name: package name<br></span><span style="color:rgb(128,128,128);font-style:italic">        </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> line: line in the requirements.txt file<br></span><span style="color:rgb(128,128,128);font-style:italic">        """<br></span><span style="color:rgb(128,128,128);font-style:italic">        </span>...</font><br></pre>The postcondition tells us that the resulting map keys the values on their name property.<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)">@icontract.post</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>result: <span style="color:rgb(0,0,128)">all</span>(<a href="http://val.name" target="_blank" rel="noreferrer">val.name</a> == key <span style="color:rgb(0,0,128);font-weight:bold">for </span>key, val <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.items()))<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>parse_requirements(text: <span style="color:rgb(0,0,128)">str</span>, filename: <span style="color:rgb(0,0,128)">str </span>= <span style="color:rgb(0,128,0);font-weight:bold">'<unknown>'</span>) -> Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement]:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">    Parse requirements file and return package name -> package requirement as in requirements.txt<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> text: content of the ``requirements.txt``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> filename: where we got the ``requirements.txt`` from (URL or path)<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:return</span><span style="color:rgb(128,128,128);font-style:italic">: name of the requirement (*i.e.* pip package) -> parsed requirement<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br>    ...<br></span></pre><br></div><div>The postcondition ensures that the resulting list contains only unique elements. Mind that if you returned a set, the order would have been lost.<br></div><div><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)">@icontract.post</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>result: <span style="color:rgb(0,0,128)">len</span>(result) == <span style="color:rgb(0,0,128)">len</span>(<span style="color:rgb(0,0,128)">set</span>(result)), <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>missing_requirements(module_to_requirement: Mapping[<span style="color:rgb(0,0,128)">str</span>, <span style="color:rgb(0,0,128)">str</span>],<br>                         requirements: Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement]) -> List[<span style="color:rgb(0,0,128)">str</span>]:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""<br></span><span style="color:rgb(128,128,128);font-style:italic">    List requirements from module_to_requirement missing in the ``requirements``.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> module_to_requirement: parsed ``module_to_requiremnt.tsv``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:param</span><span style="color:rgb(128,128,128);font-style:italic"> requirements: parsed ``requirements.txt``<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(128,128,128);font-weight:bold">:return</span><span style="color:rgb(128,128,128);font-style:italic">: list of requirement names<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br>    ...<br></span></pre>Here is a bit more complex example. <br>- The precondition A requires that all the supplied relative paths (rel_paths) are indeed relative (as opposed to absolute). <br>- The postcondition B ensures that the initial set of paths (given in rel_paths) is included in the results.<br></div><div>- The postcondition C ensures that the requirements in the results are the subset of the given requirements.<br></div><div>- The precondition D requires that there are no missing requirements (<i>i.e. </i>that each requirement in the given module_to_requirement is also defined in the given requirements).<br></div><div><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)">@icontract.pre</span>(<span style="color:rgb(0,0,128);font-weight:bold">lambda </span>rel_paths: <span style="color:rgb(0,0,128)">all</span>(rel_pth.root == <span style="color:rgb(0,128,0);font-weight:bold">"" </span><span style="color:rgb(0,0,128);font-weight:bold">for </span>rel_pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>rel_paths))  # A<br><span style="color:rgb(0,0,178)">@icontract.post</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>rel_paths, result: <span style="color:rgb(0,0,128)">all</span>(pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.rel_paths <span style="color:rgb(0,0,128);font-weight:bold">for </span>pth <span style="color:rgb(0,0,128);font-weight:bold">in </span>rel_paths),<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW,<br>    <span style="color:rgb(102,0,153)">description</span>=<span style="color:rgb(0,128,0);font-weight:bold">"Initial relative paths included"</span>)  # B<br><span style="color:rgb(0,0,178)">@icontract.post</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>requirements, result: <span style="color:rgb(0,0,128)">all</span>(<a href="http://req.name" target="_blank" rel="noreferrer">req.name</a> <span style="color:rgb(0,0,128);font-weight:bold">in </span>requirements <span style="color:rgb(0,0,128);font-weight:bold">for </span>req <span style="color:rgb(0,0,128);font-weight:bold">in </span>result.requirements),<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)  # C<br><span style="color:rgb(0,0,178)">@icontract.pre</span>(<br>    <span style="color:rgb(0,0,128);font-weight:bold">lambda </span>requirements, module_to_requirement: missing_requirements(module_to_requirement, requirements) == [],<br>    <span style="color:rgb(102,0,153)">enabled</span>=icontract.SLOW)  # D<br><span style="color:rgb(0,0,128);font-weight:bold">def </span>collect_dependency_graph(root_dir: pathlib.Path, rel_paths: List[pathlib.Path],<br>                             requirements: Mapping[<span style="color:rgb(0,0,128)">str</span>, Requirement],<br>                             module_to_requirement: Mapping[<span style="color:rgb(0,0,128)">str</span>, <span style="color:rgb(0,0,128)">str</span>]) -> Package:<br></pre></div><div><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(128,128,128);font-style:italic">    """<br></span><span style="color:rgb(128,128,128);font-style:italic">    Collect the dependency graph of the initial set of python files from the code base.<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> root_dir: root directory of the codebase such as "/home/marko/workspace/pqry/production/src/py"<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> rel_paths: initial set of python files that we want to package. These paths are relative to root_dir.<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> requirements: requirements of the whole code base, mapped by package name<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :param</span><span style="color:rgb(128,128,128);font-style:italic"> module_to_requirement: module to requirement correspondence of the whole code base<br></span><span style="color:rgb(128,128,128);font-weight:bold">    :return</span><span style="color:rgb(128,128,128);font-style:italic">: resolved depedendency graph including the given initial relative paths,<br></span><span style="color:rgb(128,128,128);font-style:italic">    """<br></span></pre>I hope these examples convince you (at least a little bit :-)) that contracts are easier and clearer to write than asserts. As noted before in this thread, you can have the same <i>behavior</i> with asserts as 
long as you don't need to inherit the contracts. But the contract 
decorators make it very explicit what conditions should hold <i>without</i>
 having to look into the implementation. Moreover, it is very hard to 
ensure the postconditions with asserts as soon as you have a complex 
control flow <font size="2">since you would need to duplicate the assert at every return statement. (You could implement a context manager that ensures the postconditions, but a context manager is not more readable than decorators and you have to duplicate them as documentation in the docstring).<br></font><br>In my view, contracts are also superior to many kinds of tests. As the contracts are <i>always</i> enforced, they also enforce the correctness throughout the program execution whereas the unit tests and doctests only cover a list of selected cases. Furthermore, writing the contracts in these examples as doctests or unit tests would escape the attention of most less experienced programmers which are not  used to read unit tests as documentation. Finally, these unit tests would be much harder to read than the decorators (<i>e.g.</i>, the unit test would supply invalid arguments and then check for ValueError which is already a much more convoluted piece of code than the preconditions and postconditions as decorators. Such testing code also lives in a file separate from the original implementation making it much harder to locate and maintain). <br><br></div><div>Mind that the contracts <i>do not</i> <i>replace</i> the unit tests or the doctests. The contracts make merely tests obsolete that test that the function or class actually observes the contracts. Design-by-contract helps you skip those tests and focus on the more complex ones that test the behavior. Another positive effect of the contracts is that they make your tests deeper: if you specified the contracts throughout the code base, a test of a function that calls other functions in its implementation will also make sure that all the contracts of that other functions hold. This can be difficult to implement  with standard unit test frameworks.<br></div><div><br></div><div>Another aspect of the design-by-contract, which is IMO ignored quite often, is the educational one. Contracts force the programmer to actually sit down and think <i>formally</i> about the inputs and the outputs (hopefully?) <i>before</i> she starts to implement a function. Since many schools use Python to teach programming  (especially at high school level), I imagine writing contracts of a function to be a very good exercise in formal thinking for the students.<br></div><div><br></div><div>Please let me know what points <i>do not </i>convince you that Python needs contracts (in whatever form -- be it as a standard library, be it as a language construct, be it as a widely adopted and collectively maintained third-party library). I would be very glad to address these points in my next message(s).<br></div><div><br>Cheers,<br></div><div>Marko<br></div></div></div></div></div></div></div></div></div></div></div><div class="elided-text">
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank" rel="noreferrer">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</div></blockquote></div><br></div>