As of 0.15.1, the contents of a 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 is currently return expr. This is effectively a keyword hijack — in the spirit of pytest's assert rewriting — where a standard Python construct is repurposed to mean something else inside a specific block. Not very pythonic. It conflicts with the established meaning of return and will confuse code analyzers and human readers alike.
Plan
Step 1 — 2.2.0 (non-breaking)
Add an expr macro to declare the tested expression, so the code reads more like standard Python. Name needs some thought:
returns[] — closest to intent, but will confuse humans as to why there is a separate returns vs. the standard return.
result[], results_in[], evaluates_into[] — semantically slightly off; we want to declare a check, not a result.
check[] — checkmate in t(h)ree? Probably the best so far.
- Whatever the final name, it should be compact (preferably one short-ish word) while making the intent blindingly obvious.
During 2.2.0, using return expr inside a with test block continues to work, but emits a DeprecationWarning suggesting the new macro.
Step 2 — 3.0.0 (breaking)
Let return inside a with test block have its usual Python meaning (return from the enclosing function). unpythonic aims to be as pythonic as possible; undoing the keyword hijack — not banning return — is the point.
This likely means the with test: body can no longer be implicitly lifted to a function (or the lift has to transparently propagate return out to the enclosing function). Implementation details TBD — tracked here because the user-visible end state is clear even if the mechanism needs design work.
Scope note
Local variables assigned inside with test: would still be test-local under the current implementation (scope boundary from the implicit lift), which is acceptable precedent — comprehensions and generator expressions do the same, as do unpythonic's continuations. If a variable needs to be visible in the parent scope, use a box (or nonlocal / global as appropriate).
As of 0.15.1, the contents of a
with testblock 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 is currently
return expr. This is effectively a keyword hijack — in the spirit of pytest'sassertrewriting — where a standard Python construct is repurposed to mean something else inside a specific block. Not very pythonic. It conflicts with the established meaning ofreturnand will confuse code analyzers and human readers alike.Plan
Step 1 — 2.2.0 (non-breaking)
Add an expr macro to declare the tested expression, so the code reads more like standard Python. Name needs some thought:
returns[]— closest to intent, but will confuse humans as to why there is a separatereturnsvs. the standardreturn.result[],results_in[],evaluates_into[]— semantically slightly off; we want to declare a check, not a result.check[]— checkmate in t(h)ree? Probably the best so far.During 2.2.0, using
return exprinside awith testblock continues to work, but emits aDeprecationWarningsuggesting the new macro.Step 2 — 3.0.0 (breaking)
Let
returninside awith testblock have its usual Python meaning (return from the enclosing function). unpythonic aims to be as pythonic as possible; undoing the keyword hijack — not banningreturn— is the point.This likely means the
with test:body can no longer be implicitly lifted to a function (or the lift has to transparently propagatereturnout to the enclosing function). Implementation details TBD — tracked here because the user-visible end state is clear even if the mechanism needs design work.Scope note
Local variables assigned inside
with test:would still be test-local under the current implementation (scope boundary from the implicit lift), which is acceptable precedent — comprehensions and generator expressions do the same, as do unpythonic's continuations. If a variable needs to be visible in the parent scope, use abox(ornonlocal/globalas appropriate).