<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/"/>

<title>Jeff Epler's blog</title>
<modified>2022-12-21T15:12:09Z</modified>
<tagline>Photos, electronics, cnc, and more</tagline>
<author><name>Jeff Epler</name><email>jepler@unpythonic.net</email></author>
<entry>
<title>A quick example of transforming Python with libcst</title>
<issued>2022-12-21T15:12:09Z</issued>
<modified>2022-12-21T15:12:09Z</modified>
<id>https://gamma.unpythonic.net/01671635529</id>
<link rel="alternate" type="text/html" href="https://gamma.unpythonic.net/01671635529"/>
<content type="text/html" mode="escaped">


&lt;p&gt;I had occasion to encounter a Python library that used &lt;tt&gt;assert&lt;/tt&gt;
with a side effect:
&lt;pre&gt;
assert initialize_hardware(), &quot;hardware failed to initialize&quot;
&lt;/pre&gt;
looking a bit more widely, this idiom was apparently used hundreds of times
across a family of Python libraries.

&lt;p&gt;&amp;quot;Aha&amp;quot;, I said, &amp;quot;I bet I can fix this with an automated tool&amp;quot;. In this round
of investigation, I found &lt;a href=&quot;https://libcst.readthedocs.io&quot;&gt;LibCST&lt;/a&gt;
and set about creating a program that would do what was needed, namely, to
turn at assert into &lt;tt&gt;if not initialize_hardware(): raise
RuntimeError(&quot;hardware failed to initialize&quot;)&lt;/tt&gt;.

&lt;p&gt;While LibCST has an explicit facility for &amp;quot;codemodding&amp;quot;, I didn't notice it
at first and wrote in terms of transformers, with my own command-line driver
program.

&lt;p&gt;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: &lt;a href=&quot;https://github.com/Instagram/LibCST/issues/753&quot;&gt;&lt;strong&gt;Unexpected
keyword argument 'default_semicolon'&lt;/strong&gt;&lt;/a&gt;. That linked issue didn't
provide an answer, but my further investigation did.

&lt;p&gt;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)

&lt;p&gt;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.

&lt;p&gt;&lt;script src=&quot;https://gist.github.com/jepler/c12e35cbfa9ad5a06cee9de647ba8225.js&quot;&gt;&lt;/script&gt;
</content>
</entry>
</feed>
