Jeff Epler's blog2022-12-21T15:12:09ZPhotos, electronics, cnc, and moreJeff Eplerjepler@unpythonic.netA quick example of transforming Python with libcst2022-12-21T15:12:09Z2022-12-21T15:12:09Zhttps://emergent.unpythonic.net/01671635529
<p>I had occasion to encounter a Python library that used <tt>assert</tt>
with a side effect:
<pre>
assert initialize_hardware(), "hardware failed to initialize"
</pre>
looking a bit more widely, this idiom was apparently used hundreds of times
across a family of Python libraries.
<p>"Aha", I said, "I bet I can fix this with an automated tool". In this round
of investigation, I found <a href="https://libcst.readthedocs.io">LibCST</a>
and set about creating a program that would do what was needed, namely, to
turn at assert into <tt>if not initialize_hardware(): raise
RuntimeError("hardware failed to initialize")</tt>.
<p>While LibCST has an explicit facility for "codemodding", I didn't notice it
at first and wrote in terms of transformers, with my own command-line driver
program.
<p>Unfortunately, while my transformer succeeded, attempting to format the CST
back into code would result in an error without very many matches on my
favorite search engine: <a href="https://github.com/Instagram/LibCST/issues/753"><strong>Unexpected
keyword argument 'default_semicolon'</strong></a>. That linked issue didn't
provide an answer, but my further investigation did.
<p>In the Python grammer as represented by LibCST, an assert statement is
part of a SimpleStatementLine, while an if statement is not wrapped in a
SimpleStatementLine. So if the transformation of an Assert node into an If
node is done alone, the new If node lies inside a SimpleStatementLine node,
and that is not valid. The problem is not detected until rendering the CST
back into code. (It may be possible that using type checking would have found a
problem, as this is essentially a type error)
<p>The solution that I arrived at was to also transform any SimpleStatementLine
which ended up containing an If node, by using the FlattenSentinel to do it.
I think it might have been even more correct to directly perform the
transformation within SimpleStatementLine, but what I ended up works now.
<p><script src="https://gist.github.com/jepler/c12e35cbfa9ad5a06cee9de647ba8225.js"></script>