I now fully understand your use case. IIUC, what would make this easier is a syntax for defining preconditions, post conditions, and invariants that supports:
* inheritance, composition (mixins) This isn't possible with decorators because there's no reference to the post-decorated function/method. A conventional method naming scheme and composition like _pre_check_[004]_height() would be unreasonably verbose; though it would then be possible to 'relax' specific contracts by name. * A better flow? Decorators subvert the normal top-down source layout (and so require up-down eye movements and working memory to remember what's going on) * Test case generation Generate tests and fixtures to verify the preconditions, postconditions, and invariants. * An Interpretor/compilation flag to skip the contracts/tests/assertions PYTHONOPTIMIZE (-O, -OO), __debug__ Isn't there now an -X <...> interpretor flag in CPython? On Tuesday, August 28, 2018, Marko Ristin-Kaufmann <marko.ris...@gmail.com> wrote: > Hi, > To clarify the benefits of the contracts, let me give you an example from > our code base: > > @icontract.pre(lambda x: x >= 0) > @icontract.pre(lambda y: y >= 0) > @icontract.pre(lambda width: width >= 0) > @icontract.pre(lambda height: height >= 0) > @icontract.pre(lambda x, width, img: x + width <= pqry.opencv.width_of(img)) > @icontract.pre(lambda y, height, img: y + height <= > pqry.opencv.height_of(img)) > @icontract.post(lambda self: (self.x, self.y) in self) > @icontract.post(lambda self: (self.x + self.width - 1, self.y + self.height - > 1) in self) > @icontract.post(lambda self: (self.x + self.width, self.y + self.height) not > in self) > def __init__(self, img: np.ndarray, x: int, y: int, width: int, height: int) > -> None: > self.img = img[y:y + height, x:x + width].copy() > self.x = x > self.y = y > self.width = width > self.height = height > > def __contains__(self, xy: Tuple[int, int]) -> bool: > x, y = xy > return self.x <= x < self.x + self.width and \ > self.y <= y < self.y + self.height > > We use mypy and type annotations for typing, and we don't need contracts > for that. In this particular case, contracts are very handy to formulate > how we deal with pixels. If you carefully look at the contracts, you will > see: > * pixels are indexed starting from 0 (as oppose to starting from 1) > * region-of-interest needs to fit within an image. It can not be outside > of its boundaries > * pixels within the region-of-interest are in [x, x + width), [y, y + > height) (where "[" means inclusive and ")" exclusive) > > *Why not types?* These constraints are either impossible or very hard to > write as types. Moreover, I doubt that you could give such types meaningful > names, and I expect the readability to suffer immensely. I suppose any > constraints involving more than a couple of variables is hard to write as a > type. > > *Benefits.* You could write all of this in human-readable informal > docstring of a class, but then it would never be verified. This way we are > sure that contracts are always there. Whenever we test, we can be sure that > all the contracted properties hold. Mind that we do not test that they hold > for that single test case -- we automatically test that they hold for all > the test cases. > > This is, in my opinion, far superior to ambiguous human documentation or > print statements (which are deleted after the debugging). I suppose *any* > larger code base built by a team of more than one developer needs this kind > of rigor. The contracts are not meant to be used only in high-risk > applications -- they are meant to improve any code base. > > *Problem without standard support. *The current libraries (dpcontracts, > icontract) can deal with pre and postconditions and class invariants of a > concrete class. > > However, the current libraries break as soon as you have inheritance. > There is no way in python to inherit the function decorators and to modify > them. If we are to inherit from the above-mentioned class ROI, we need to > make sure that the invariants of the parent class hold (*e.g., *what is > contained in a ROI) as well as invariants of the child class. We might want > to weaken the requirements (*e.g., *a ROI that can deal with pixels > outside of an image) such that x and y can be an arbitrary numbers, not > restricted to 0 <= x < img.width and 0 <= y < img.height respectively. > > Additionally, without standard approach, we do not know how to deal with > contracts when we have a third-party tool that would like to use them (*e.g., > *for automatic generation of unit tests, static testing or visualization > in IDE or in documentation). > > @Wes Turner <wes.tur...@gmail.com>: If I understood you correctly, you > are looking for a library that gives you verbose messages when a contract > is breached. Please have a look at icontract library: > https://github.com/Parquery/icontract > > We added informative messages particularly because we wanted to have > verbose output when something goes wrong.This was helpful not only during > the development, but also in production since failure cases were hard to > reproduce and anticipate in the first place (otherwise, they wouldn't have > made it into the production). Here is an example with an error message: > > >>> class B:... def __init__(self) -> None:... self.x = 7...... > >>> def y(self) -> int:... return 2...... def __repr__(self) -> > >>> str:... return "instance of B"...>>> class A:... def > >>> __init__(self)->None:... self.b = B()...... def > >>> __repr__(self) -> str:... return "instance of A"...>>> > >>> SOME_GLOBAL_VAR = 13>>> @icontract.pre(lambda a: a.b.x + a.b.y() > > >>> SOME_GLOBAL_VAR)... def some_func(a: A) -> None:... pass...>>> an_a = > >>> A()>>> some_func(an_a) > Traceback (most recent call last): > ... > icontract.ViolationError: Precondition violated: (a.b.x + a.b.y()) > > SOME_GLOBAL_VAR:SOME_GLOBAL_VAR was 13 > a was instance of A > a.b was instance of B > a.b.x was 7 > a.b.y() was 2 > > > On Tue, 28 Aug 2018 at 03:19, Wes Turner <wes.tur...@gmail.com> wrote: > >> Thanks for the explanation. >> >> This may be a bit OT, >> but is there a good way to do runtime assertions (in particular for data >> validation) that's as easy as assert? >> >> - self.assertEqual (unittest.TestCase.assertEqual) is hardly usable in >> other class instances, >> - pytest can be used at runtime but has somewhat nontrivial overhead >> - I always end up writing e.g. _assert() and _assertEqual() that throw >> AssertionErrors and helpful error messages just like unittest. >> >> >> How do contracts differ from checking interfaces with e.g. zope.interface? >> https://zopeinterface.readthedocs.io/en/latest/verify.html >> >> I'll read up a bit more on design by contract. I seem to have >> conceptualized dbc as a composition approach with typed interfaces and type >> and value assertions, but that's not it? >> >> If the parameter value assertions in pycontracts aren't really contracts, >> and the mypy compile-time parameter and return type checks are not really >> contracts, and zope.interface verifications aren't really contracts; what >> does that leave in terms of abstract data types, preconditions, >> postconditions, and invariants? >> >> https://en.wikipedia.org/wiki/Design_by_contract >> >> On Monday, August 27, 2018, Steven D'Aprano <st...@pearwood.info > wrote: >> >>> On Mon, Aug 27, 2018 at 11:01:21AM -0400, Wes Turner wrote: >>> > Runtime checks: data validation & code validation >>> > Compile-time checks: code validation >>> > >>> > What sort of data validation is appropriate for assert statements or >>> > contacts that may be skipped due to trading performance for more risk >>> > ('optimized out')? >>> >>> That depends on what you mean by "data validation". >>> >>> Testing for bad input, or testing for predictable error states such as >>> I/O errors, missing files, permission errors, server down etc is >>> not appropriate for assertions (which includes contracts). >>> >>> The rule I use is that assertions are for: >>> >>> (1) testing your program state, which is under your control; and >>> >>> (2) communicating the intention of your program as executable >>> code rather than comments. >>> >>> The ultimate aim of contracts and assertions is to eventually disable >>> them when the program is bug-free. >>> >>> The Eiffel docs say: >>> >>> It should be clear from the preceding discussion that contracts >>> are not a mechanism to test for special conditions, for example >>> erroneous user input. For that purpose, the usual control >>> structures [...] are available [...] An assertion is instead a >>> correctness condition governing the relationship between two >>> software modules (not a software module and a human, or a >>> software module and an external device). ... Bluntly: >>> >>> Rule -- Assertion Violation: A run-time assertion violation is >>> the manifestation of a bug. >>> >>> https://www.eiffel.org/doc/eiffel/ET-_Design_by_Contract_ >>> %28tm%29%2C_Assertions_and_Exceptions >>> >>> For detecting *external* error states (anything to do with data that >>> comes from outside your program, like user input) you can never let your >>> guard down and never disable the test, because servers can always go >>> down, users can always give you bad data, files can always be corrupt. >>> It is unsafe to disable these tests and so these should not be >>> assertions. >>> >>> For a library function intended to be called by third-parties, the >>> function arguments aren't under the control of the library author so >>> should not be tested with assertions. But for an application where the >>> author controls those function arguments, they are under the author's >>> control and may be assertions or contracts. >>> >>> Design By Contract is partly a methodology and partly a set of syntax. >>> Its a way of thinking about the design of your program. In practice, you >>> don't have to buy 100% into DBC to get some benefit for it. A study done >>> a few years back looked at 21 large projects in Eiffel, JML (Java) and >>> Code Contracts for C# and found that 33% of the classes used contracts. >>> >>> http://se.ethz.ch/~meyer/publications/methodology/contract_analysis.pdf >>> >>> Like unit testing, you don't need 100% coverage to get benefit. 10% is >>> better than nothing, and 20% is better than 10%. >>> >>> I wrote more about assertions here: >>> >>> https://import-that.dreamwidth.org/676.html >>> >>> >>> -- >>> Steve >>> _______________________________________________ >>> Python-ideas mailing list >>> Python-ideas@python.org >>> https://mail.python.org/mailman/listinfo/python-ideas >>> Code of Conduct: http://python.org/psf/codeofconduct/ >>> >> _______________________________________________ >> Python-ideas mailing list >> Python-ideas@python.org >> https://mail.python.org/mailman/listinfo/python-ideas >> Code of Conduct: http://python.org/psf/codeofconduct/ >> >
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/