scripts: Implemented recursive punescape nesting
The previous implementation of punescape nesting only supported one
layer, because I didn't want to rewrite the pure re.sub approach.
But regex is not a pushdown automaton!
I.e. it's impossible to match both of these correctly:
%{a%}s%{b%}s
%{a%{a%}s%}s
This rewrites punescape and psplit to properly parse the punescape
string, recursing when we encounter "%{" and terminating on "%}s".
As a plus this shows that the punescape grammar is sound. This was a bit
up in the air with the hacky regex globbing.
This commit is contained in:
+85
-30
@@ -472,7 +472,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -481,7 +481,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -505,37 +506,62 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
# split %-escaped strings into chars
|
# split %-escaped strings into chars
|
||||||
def psplit(s):
|
def psplit(s, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -544,10 +570,39 @@ def psplit(s):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
s_ = []
|
||||||
|
i = start or 0
|
||||||
|
while i < (end if end is not None else len(s)):
|
||||||
|
m = pattern.match(s, i)
|
||||||
|
if m:
|
||||||
|
m_ = m.group('pun')[0]
|
||||||
|
i_ = m.end()
|
||||||
|
if m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = psplit(s, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = m__[0].end()
|
||||||
|
else:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = end if end is not None else len(s)
|
||||||
|
continue
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
else:
|
||||||
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return s_
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
|
|||||||
+53
-27
@@ -334,7 +334,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -343,7 +343,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -367,34 +368,59 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+53
-27
@@ -1710,7 +1710,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -1719,7 +1719,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -1743,34 +1744,59 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
class PunescapeGetattr:
|
class PunescapeGetattr:
|
||||||
def __init__(self, r):
|
def __init__(self, r):
|
||||||
|
|||||||
+85
-30
@@ -4205,7 +4205,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -4214,7 +4214,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -4238,37 +4239,62 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
# split %-escaped strings into chars
|
# split %-escaped strings into chars
|
||||||
def psplit(s):
|
def psplit(s, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -4277,10 +4303,39 @@ def psplit(s):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
s_ = []
|
||||||
|
i = start or 0
|
||||||
|
while i < (end if end is not None else len(s)):
|
||||||
|
m = pattern.match(s, i)
|
||||||
|
if m:
|
||||||
|
m_ = m.group('pun')[0]
|
||||||
|
i_ = m.end()
|
||||||
|
if m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = psplit(s, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = m__[0].end()
|
||||||
|
else:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = end if end is not None else len(s)
|
||||||
|
continue
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
else:
|
||||||
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return s_
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
|
|||||||
+53
-27
@@ -4094,7 +4094,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -4103,7 +4103,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -4127,34 +4128,59 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
|
|
||||||
# naive space filling curve (the default)
|
# naive space filling curve (the default)
|
||||||
|
|||||||
+85
-30
@@ -480,7 +480,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -489,7 +489,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -513,37 +514,62 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
# split %-escaped strings into chars
|
# split %-escaped strings into chars
|
||||||
def psplit(s):
|
def psplit(s, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -552,10 +578,39 @@ def psplit(s):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
s_ = []
|
||||||
|
i = start or 0
|
||||||
|
while i < (end if end is not None else len(s)):
|
||||||
|
m = pattern.match(s, i)
|
||||||
|
if m:
|
||||||
|
m_ = m.group('pun')[0]
|
||||||
|
i_ = m.end()
|
||||||
|
if m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = psplit(s, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = m__[0].end()
|
||||||
|
else:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = end if end is not None else len(s)
|
||||||
|
continue
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
else:
|
||||||
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return s_
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
|
|||||||
+85
-30
@@ -638,7 +638,7 @@ class CsvAttr:
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -647,7 +647,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -671,37 +672,62 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
# split %-escaped strings into chars
|
# split %-escaped strings into chars
|
||||||
def psplit(s):
|
def psplit(s, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -710,10 +736,39 @@ def psplit(s):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
s_ = []
|
||||||
|
i = start or 0
|
||||||
|
while i < (end if end is not None else len(s)):
|
||||||
|
m = pattern.match(s, i)
|
||||||
|
if m:
|
||||||
|
m_ = m.group('pun')[0]
|
||||||
|
i_ = m.end()
|
||||||
|
if m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = psplit(s, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = m__[0].end()
|
||||||
|
else:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = end if end is not None else len(s)
|
||||||
|
continue
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
else:
|
||||||
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return s_
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
|
|||||||
+53
-27
@@ -522,7 +522,7 @@ class CsvAttr:
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -531,7 +531,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -555,34 +556,59 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
|
|
||||||
# some classes for organizing subplots into a grid
|
# some classes for organizing subplots into a grid
|
||||||
|
|||||||
+85
-30
@@ -559,7 +559,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -568,7 +568,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -592,37 +593,62 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
# split %-escaped strings into chars
|
# split %-escaped strings into chars
|
||||||
def psplit(s):
|
def psplit(s, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -631,10 +657,39 @@ def psplit(s):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
s_ = []
|
||||||
|
i = start or 0
|
||||||
|
while i < (end if end is not None else len(s)):
|
||||||
|
m = pattern.match(s, i)
|
||||||
|
if m:
|
||||||
|
m_ = m.group('pun')[0]
|
||||||
|
i_ = m.end()
|
||||||
|
if m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = psplit(s, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = m__[0].end()
|
||||||
|
else:
|
||||||
|
s_.append(m.group() + ''.join(v))
|
||||||
|
i = end if end is not None else len(s)
|
||||||
|
continue
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
|
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
else:
|
||||||
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return s_
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
|
|||||||
+53
-27
@@ -420,7 +420,7 @@ def si2(x):
|
|||||||
# parse %-escaped strings
|
# parse %-escaped strings
|
||||||
#
|
#
|
||||||
# attrs can override __getitem__ for lazy attr generation
|
# attrs can override __getitem__ for lazy attr generation
|
||||||
def punescape(s, attrs=None):
|
def punescape(s, attrs=None, start=None, end=None, submatch=None):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
|
||||||
'[%ns]'
|
'[%ns]'
|
||||||
@@ -429,7 +429,8 @@ def punescape(s, attrs=None):
|
|||||||
'|' 'U........'
|
'|' 'U........'
|
||||||
'|' '\((?P<field>[^)]*)\)'
|
'|' '\((?P<field>[^)]*)\)'
|
||||||
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
|
||||||
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
|
'|' '\{'
|
||||||
|
'|' '\}'
|
||||||
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
|
||||||
|
|
||||||
def format(f, v):
|
def format(f, v):
|
||||||
@@ -453,34 +454,59 @@ def punescape(s, attrs=None):
|
|||||||
# note we need Python's new format syntax for binary
|
# note we need Python's new format syntax for binary
|
||||||
return ('{:%s}' % f).format(v)
|
return ('{:%s}' % f).format(v)
|
||||||
|
|
||||||
def unescape(m):
|
s_ = []
|
||||||
if m.group('pun')[0] == '%': s = '%'
|
i = start or 0
|
||||||
elif m.group('pun')[0] == 'n': s = '\n'
|
while i < (end if end is not None else len(s)):
|
||||||
elif m.group('pun')[0] == 's': s = ' '
|
m = pattern.match(s, i)
|
||||||
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
|
if m:
|
||||||
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
|
m_ = m.group('pun')[0]
|
||||||
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
|
i_ = m.end()
|
||||||
elif m.group('pun')[0] == '(':
|
if m_ == '%': v_ = '%'
|
||||||
if attrs is not None:
|
elif m_ == 'n': v_ = '\n'
|
||||||
try:
|
elif m_ == 's': v_ = ' '
|
||||||
v = attrs[m.group('field')]
|
elif m_ == 'x': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
except KeyError:
|
elif m_ == 'u': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
return m.group()
|
elif m_ == 'U': v_ = chr(int(m.group('pun')[1:], 16))
|
||||||
|
elif m_ == '(':
|
||||||
|
if attrs is not None:
|
||||||
|
try:
|
||||||
|
v = attrs[m.group('field')]
|
||||||
|
except KeyError:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
s_.append(m.group())
|
||||||
|
i = i_
|
||||||
|
continue
|
||||||
|
v_ = format(m.group('format'), v)
|
||||||
|
elif m_ == '{':
|
||||||
|
m__ = []
|
||||||
|
v = punescape(s, attrs, m.end(), None, m__)
|
||||||
|
if m__:
|
||||||
|
v_ = format(m__[0].group('subformat'), v)
|
||||||
|
i_ = m__[0].end()
|
||||||
|
else:
|
||||||
|
v_ = str(v)
|
||||||
|
i_ = end if end is not None else len(s)
|
||||||
|
elif m_ == '}':
|
||||||
|
if submatch is not None:
|
||||||
|
submatch.append(m)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(m.group())
|
||||||
s = format(m.group('format'), v)
|
i = i_
|
||||||
elif m.group('pun')[0] == '{':
|
continue
|
||||||
v = punescape(m.group('substring'), attrs)
|
|
||||||
s = format(m.group('subformat'), v)
|
if m.group('rep'):
|
||||||
|
v_ = int(m.group('rep'), 10) * v_
|
||||||
|
s_.append(v_)
|
||||||
|
i = i_
|
||||||
else:
|
else:
|
||||||
return m.group()
|
s_.append(s[i])
|
||||||
|
i += 1
|
||||||
|
|
||||||
if m.group('rep'):
|
return ''.join(s_)
|
||||||
s = int(m.group('rep'), 10) * s
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
return re.sub(pattern, unescape, s)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user