Package: enpassant
Simpleen passantassignment, giving Python clearer conditional statementsUsagefrom __future__ import print_function # Python 2/3 compat
from enpassant import *
result = Passer()

while result / expensive_request():
    print(result.report())

    # assuming report() is a method of the object
    # expensive_request() would naturally hand backDiscussionMany languages supporten passant(in passing) assignment, like so:if result = expensive_request():
    print(result.report())In Python, that’s a syntax error.
This leads to more code lines and, in some cases, less
visual clarity:result = expensive_request()
if result:
    print(result.report())Or worse, in the case of looping structures:result = expensive_request()
while result:
    print(result.report())
    result = expensive_request()It doesn’t look so bad here, in a highly distilled example. But in real
programs, the called function often has parameters to be managed, and the
surrounding code is invariably longer and more complicated. The more complicated
the surrounding computations and requests, the simpler the comparison itself
should be. As theZen of Pythonintones: “Simple is better than complex.” and “Readability counts.”I hope that Python will eventually provide a concise way of handling this, such
as:while expensive_request() as result:
    print(result.report())But in the meanwhile,enpassantprovides a workaround.How it Worksfrom enpassant import *
result = Passer()

while result / expensive_request():
    print(result.report())Hereresult / expensive_request()is read “the result of the
expensive_request.”resultis merely a proxy object that, when it
encounters the division operator, returns the denominator. That is,result / whatever == whatever. But it alsoremembersthe denominator value.
Then, whenever you want the result value provided (presumably, later in the
body of your loop or conditional), simply access it throughresult. If
you want the full object returned byexpensive_request()you can get it
viaresult.value. Or or the result has items or attributes, they are
available by indexing or naming the attribute directly.Easy peasy!NB: If you change the items or attributes ofresult, those settings are
also forwarded to the underlying object.resultis not a copy, but a
true proxy, and as close to the actual object returned as I can make it
given current Python strictures.Some Detailsenpassant“assignment” is transparent to conditional expressions,
because the value of the expression is always the value of the denominator.
ButPassersare also guaranteed to have a Boolean value identical to
that of the value they contain, should you wish to use them in subsequent
tests.Theresultin the example above isn’t the pure result of the following
function call (or expression), but rather a proxy to it. Item ([])
and attribute (.) access work directly onresultbecausePasserobjects pass ongetitemandget-attributerequests to their
enclosed value. Usually, this is a convenience, and avoids having to
needlessly state that it’s reallyresult.valuethat’s being indexed or
dereferenced. But if you need the specific object returned (say for an
object identity orisinstancetest, useresult.valuedirectly.Alternative Value AccessIt is also possible to retrieve the value of aPasserby calling it:if result / expensive_request():
    print(result().report())This technique makes clear that the value is being rendered via some
process, rather than just presented as a normal Python name / variable. And
the resulting object fromresult()is the true and complete result of
the earlier function call, with no need for implicit / auto-magical
forwarding of items and attributes. Which style makes sense is a matter of
judgment and taste.Or, if you prefer something terser, the+(unary positive) operation
will also yield the value:if result / expensive_request():
    print(+result.report())Alternative InvocationsIf you prefer the less-than (<) or less-than-or-equal (<=) operators
as indicators thatresulttakes the value of the following value, they
are supported as aliases of the division operation (/). Thus, the
following are identical:if result / expensive_request():
    print(result.report())

if result < expensive_request():
    print(result.report())

if result <= expensive_request():
    print(result.report())It’s a matter of preference which seems most logical, appropriate, and
expressive. None of them are as good Note, however, that the operation usually
known as division (/) has a much higher precedence (i.e. tighter binding to
its operands) than the typical comparison operations (<and<=). If used
with a more complex expressions, either know your precedence or use parenthesis
to disambiguate!It’d be swell if Python supported arbitrary symbols. Unicode has what would
be reasonable alternative assignment markers, such as ← (LEFTARDS
ARROW), but
alas! Until Python gets more Unicode-savvy, we have to choose some existing
ASCII operator to repurpose.It is also possible to use a function call idiom if you prefer:if result(expensive_request()):
    print(result.report())This has the virtue of looking like a “wrapping” of the expensive
request value, rather than reusing / overloading an existing operation.Grabber and SimilarI’ve experimented with other forms of collecting and rendering values.
This version ofenpassantincludes the results of one of those experiments.
Objects of theGrabberclass can have their attributes set on their first
access. Subsequent uses of that attribute yield the set value.:info = Grabber()
info.name('Joe')
assert info.name == 'Joe'The challenge with this approach is that once set, attribute values cannot be
reset.NotesSeCHANGES.ymlfor the change log.En passantis a chess
term.En passant assignment / naming is discussed inIssue1714448andPEP 379, which have
been rejected and withdrawn, respectively. But that is years gone
by. I hope the idea will be productively reconsidered in the future.Automated multi-version testing managed withpytest,pytest-cov,coverage, andtox.
Packaging linting withpyroma.Successfully packaged for, and
tested against, all early 2017 versions of Python: 2.6, 2.7, 3.2, 3.3,
3.4, 3.5, and 3.6, as well as latest PyPy and PyPy3.On Python 2.6, uses Raymond Hettinger’sordereddictmodule (which is included in the source tree for ease of installation)
to provideOrderedDict. Thank you, Raymond!Thesimplerepackage similarly
provides en passant handling (and other helpers) for the important, common
case of regular expression searches.An alterantive module:dataholder.The author,Jonathan Euniceor@jeunice on Twitterwelcomes your comments and suggestions.InstallationTo install or upgrade to the latest version:pip install -U enpassantYou may need to prefix these withsudoto authorize
installation. In environments without super-user privileges, you may want to
usepip’s--useroption, to install only for a single user, rather
than system-wide. You may also need version-specific versions ofpip,
such aspip2andpip3, depending on your system configuration.