scripts: Adopted % modifiers in all attr arguments
This turns out to be extremely useful, for the sole purpose of being able to specify colors/formats/etc in csv fields (-C'%(fields)s' for example, or -C'#%(field)06x' for a cooler example). This is a bit tricky for --chars, but doable with a psplit helper function. Also fixed a bug in plot.py where we weren't using dataattrs_ correctly.
This commit is contained in:
+32
-12
@@ -511,6 +511,17 @@ def punescape(s, attrs=None):
|
|||||||
else: assert False
|
else: assert False
|
||||||
return re.sub(pattern, unescape, s)
|
return re.sub(pattern, unescape, s)
|
||||||
|
|
||||||
|
# split %-escaped strings into chars
|
||||||
|
def psplit(s):
|
||||||
|
pattern = re.compile(
|
||||||
|
'%[%n]'
|
||||||
|
'|' '%x..'
|
||||||
|
'|' '%u....'
|
||||||
|
'|' '%U........'
|
||||||
|
'|' '%\((?P<field>[^)]*)\)'
|
||||||
|
'(?P<format>[+\- #0-9\.]*[sdboxXfFeEgG])')
|
||||||
|
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
||||||
|
|
||||||
|
|
||||||
# a hack log that preserves sign, with a linear region between -1 and 1
|
# a hack log that preserves sign, with a linear region between -1 and 1
|
||||||
def symlog(x):
|
def symlog(x):
|
||||||
@@ -1010,9 +1021,9 @@ def main(csv_paths, *,
|
|||||||
chars_ = []
|
chars_ = []
|
||||||
for char in chars:
|
for char in chars:
|
||||||
if isinstance(char, tuple):
|
if isinstance(char, tuple):
|
||||||
chars_.extend((char[0], c) for c in char[1])
|
chars_.extend((char[0], c) for c in psplit(char[1]))
|
||||||
else:
|
else:
|
||||||
chars_.extend(char)
|
chars_.extend(psplit(char))
|
||||||
chars_ = Attr(chars_, defaults=(
|
chars_ = Attr(chars_, defaults=(
|
||||||
CHARS_POINTS_AND_LINES if points_and_lines
|
CHARS_POINTS_AND_LINES if points_and_lines
|
||||||
else [True]))
|
else [True]))
|
||||||
@@ -1020,9 +1031,9 @@ def main(csv_paths, *,
|
|||||||
line_chars_ = []
|
line_chars_ = []
|
||||||
for line_char in line_chars:
|
for line_char in line_chars:
|
||||||
if isinstance(line_char, tuple):
|
if isinstance(line_char, tuple):
|
||||||
line_chars_.extend((line_char[0], c) for c in line_char[1])
|
line_chars_.extend((line_char[0], c) for c in psplit(line_char[1]))
|
||||||
else:
|
else:
|
||||||
line_chars_.extend(line_char)
|
line_chars_.extend(psplit(line_char))
|
||||||
line_chars_ = Attr(line_chars_, defaults=(
|
line_chars_ = Attr(line_chars_, defaults=(
|
||||||
[True] if points_and_lines or not points
|
[True] if points_and_lines or not points
|
||||||
else [False]))
|
else [False]))
|
||||||
@@ -1182,13 +1193,21 @@ def main(csv_paths, *,
|
|||||||
|
|
||||||
# figure out colors/chars here so that subplot defines
|
# figure out colors/chars here so that subplot defines
|
||||||
# don't change them later, that'd be bad
|
# don't change them later, that'd be bad
|
||||||
datachars_ = {name: chars_[i, name]
|
datachars_ = {name: (lambda c:
|
||||||
|
c if isinstance(c, bool)
|
||||||
|
# limit to 1 char
|
||||||
|
else punescape(c, dataattrs_[name])[0])(
|
||||||
|
chars_[i, name])
|
||||||
for i, name in enumerate(datasets_.keys())}
|
for i, name in enumerate(datasets_.keys())}
|
||||||
dataline_chars_ = {name: line_chars_[i, name]
|
dataline_chars_ = {name: (lambda c:
|
||||||
|
c if isinstance(c, bool)
|
||||||
|
# limit to 1 char
|
||||||
|
else punescape(c, dataattrs_[name])[0])(
|
||||||
|
line_chars_[i, name])
|
||||||
for i, name in enumerate(datasets_.keys())}
|
for i, name in enumerate(datasets_.keys())}
|
||||||
datacolors_ = {name: colors_[i, name]
|
datacolors_ = {name: punescape(colors_[i, name], dataattrs_[name])
|
||||||
for i, name in enumerate(datasets_.keys())}
|
for i, name in enumerate(datasets_.keys())}
|
||||||
datalabels_ = {name: punescape(labels_[i, name], mergedattrs_)
|
datalabels_ = {name: punescape(labels_[i, name], dataattrs_[name])
|
||||||
for i, name in enumerate(datasets_.keys())
|
for i, name in enumerate(datasets_.keys())
|
||||||
if (i, name) in labels_}
|
if (i, name) in labels_}
|
||||||
|
|
||||||
@@ -1215,7 +1234,7 @@ def main(csv_paths, *,
|
|||||||
else ','.join(name))
|
else ','.join(name))
|
||||||
|
|
||||||
if label:
|
if label:
|
||||||
legend_.append((label, colors_[i, name]))
|
legend_.append((label, datacolors_[name]))
|
||||||
legend_width = max(legend_width, len(label)+1)
|
legend_width = max(legend_width, len(label)+1)
|
||||||
|
|
||||||
# figure out our canvas size
|
# figure out our canvas size
|
||||||
@@ -1695,7 +1714,7 @@ if __name__ == "__main__":
|
|||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add characters to use for points. Can be assigned to a "
|
help="Add characters to use for points. Can be assigned to a "
|
||||||
"specific group where a group is the comma-separated "
|
"specific group where a group is the comma-separated "
|
||||||
"'by' fields.")
|
"'by' fields. Accepts %% modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-_', '--add-line-char', '--line-chars',
|
'-_', '--add-line-char', '--line-chars',
|
||||||
dest='line_chars',
|
dest='line_chars',
|
||||||
@@ -1708,7 +1727,7 @@ if __name__ == "__main__":
|
|||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add characters to use for lines. Can be assigned to a "
|
help="Add characters to use for lines. Can be assigned to a "
|
||||||
"specific group where a group is the comma-separated "
|
"specific group where a group is the comma-separated "
|
||||||
"'by' fields.")
|
"'by' fields. Accepts %% modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-C', '--add-color',
|
'-C', '--add-color',
|
||||||
dest='colors',
|
dest='colors',
|
||||||
@@ -1720,7 +1739,8 @@ if __name__ == "__main__":
|
|||||||
)(*x.split('=', 1))
|
)(*x.split('=', 1))
|
||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add a color to use. Can be assigned to a specific group "
|
help="Add a color to use. Can be assigned to a specific group "
|
||||||
"where a group is the comma-separated 'by' fields.")
|
"where a group is the comma-separated 'by' fields. Accepts %% "
|
||||||
|
"modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--color',
|
'--color',
|
||||||
choices=['never', 'always', 'auto'],
|
choices=['never', 'always', 'auto'],
|
||||||
|
|||||||
+5
-4
@@ -890,9 +890,9 @@ def main(csv_paths, output, *,
|
|||||||
|
|
||||||
# figure out formats/colors here so that subplot defines don't change
|
# figure out formats/colors here so that subplot defines don't change
|
||||||
# them later, that'd be bad
|
# them later, that'd be bad
|
||||||
dataformats_ = {name: formats_[i, name]
|
dataformats_ = {name: punescape(formats_[i, name], dataattrs_[name])
|
||||||
for i, name in enumerate(datasets_.keys())}
|
for i, name in enumerate(datasets_.keys())}
|
||||||
datacolors_ = {name: colors_[i, name]
|
datacolors_ = {name: punescape(colors_[i, name], dataattrs_[name])
|
||||||
for i, name in enumerate(datasets_.keys())}
|
for i, name in enumerate(datasets_.keys())}
|
||||||
datalabels_ = {name: punescape(labels_[i, name], dataattrs_[name])
|
datalabels_ = {name: punescape(labels_[i, name], dataattrs_[name])
|
||||||
for i, name in enumerate(datasets_.keys())
|
for i, name in enumerate(datasets_.keys())
|
||||||
@@ -1325,7 +1325,8 @@ if __name__ == "__main__":
|
|||||||
)(*x.split('=', 1))
|
)(*x.split('=', 1))
|
||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add a color to use. Can be assigned to a specific group "
|
help="Add a color to use. Can be assigned to a specific group "
|
||||||
"where a group is the comma-separated 'by' fields.")
|
"where a group is the comma-separated 'by' fields. Accepts %% "
|
||||||
|
"modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-F', '--add-format',
|
'-F', '--add-format',
|
||||||
dest='formats',
|
dest='formats',
|
||||||
@@ -1338,7 +1339,7 @@ if __name__ == "__main__":
|
|||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add a matplotlib format to use. Can be assigned to a "
|
help="Add a matplotlib format to use. Can be assigned to a "
|
||||||
"specific group where a group is the comma-separated 'by' "
|
"specific group where a group is the comma-separated 'by' "
|
||||||
"fields.")
|
"fields. Accepts %% modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-.', '--points',
|
'-.', '--points',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
|||||||
+20
-7
@@ -21,7 +21,7 @@ import shutil
|
|||||||
# we don't actually need that many chars/colors thanks to the
|
# we don't actually need that many chars/colors thanks to the
|
||||||
# 4-colorability of all 2d maps
|
# 4-colorability of all 2d maps
|
||||||
CHARS = ['.']
|
CHARS = ['.']
|
||||||
COLORS = [34, 31, 32, 35, 33, 36]
|
COLORS = ['34', '31', '32', '35', '33', '36']
|
||||||
|
|
||||||
CHARS_DOTS = " .':"
|
CHARS_DOTS = " .':"
|
||||||
CHARS_BRAILLE = (
|
CHARS_BRAILLE = (
|
||||||
@@ -295,6 +295,17 @@ def punescape(s, attrs=None):
|
|||||||
else: assert False
|
else: assert False
|
||||||
return re.sub(pattern, unescape, s)
|
return re.sub(pattern, unescape, s)
|
||||||
|
|
||||||
|
# split %-escaped strings into chars
|
||||||
|
def psplit(s):
|
||||||
|
pattern = re.compile(
|
||||||
|
'%[%n]'
|
||||||
|
'|' '%x..'
|
||||||
|
'|' '%u....'
|
||||||
|
'|' '%U........'
|
||||||
|
'|' '%\((?P<field>[^)]*)\)'
|
||||||
|
'(?P<format>[+\- #0-9\.]*[sdboxXfFeEgG])')
|
||||||
|
return [m.group() for m in re.finditer(pattern.pattern + '|.', s)]
|
||||||
|
|
||||||
|
|
||||||
# a little ascii renderer
|
# a little ascii renderer
|
||||||
class Canvas:
|
class Canvas:
|
||||||
@@ -740,9 +751,9 @@ def main(csv_paths, *,
|
|||||||
chars_ = []
|
chars_ = []
|
||||||
for char in chars:
|
for char in chars:
|
||||||
if isinstance(char, tuple):
|
if isinstance(char, tuple):
|
||||||
chars_.extend((char[0], c) for c in char[1])
|
chars_.extend((char[0], c) for c in psplit(char[1]))
|
||||||
else:
|
else:
|
||||||
chars_.extend(char)
|
chars_.extend(psplit(char))
|
||||||
chars_ = Attr(chars_, defaults=CHARS)
|
chars_ = Attr(chars_, defaults=CHARS)
|
||||||
|
|
||||||
colors_ = Attr(colors, defaults=COLORS)
|
colors_ = Attr(colors, defaults=COLORS)
|
||||||
@@ -821,11 +832,11 @@ def main(csv_paths, *,
|
|||||||
# use colors for top of tree
|
# use colors for top of tree
|
||||||
for i, t in enumerate(tile.children):
|
for i, t in enumerate(tile.children):
|
||||||
for t_ in t.tiles():
|
for t_ in t.tiles():
|
||||||
t_.color = colors_[i, t.key]
|
t_.color = punescape(colors_[i, t.key], t_.attrs)
|
||||||
|
|
||||||
# and chars/labels for bottom of tree
|
# and chars/labels for bottom of tree
|
||||||
for i, t in enumerate(tile.leaves()):
|
for i, t in enumerate(tile.leaves()):
|
||||||
t.char = chars_[i, t.key]
|
t.char = punescape(chars_[i, t.key], t.attrs)[0] # limit to 1 char
|
||||||
if (i, t.key) in labels_:
|
if (i, t.key) in labels_:
|
||||||
t.label = punescape(labels_[i, t.key], t.attrs)
|
t.label = punescape(labels_[i, t.key], t.attrs)
|
||||||
|
|
||||||
@@ -1061,7 +1072,8 @@ if __name__ == "__main__":
|
|||||||
)(*x.split('=', 1))
|
)(*x.split('=', 1))
|
||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add characters to use. Can be assigned to a specific group "
|
help="Add characters to use. Can be assigned to a specific group "
|
||||||
"where a group is the comma-separated 'by' fields.")
|
"where a group is the comma-separated 'by' fields. Accepts %% "
|
||||||
|
"modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-C', '--add-color',
|
'-C', '--add-color',
|
||||||
dest='colors',
|
dest='colors',
|
||||||
@@ -1073,7 +1085,8 @@ if __name__ == "__main__":
|
|||||||
)(*x.split('=', 1))
|
)(*x.split('=', 1))
|
||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add a color to use. Can be assigned to a specific group "
|
help="Add a color to use. Can be assigned to a specific group "
|
||||||
"where a group is the comma-separated 'by' fields.")
|
"where a group is the comma-separated 'by' fields. Accepts %% "
|
||||||
|
"modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--color',
|
'--color',
|
||||||
choices=['never', 'always', 'auto'],
|
choices=['never', 'always', 'auto'],
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ def main(csv_paths, output, *,
|
|||||||
# use colors for top of tree
|
# use colors for top of tree
|
||||||
for i, t in enumerate(tile.children):
|
for i, t in enumerate(tile.children):
|
||||||
for t_ in t.tiles():
|
for t_ in t.tiles():
|
||||||
t_.color = colors_[i, t_.key]
|
t_.color = punescape(colors_[i, t_.key], t_.attrs)
|
||||||
|
|
||||||
# and labels everywhere
|
# and labels everywhere
|
||||||
for i, t in enumerate(tile.tiles()):
|
for i, t in enumerate(tile.tiles()):
|
||||||
@@ -969,7 +969,8 @@ if __name__ == "__main__":
|
|||||||
)(*x.split('=', 1))
|
)(*x.split('=', 1))
|
||||||
if '=' in x else x.strip(),
|
if '=' in x else x.strip(),
|
||||||
help="Add a color to use. Can be assigned to a specific group "
|
help="Add a color to use. Can be assigned to a specific group "
|
||||||
"where a group is the comma-separated 'by' fields.")
|
"where a group is the comma-separated 'by' fields. Accepts %% "
|
||||||
|
"modifiers.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-W', '--width',
|
'-W', '--width',
|
||||||
type=lambda x: int(x, 0),
|
type=lambda x: int(x, 0),
|
||||||
|
|||||||
Reference in New Issue
Block a user