Not hard to slap one together with pyparsing, after looking at the docs:
import pyparsing as pp
# from https://docs.python.org/3/library/stdtypes.html?highlight=string%20interpolation#printf-style-string-formatting
PCT,LPAREN,RPAREN,DOT = map(pp.Literal, '%().')
conversion_flag_expr = pp.oneOf(list("#0- +"))
conversion_type_expr = pp.oneOf(list("diouxXeEfFgGcrsa%"))
length_mod_expr = pp.oneOf(list("hlL"))
interp_expr = (PCT
+ pp.Optional(LPAREN + pp.Word(pp.printables, excludeChars=")")("mapping_key") + RPAREN)
+ pp.Optional(conversion_flag_expr("conversion_flag"))
+ pp.Optional(('*' | pp.pyparsing_common.integer)("min_width"))
+ pp.Optional(DOT + pp.pyparsing_common.integer("max_width"))
+ pp.Optional(length_mod_expr("length_modifier"))
+ conversion_type_expr("conversion_type")
)
tests = """
Now is the winter of our %s made %(adjective)s %(season)s by this %(relative)s of %(city)s
%(name)s, %(name)s, %(name)s, a %4.8d deaths were not enough for %(name)s
""".splitlines()
for t in tests:
print(t)
for t,s,e in interp_expr.scanString(t):
print("start: {} end: {}\n{}".format(s, e, t.dump()))
print()
Prints:
Now is the winter of our %s made %(adjective)s %(season)s by this %(relative)s of %(city)s
start: 29 end: 31
['%', 's']
- conversion_type: 's'
start: 37 end: 50
['%', '(', 'adjective', ')', 's']
- conversion_type: 's'
- mapping_key: 'adjective'
start: 51 end: 61
['%', '(', 'season', ')', 's']
- conversion_type: 's'
- mapping_key: 'season'
start: 70 end: 82
['%', '(', 'relative', ')', 's']
- conversion_type: 's'
- mapping_key: 'relative'
start: 86 end: 94
['%', '(', 'city', ')', 's']
- conversion_type: 's'
- mapping_key: 'city'
%(name)s, %(name)s, %(name)s, a %4.8d deaths were not enough for %(name)s
start: 4 end: 12
['%', '(', 'name', ')', 's']
- conversion_type: 's'
- mapping_key: 'name'
start: 14 end: 22
['%', '(', 'name', ')', 's']
- conversion_type: 's'
- mapping_key: 'name'
start: 24 end: 32
['%', '(', 'name', ')', 's']
- conversion_type: 's'
- mapping_key: 'name'
start: 36 end: 41
['%', 4, '.', 8, 'd']
- conversion_type: 'd'
- max_width: 8
- min_width: 4
start: 68 end: 76
['%', '(', 'name', ')', 's']
- conversion_type: 's'
- mapping_key: 'name'
If you prefer regex, it should not be hard to convert this prototype to something workable.