
Chris Angelico writes:
Okay, but what is the contract that's being violated when you use data= ? How would you define the contract that this would track? That's what I'm asking.
The output is input to some other component. In production, that component would not be under your control, perhaps, but in testing it surely is. The contract would be associated with the other component, and it would be "my input is JSON".
Alternatively, if "your" component is being used in a protocol that specifies JSON, the contract could be "what I post is JSON". Presumably that contract can't be checked in production, but in testing it could be. If the inputs to your component satisfy their contracts, but JSON isn't coming out, the problem is in your component.
Ie, I don't see how DbC can diagnose *how* a component is broken, in general. It *can* localize the breakage, and provide some hints based on the particular contract that is "violated".
I think this shows that in a broad class of cases, for existing code, DbC doesn't do much that a developer with a debugger (such as print() ;-) can't already do, and the developer can do it much more flexibly.
However, getting meta,
Just had a confused developer wondering why calling an API with session.post(...., data={...some object dict here}) didn't work properly. (Solved by s/data/json)
session.post seems to be a pretty horrible API. You'd think this would either be
session.post_json(..., data={...})
or
session.post(..., data={...}, format='json')
with the former preferred (and session.post deprecated in favor of session.form_encoded; I guess you could also use the latter and require the 'format' argument).
How would this help in the DbC context? Your server (if you own it), or your mock server in the test suite, will complain that its contract "my input is JSON" is being violated, because it's explicitly an entry condition, your programmer looks at the component that's supposed to produce JSON, sees "form_encoded" either in the function name or the format argument's value, and the likelihood of confusion is small.
The contribution of DbC here is not in the contract itself, but in the discipline of thinking "how would I write this contract so that its violation would point me to the coding error?", which leads to refactoring the 'post' method.
Is DbC better than the server doing "assert is_valid_json(input)"? That too works best with the suggested refactoring. Evidently it's not better, but there are things that assert can't do. For example, the server might incrementally parse the JSON, yielding useful subobjects as you go along. Then you can't just assert is_valid_json at the beginning of the response generator; you need to do this at the server level. A DbC toolkit would presumably provide a way to decorate the server with
try: server.accept_json_object() except JSONParseError as e: report_contract_violation(e)
This is, of course, all imaginary, and I have no idea whether it would work as suggested in practice. It will be interesting to me to see, not only Marko's contracts in a DbC-ized module, but also places where he demands refactoring so that an *informative* contract can be written.
Steve