Package: icontract
icontracticontract providesdesign-by-contractto Python3 with informative
violation messages and inheritance.It also gives a base for a flourishing of a wider ecosystem:A linterpyicontract-lint,A sphinx plug-insphinx-icontract,A toolicontract-hypothesisfor automated testing and ghostwriting test files which infersHypothesisstrategies based on the contracts,together with IDE integrations such asicontract-hypothesis-vim,icontract-hypothesis-pycharm, andicontract-hypothesis-vscode,Directly integrated intoCrossHair, a tool for automatic verification of Python programs,together with IDE integrations such ascrosshair-pycharmandcrosshair-vscode, andAn integration withFastAPIthroughfastapi-icontractto enforce contracts on your HTTP API and display them
in OpenAPI 3 schema and Swagger UI, andAn extensive corpus,Python-by-contract corpus, of Python programs annotated with contracts for educational, testing and research purposes.Related ProjectsThere exist a couple of contract libraries. However, at the time of this writing (September 2018), they all required the
programmer either to learn a new syntax (PyContracts) or to write
redundant condition descriptions (e.g.,contracts,covenant,deal,dpcontracts,pyadbcandpcd).This library was strongly inspired by them, but we go two steps further.First, our violation message on contract breach are much more informative. The message includes the source code of the
contract condition as well as variable values at the time of the breach. This promotes don’t-repeat-yourself principle
(DRY) and spare the programmer the tedious task of repeating
the message that was already written in code.Second, icontract allows inheritance of the contracts and supports weakining of the preconditions
as well as strengthening of the postconditions and invariants. Notably, weakining and strengthening of the contracts
is a feature indispensable for modeling many non-trivial class hierarchies. Please see SectionInheritance.
To the best of our knowledge, there is currently no other Python library that supports inheritance of the contracts in a
correct way.In the long run, we hope that design-by-contract will be adopted and integrated in the language. Consider this library
a work-around till that happens. You might be also interested in the archived discussion on how to bring
design-by-contract into Python language onpython-ideas mailing list.TeasersWe give a couple of teasers here to motivate the library.
Please see the documentation available onicontract.readthedocs.iofor a full scope of its
capabilities.The script is also available as arepl.it post.>>>importicontract>>>@icontract.require(lambdax:x>3)...defsome_func(x:int,y:int=5)->None:...pass...>>>some_func(x=5)# Pre-condition violation>>>some_func(x=1)Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[1]>,line1in<module>:x>3:xwas1ywas5# Pre-condition violation with a description>>>@icontract.require(lambdax:x>3,"x must not be small")...defsome_func(x:int,y:int=5)->None:...pass...>>>some_func(x=1)Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[4]>,line1in<module>:xmustnotbesmall:x>3:xwas1ywas5# Pre-condition violation with more complex values>>>classB:...def__init__(self)->None:...self.x=7......defy(self)->int:...return2......def__repr__(self)->str:...return"instance of B"...>>>classA:...def__init__(self)->None:...self.b=B()......def__repr__(self)->str:...return"instance of A"...>>>SOME_GLOBAL_VAR=13>>>@icontract.require(lambdaa:a.b.x+a.b.y()>SOME_GLOBAL_VAR)...defsome_func(a:A)->None:...pass...>>>an_a=A()>>>some_func(an_a)Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[9]>,line1in<module>:a.b.x+a.b.y()>SOME_GLOBAL_VAR:SOME_GLOBAL_VARwas13awasinstanceofAa.bwasinstanceofBa.b.xwas7a.b.y()was2# Post-condition>>>@icontract.ensure(lambdaresult,x:result>x)...defsome_func(x:int,y:int=5)->int:...returnx-y...>>>some_func(x=10)Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[12]>,line1in<module>:result>x:resultwas5xwas10ywas5# Pre-conditions fail before post-conditions.>>>@icontract.ensure(lambdaresult,x:result>x)...@icontract.require(lambdax:x>3,"x must not be small")...defsome_func(x:int,y:int=5)->int:...returnx-y...>>>some_func(x=3)Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[14]>,line2in<module>:xmustnotbesmall:x>3:xwas3ywas5# Invariant>>>@icontract.invariant(lambdaself:self.x>0)...classSomeClass:...def__init__(self)->None:...self.x=-1......def__repr__(self)->str:...return"an instance of SomeClass"...>>>some_instance=SomeClass()Traceback(mostrecentcalllast):...icontract.errors.ViolationError:File<doctestREADME.rst[16]>,line1in<module>:self.x>0:selfwasaninstanceofSomeClassself.xwas-1InstallationInstall icontract with pip:pip3installicontractVersioningWe followSemantic Versioning. The version X.Y.Z indicates:X is the major version (backward-incompatible),Y is the minor version (backward-compatible), andZ is the patch version (backward-compatible bug fix).