About me I've been a computer programmer since I first started typing in program listings on a Commodore Vic 20 when I was about 8. My hobbies include electronics, CNC manufacturing, photography, beer and winemaking.
I live with my wife and lots of left-over parts from unfinished projects in Lincoln, Nebraska, USA.]
Blog
21 December 2022, 15:12 UTCA quick example of transforming Python with libcst
I had occasion to encounter a Python library that used assert
with a side effect:
assert initialize_hardware(), "hardware failed to initialize"
looking a bit more widely, this idiom was apparently used hundreds of times
across a family of Python libraries.
"Aha", I said, "I bet I can fix this with an automated tool". In this round
of investigation, I found LibCST
and set about creating a program that would do what was needed, namely, to
turn at assert into if not initialize_hardware(): raise
RuntimeError("hardware failed to initialize").
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.
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: Unexpected
keyword argument 'default_semicolon'. That linked issue didn't
provide an answer, but my further investigation did.
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)
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.
A quick example of transforming Python with libcst
I had occasion to encounter a Python library that used assert with a side effect:
assert initialize_hardware(), "hardware failed to initialize"looking a bit more widely, this idiom was apparently used hundreds of times across a family of Python libraries.
"Aha", I said, "I bet I can fix this with an automated tool". In this round of investigation, I found LibCST and set about creating a program that would do what was needed, namely, to turn at assert into if not initialize_hardware(): raise RuntimeError("hardware failed to initialize").
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.
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: Unexpected keyword argument 'default_semicolon'. That linked issue didn't provide an answer, but my further investigation did.
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)
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.
All older entries
Website Copyright © 2004-2024 Jeff Epler