-
Notifications
You must be signed in to change notification settings - Fork 3
Description
As of 0.15.1, the contents of the with test block are implicitly lifted into a function. This in itself is fine; it is documented behavior, and arguably the simplest way to achieve what the macro needs to do.
The problem is that to declare the expression whose value should be checked to decide the result of the test, the syntax to do so is currently return expr. This is arguably an abuse of a standard construct, in conflict with its established meaning, and thus it will likely confuse code analyzers and human readers alike.
So, let's add an expr macro to make the declaration instead, thus making the code read more like standard Python. The name of the macro needs some thought:
returns[]is the closest to the intention, but will confuse humans as to why there is a separatereturnsvs. the standardreturn.result[],results_in[], orevaluates_into[]. Semantically slightly off; we want to declare a check, not a result.check[]. Checkmate in t(h)ree? Still, probably the best so far.- Whatever the final name is, it should be compact (preferably one short-ish word), while making the intent blindingly obvious.
Also, with this change, we should make return inside a with test block into a syntax error, because it will not actually exit the parent function containing the with test block. This is a breaking change, so it has to wait until 1.0.0 (which is the next major release after 0.15.0). The best thing to do is to already introduce the alternative syntax in 0.15.x, and make the use of return in a with test block emit a DeprecationWarning until 1.0.0, where the old syntax will be removed.
Any local variables assigned to will still be local to the test. But an unexpected scope boundary shouldn't be as much of a problem as the abuse of a standard syntactic construct. For this there is precedent in Python itself, since comprehensions and generator expressions introduce a scope boundary. So do unpythonic's continuations.
The solution is the same; if you need to be able to replace a thing in the parent scope, keep it in a box. (You can also use nonlocal or global as appropriate, but that may make the coverage analyzer error out if a variable is seemingly assigned to before its nonlocal declaration.)