Skip to content

Instantly share code, notes, and snippets.

@vsajip
Last active October 11, 2018 08:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vsajip/d1b135010a07b7cd35673544feb61cd8 to your computer and use it in GitHub Desktop.
Save vsajip/d1b135010a07b7cd35673544feb61cd8 to your computer and use it in GitHub Desktop.
Logging format string validation
import os
import re
import string
import sys
_sfmt = string.Formatter()
FIELD_SPEC = re.compile(r'^(\d+|\w+)(\.\w+|\[[^]]+\])*$')
FMT_SPEC = re.compile(r'^(.?[<>=^])?[+ -]?#?0?(\d+|{\w+})?[,_]?(\.(\d+|{\w+}))?[bcdefgnosx%]?$')
def validate_braces(fs):
fields = set()
try:
for _, fieldname, spec, conversion in _sfmt.parse(fs):
if fieldname:
if not FIELD_SPEC.match(fieldname):
raise ValueError('invalid field name/expression: %r' % fieldname)
fields.add(fieldname)
if conversion:
if conversion not in 'rsa':
raise ValueError('invalid conversion: %r' % conversion)
if spec:
if not FMT_SPEC.match(spec):
raise ValueError('bad specifier: %r' % spec)
except ValueError as e:
raise ValueError('invalid format: %s' % e)
if not fields:
raise ValueError('invalid format: no fields')
def validate_dollars(fs):
p = string.Template.pattern
fields = set()
for m in p.finditer(fs):
d = m.groupdict()
if d['named']:
fields.add(d['named'])
elif d['braced']:
fields.add(d['braced'])
elif m.group(0) == '$':
raise ValueError('invalid format: bare \'$\' not allowed')
if not fields:
raise ValueError('invalid format: no fields')
def main():
# G -> good and valid
# E -> format causes errors in underlying engine
# F -> cases which the underlying engine wll be happy with, but we're not
brace_cases = (
'G:{foo}',
'G:{foo.bar}',
'G:{foo[0].bar[1].baz}',
'G:{foo[k1].bar[k2].baz}',
'G:{12[k1].bar[k2].baz}',
'G:{foo!r}',
'G:{foo!s}',
'G:{foo!a}',
'G:{foo!r:4.2}',
'E:{foo!r:4.2',
'E:{{foo!r:4.2}',
'F:{{foo!r:4.2}}',
'G:{foo:{w}.{p}}',
'G:{foo:12.{p}}',
'G:{foo:{w}.6}',
'F:{foo:{{w}}.{{p}}}',
'F:{foo/bar}',
'E:{foo:{{w}}.{{p}}}}',
'F:{foo!X:{{w}}.{{p}}}',
'F:{foo!a:random}',
'E:{foo!a:ran{dom}',
'F:{foo!a:ran{d}om}',
'F:{foo.!a:d}',
)
for case in brace_cases:
try:
validate_braces(case)
print('%-30s -> ok' % case)
except ValueError as e:
print('%-30s -> %s' % (case, e))
dollar_cases = (
'G:$foo',
'G:${bar}',
'G:$bar $$',
'E:$bar $$$',
'G:$bar $$$$',
'E:foo $',
'F:foo',
'E:foo $.',
)
print('-' * 80)
for case in dollar_cases:
try:
validate_dollars(case)
print('%-30s -> ok' % case)
except ValueError as e:
print('%-30s -> %s' % (case, e))
if __name__ == '__main__':
try:
rc = main()
except Exception as e:
sys.stderr.write('Failed: %s\n' % e)
import traceback; traceback.print_exc()
rc = 1
sys.exit(rc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment