scripts: Adopted Attr rework in plot.py/plotmpl.py

Unifying these complicated attr-assigning flags across all the scripts
is the main benefit of the new internal Attr system.

The only tricky bit is we need to somehow keep track of all input fields
in case % modifiers reference fields, when we could previously discard
non-data fields.

Tricky but doable.

Updated flags:

- -L/--label -> -L/--add-label
- --colors -> -C/--add-color
- --formats -> -F/--add-format
- --chars -> -*/--add-char/--chars
- --line-chars -> -_/--add-line-char/--line-chars

I've also tweaked Attr to accept glob matches when figuring out group
assignments. This is useful for matching slightly different, but
similarly named results in our benchmark scripts.

There's probably a clever way to do this by injecting new by fields with
csv.py, but just adding globbing is simpler and makes attr assignment
even more flexible.
This commit is contained in:
Christopher Haster
2025-02-20 05:08:55 -06:00
parent 8b04e35ea5
commit 86f3bad2a4
6 changed files with 802 additions and 397 deletions
+36 -8
View File
@@ -11,6 +11,7 @@ if __name__ == "__main__":
import bisect
import collections as co
import csv
import fnmatch
import itertools as it
import math as mt
import re
@@ -163,7 +164,7 @@ class Attr:
if (defaults is not None
and not any(
not isinstance(attr, tuple)
or attr[0] in {None, (), ('',)}
or attr[0] in {None, (), ('*',)}
for attr in (attrs or []))):
attrs = defaults + (attrs or [])
@@ -173,7 +174,7 @@ class Attr:
for attr in (attrs or []):
if not isinstance(attr, tuple):
attr = ((), attr)
elif attr[0] in {None, (), ('',)}:
elif attr[0] in {None, (), ('*',)}:
attr = ((), attr[1])
self.attrs.append(attr)
@@ -206,7 +207,7 @@ class Attr:
for ks, vs in self.keyed.items():
prefix = []
for j, k in enumerate(ks):
if j < len(key) and (not k or key[j] == k):
if j < len(key) and fnmatch.fnmatchcase(key[j], k):
prefix.append(k)
else:
prefix = None
@@ -225,6 +226,33 @@ class Attr:
def __contains__(self, key):
return self.__getitem__(key) is not None
# a key function for sorting by key order
def key(self, key):
# allow key to be a tuple to make sorting dicts easier
if (isinstance(key, tuple)
and len(key) >= 1
and isinstance(key[0], tuple)):
key = key[0]
best = None
for i, ks in enumerate(self.keyed.keys()):
prefix = []
for j, k in enumerate(ks):
if j < len(key) and (not k or key[j] == k):
prefix.append(k)
else:
prefix = None
break
if prefix is not None and (
best is None or len(prefix) >= len(best[0])):
best = (prefix, i)
if best is not None:
return best[1]
return len(self.keyed)
# parse %-escaped strings
def punescape(s, attrs=None):
if attrs is None:
@@ -673,9 +701,9 @@ def main(csv_paths, *,
by=None,
fields=None,
defines=[],
labels=None,
chars=None,
colors=None,
labels=[],
chars=[],
colors=[],
color=False,
dots=False,
braille=False,
@@ -698,7 +726,7 @@ def main(csv_paths, *,
# what chars/colors/labels to use?
chars_ = []
for char in (chars or []):
for char in chars:
if isinstance(char, tuple):
chars_.extend((char[0], c) for c in char[1])
else:
@@ -1004,7 +1032,7 @@ if __name__ == "__main__":
"where a group is the comma-separated 'by' fields. Accepts %% "
"modifiers.")
parser.add_argument(
'-.', '--add-char', '--chars',
'-*', '--add-char', '--chars',
dest='chars',
action='append',
type=lambda x: (