#!/usr/bin/env python3 import argparse import ast import astor import os import re import sys class Interpolator(ast.NodeTransformer): def visit_Call(self, node: ast.Call): node = self.generic_visit(node) is_format_expr = ( isinstance(node.func, ast.Attribute) and node.func.attr == "format" and isinstance(node.func.value, ast.Str) ) if not is_format_expr: return node format_string = node.func.value.s format_args = node.args + [None] substrings = format_string.split("{}") components = [] for (substring, arg) in zip(substrings, format_args): if substring: components.append(ast.Str(substring)) if arg: components.append( ast.FormattedValue(arg, conversion=-1, format_spec=None) ) return ast.JoinedStr(components) def _main(): parser = argparse.ArgumentParser() parser.add_argument("files", nargs="+") args = parser.parse_args() rewriter = Interpolator() for path in args.files: with open(path) as input: content = input.read() rewritten = rewriter.visit(ast.parse(content)) with open(path, "w") as output: output.write(astor.to_source(rewritten)) if __name__ == "__main__": _main()