scripts: Adopted % for escape codes
This is what git --format does, and it's a clever way sidestep the escape-hell that is bash sometimes.
This commit is contained in:
+50
-27
@@ -14,13 +14,13 @@ if __name__ == "__main__":
|
|||||||
__import__('sys').path.pop(0)
|
__import__('sys').path.pop(0)
|
||||||
|
|
||||||
import bisect
|
import bisect
|
||||||
import codecs
|
|
||||||
import collections as co
|
import collections as co
|
||||||
import csv
|
import csv
|
||||||
import io
|
import io
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import math as mt
|
import math as mt
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
@@ -131,9 +131,30 @@ def si2(x, w=5):
|
|||||||
s = s.rstrip('.')
|
s = s.rstrip('.')
|
||||||
return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p])
|
return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p])
|
||||||
|
|
||||||
# parse escape strings
|
# parse %-escaped strings
|
||||||
def escape(s):
|
def unescape(s):
|
||||||
return codecs.escape_decode(s.encode('utf8'))[0].decode('utf8')
|
pattern = re.compile(
|
||||||
|
'%[%=,abfnrtv0]'
|
||||||
|
'|' '%x..'
|
||||||
|
'|' '%u....'
|
||||||
|
'|' '%U........')
|
||||||
|
def unescape(m):
|
||||||
|
if m.group()[1] == '%': return '%'
|
||||||
|
elif m.group()[1] == '=': return '='
|
||||||
|
elif m.group()[1] == ',': return ','
|
||||||
|
elif m.group()[1] == 'a': return '\a'
|
||||||
|
elif m.group()[1] == 'b': return '\b'
|
||||||
|
elif m.group()[1] == 'f': return '\f'
|
||||||
|
elif m.group()[1] == 'n': return '\n'
|
||||||
|
elif m.group()[1] == 'r': return '\r'
|
||||||
|
elif m.group()[1] == 't': return '\t'
|
||||||
|
elif m.group()[1] == 'v': return '\v'
|
||||||
|
elif m.group()[1] == '0': return '\0'
|
||||||
|
elif m.group()[1] == 'x': return chr(int(m.group()[2:], 16))
|
||||||
|
elif m.group()[1] == 'u': return chr(int(m.group()[2:], 16))
|
||||||
|
elif m.group()[1] == 'U': return chr(int(m.group()[2:], 16))
|
||||||
|
else: assert False
|
||||||
|
return re.sub(pattern, unescape, s)
|
||||||
|
|
||||||
def openio(path, mode='r', buffering=-1):
|
def openio(path, mode='r', buffering=-1):
|
||||||
# allow '-' for stdin/stdout
|
# allow '-' for stdin/stdout
|
||||||
@@ -888,10 +909,10 @@ def main(csv_paths, *,
|
|||||||
else:
|
else:
|
||||||
line_chars_ = [False]
|
line_chars_ = [False]
|
||||||
|
|
||||||
# allow escape codes in labels/titles
|
# allow %-escaped codes in labels/titles
|
||||||
title = escape(title).splitlines() if title is not None else []
|
title = unescape(title).splitlines() if title is not None else []
|
||||||
xlabel = escape(xlabel).splitlines() if xlabel is not None else []
|
xlabel = unescape(xlabel).splitlines() if xlabel is not None else []
|
||||||
ylabel = escape(ylabel).splitlines() if ylabel is not None else []
|
ylabel = unescape(ylabel).splitlines() if ylabel is not None else []
|
||||||
|
|
||||||
# subplot can also contribute to subplots, resolve this here or things
|
# subplot can also contribute to subplots, resolve this here or things
|
||||||
# become a mess...
|
# become a mess...
|
||||||
@@ -912,8 +933,9 @@ def main(csv_paths, *,
|
|||||||
subplots_get('define', **subplot, subplots=subplots)):
|
subplots_get('define', **subplot, subplots=subplots)):
|
||||||
all_defines[k] |= vs
|
all_defines[k] |= vs
|
||||||
all_defines = sorted(all_defines.items())
|
all_defines = sorted(all_defines.items())
|
||||||
all_labels = ((label or [])
|
all_labels = [(unescape(k), vs) for k, vs in (
|
||||||
+ subplots_get('label', **subplot, subplots=subplots))
|
(label or [])
|
||||||
|
+ subplots_get('label', **subplot, subplots=subplots))]
|
||||||
|
|
||||||
if not all_by and not all_y:
|
if not all_by and not all_y:
|
||||||
print("error: needs --by or -y to figure out fields",
|
print("error: needs --by or -y to figure out fields",
|
||||||
@@ -938,20 +960,22 @@ def main(csv_paths, *,
|
|||||||
ysublabel = s.args.get('ylabel')
|
ysublabel = s.args.get('ylabel')
|
||||||
|
|
||||||
# allow escape codes in sublabels/subtitles
|
# allow escape codes in sublabels/subtitles
|
||||||
subtitle = (escape(subtitle).splitlines()
|
subtitle = (unescape(subtitle).splitlines()
|
||||||
if subtitle is not None else [])
|
if subtitle is not None else [])
|
||||||
xsublabel = (escape(xsublabel).splitlines()
|
xsublabel = (unescape(xsublabel).splitlines()
|
||||||
if xsublabel is not None else [])
|
if xsublabel is not None else [])
|
||||||
ysublabel = (escape(ysublabel).splitlines()
|
ysublabel = (unescape(ysublabel).splitlines()
|
||||||
if ysublabel is not None else [])
|
if ysublabel is not None else [])
|
||||||
|
|
||||||
# don't allow >2 ticklabels and render single ticklabels only once
|
# don't allow >2 ticklabels and render single ticklabels only once
|
||||||
if xticklabels_ is not None:
|
if xticklabels_ is not None:
|
||||||
|
xticklabels_ = [unescape(l) for l in xticklabels_]
|
||||||
if len(xticklabels_) == 1:
|
if len(xticklabels_) == 1:
|
||||||
xticklabels_ = ["", xticklabels_[0]]
|
xticklabels_ = ["", xticklabels_[0]]
|
||||||
elif len(xticklabels_) > 2:
|
elif len(xticklabels_) > 2:
|
||||||
xticklabels_ = [xticklabels_[0], xticklabels_[-1]]
|
xticklabels_ = [xticklabels_[0], xticklabels_[-1]]
|
||||||
if yticklabels_ is not None:
|
if yticklabels_ is not None:
|
||||||
|
yticklabels_ = [unescape(l) for l in yticklabels_]
|
||||||
if len(yticklabels_) == 1:
|
if len(yticklabels_) == 1:
|
||||||
yticklabels_ = ["", yticklabels_[0]]
|
yticklabels_ = ["", yticklabels_[0]]
|
||||||
elif len(yticklabels_) > 2:
|
elif len(yticklabels_) > 2:
|
||||||
@@ -1473,12 +1497,13 @@ if __name__ == "__main__":
|
|||||||
action='append',
|
action='append',
|
||||||
type=lambda x: (
|
type=lambda x: (
|
||||||
lambda k, vs: (
|
lambda k, vs: (
|
||||||
re.sub(r'\\([=\\])', r'\1', k.strip()),
|
k.strip(),
|
||||||
tuple(v.strip() for v in vs.split(',')))
|
tuple(v.strip() for v in vs.split(',')))
|
||||||
)(*re.split(r'(?<!\\)=', x, 1)),
|
)(*re.split(r'(?<!%)=', x, 1)),
|
||||||
help="Use this label for a given group, where a group is roughly "
|
help="Use this label for a given group, where a group is roughly "
|
||||||
"the comma-separated values in the -b/--by, -x, and -y "
|
"the comma-separated values in the -b/--by, -x, and -y "
|
||||||
"fields. Also provides an ordering. Accepts escaped equals.")
|
"fields. Also provides an ordering. Accepts %= and other "
|
||||||
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--color',
|
'--color',
|
||||||
choices=['never', 'always', 'auto'],
|
choices=['never', 'always', 'auto'],
|
||||||
@@ -1556,27 +1581,25 @@ if __name__ == "__main__":
|
|||||||
help="Units for the y-axis.")
|
help="Units for the y-axis.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--xlabel',
|
'--xlabel',
|
||||||
help="Add a label to the x-axis.")
|
help="Add a label to the x-axis. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--ylabel',
|
'--ylabel',
|
||||||
help="Add a label to the y-axis.")
|
help="Add a label to the y-axis. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--xticklabels',
|
'--xticklabels',
|
||||||
type=lambda x: [re.sub(r'\\([,\\])', r'\1', x.strip())
|
type=lambda x: [x.strip() for x in re.split(r'(?<!%),', x)]
|
||||||
for x in re.split(r'(?<!\\),', x)]
|
|
||||||
if x.strip() else [],
|
if x.strip() else [],
|
||||||
help="Comma separated xticklabels. Allows '\,' as an "
|
help="Comma separated xticklabels. Accepts %, and other "
|
||||||
"alternative for a literal ','.")
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--yticklabels',
|
'--yticklabels',
|
||||||
type=lambda x: [re.sub(r'\\([,\\])', r'\1', x.strip())
|
type=lambda x: [x.strip() for x in re.split(r'(?<!%),', x)]
|
||||||
for x in re.split(r'(?<!\\),', x)]
|
|
||||||
if x.strip() else [],
|
if x.strip() else [],
|
||||||
help="Comma separated yticklabels. Allows '\,' as an "
|
help="Comma separated yticklabels. Accepts %, and other "
|
||||||
"alternative for a literal ','.")
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-t', '--title',
|
'-t', '--title',
|
||||||
help="Add a title.")
|
help="Add a title. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--legend', '--legend-right',
|
'-l', '--legend', '--legend-right',
|
||||||
dest='legend_right',
|
dest='legend_right',
|
||||||
|
|||||||
+59
-37
@@ -13,7 +13,6 @@
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
__import__('sys').path.pop(0)
|
__import__('sys').path.pop(0)
|
||||||
|
|
||||||
import codecs
|
|
||||||
import collections as co
|
import collections as co
|
||||||
import csv
|
import csv
|
||||||
import io
|
import io
|
||||||
@@ -22,6 +21,7 @@ import logging
|
|||||||
import math as mt
|
import math as mt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
@@ -131,9 +131,30 @@ def si2(x):
|
|||||||
s = s.rstrip('.')
|
s = s.rstrip('.')
|
||||||
return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p])
|
return '%s%s%s' % ('-' if x < 0 else '', s, SI2_PREFIXES[p])
|
||||||
|
|
||||||
# parse escape strings
|
# parse %-escaped strings
|
||||||
def escape(s):
|
def unescape(s):
|
||||||
return codecs.escape_decode(s.encode('utf8'))[0].decode('utf8')
|
pattern = re.compile(
|
||||||
|
'%[%=,abfnrtv0]'
|
||||||
|
'|' '%x..'
|
||||||
|
'|' '%u....'
|
||||||
|
'|' '%U........')
|
||||||
|
def unescape(m):
|
||||||
|
if m.group()[1] == '%': return '%'
|
||||||
|
elif m.group()[1] == '=': return '='
|
||||||
|
elif m.group()[1] == ',': return ','
|
||||||
|
elif m.group()[1] == 'a': return '\a'
|
||||||
|
elif m.group()[1] == 'b': return '\b'
|
||||||
|
elif m.group()[1] == 'f': return '\f'
|
||||||
|
elif m.group()[1] == 'n': return '\n'
|
||||||
|
elif m.group()[1] == 'r': return '\r'
|
||||||
|
elif m.group()[1] == 't': return '\t'
|
||||||
|
elif m.group()[1] == 'v': return '\v'
|
||||||
|
elif m.group()[1] == '0': return '\0'
|
||||||
|
elif m.group()[1] == 'x': return chr(int(m.group()[2:], 16))
|
||||||
|
elif m.group()[1] == 'u': return chr(int(m.group()[2:], 16))
|
||||||
|
elif m.group()[1] == 'U': return chr(int(m.group()[2:], 16))
|
||||||
|
else: assert False
|
||||||
|
return re.sub(pattern, unescape, s)
|
||||||
|
|
||||||
# we want to use MaxNLocator, but since MaxNLocator forces multiples of 10
|
# we want to use MaxNLocator, but since MaxNLocator forces multiples of 10
|
||||||
# to be an option, we can't really...
|
# to be an option, we can't really...
|
||||||
@@ -622,7 +643,7 @@ def main(csv_paths, output, *,
|
|||||||
colors_ = COLORS
|
colors_ = COLORS
|
||||||
|
|
||||||
if formats is not None:
|
if formats is not None:
|
||||||
formats_ = formats
|
formats_ = [unescape(f) for f in formats]
|
||||||
elif points_and_lines:
|
elif points_and_lines:
|
||||||
formats_ = FORMATS_POINTS_AND_LINES
|
formats_ = FORMATS_POINTS_AND_LINES
|
||||||
elif points:
|
elif points:
|
||||||
@@ -727,8 +748,9 @@ def main(csv_paths, output, *,
|
|||||||
subplots_get('define', **subplot, subplots=subplots)):
|
subplots_get('define', **subplot, subplots=subplots)):
|
||||||
all_defines[k] |= vs
|
all_defines[k] |= vs
|
||||||
all_defines = sorted(all_defines.items())
|
all_defines = sorted(all_defines.items())
|
||||||
all_labels = ((label or [])
|
all_labels = [(unescape(k), vs) for k, vs in (
|
||||||
+ subplots_get('label', **subplot, subplots=subplots))
|
(label or [])
|
||||||
|
+ subplots_get('label', **subplot, subplots=subplots))]
|
||||||
|
|
||||||
if not all_by and not all_y:
|
if not all_by and not all_y:
|
||||||
print("error: needs --by or -y to figure out fields",
|
print("error: needs --by or -y to figure out fields",
|
||||||
@@ -874,7 +896,7 @@ def main(csv_paths, output, *,
|
|||||||
ax.xaxis.set_major_formatter(lambda x, pos:
|
ax.xaxis.set_major_formatter(lambda x, pos:
|
||||||
si2(x)+(xunits_ if xunits_ else ''))
|
si2(x)+(xunits_ if xunits_ else ''))
|
||||||
if xticklabels_ is not None:
|
if xticklabels_ is not None:
|
||||||
ax.xaxis.set_ticklabels(xticklabels_)
|
ax.xaxis.set_ticklabels([unescape(l) for l in xticklabels_])
|
||||||
if xticks_ is None:
|
if xticks_ is None:
|
||||||
ax.xaxis.set_major_locator(AutoMultipleLocator(2))
|
ax.xaxis.set_major_locator(AutoMultipleLocator(2))
|
||||||
elif isinstance(xticks_, list):
|
elif isinstance(xticks_, list):
|
||||||
@@ -887,7 +909,7 @@ def main(csv_paths, output, *,
|
|||||||
ax.xaxis.set_major_formatter(lambda x, pos:
|
ax.xaxis.set_major_formatter(lambda x, pos:
|
||||||
si(x)+(xunits_ if xunits_ else ''))
|
si(x)+(xunits_ if xunits_ else ''))
|
||||||
if xticklabels_ is not None:
|
if xticklabels_ is not None:
|
||||||
ax.xaxis.set_ticklabels(xticklabels_)
|
ax.xaxis.set_ticklabels([unescape(l) for l in xticklabels_])
|
||||||
if xticks_ is None:
|
if xticks_ is None:
|
||||||
ax.xaxis.set_major_locator(mpl.ticker.AutoLocator())
|
ax.xaxis.set_major_locator(mpl.ticker.AutoLocator())
|
||||||
elif isinstance(xticks_, list):
|
elif isinstance(xticks_, list):
|
||||||
@@ -900,7 +922,7 @@ def main(csv_paths, output, *,
|
|||||||
ax.yaxis.set_major_formatter(lambda x, pos:
|
ax.yaxis.set_major_formatter(lambda x, pos:
|
||||||
si2(x)+(yunits_ if yunits_ else ''))
|
si2(x)+(yunits_ if yunits_ else ''))
|
||||||
if yticklabels_ is not None:
|
if yticklabels_ is not None:
|
||||||
ax.yaxis.set_ticklabels(yticklabels_)
|
ax.yaxis.set_ticklabels([unescape(l) for l in yticklabels_])
|
||||||
if yticks_ is None:
|
if yticks_ is None:
|
||||||
ax.yaxis.set_major_locator(AutoMultipleLocator(2))
|
ax.yaxis.set_major_locator(AutoMultipleLocator(2))
|
||||||
elif isinstance(yticks_, list):
|
elif isinstance(yticks_, list):
|
||||||
@@ -913,7 +935,7 @@ def main(csv_paths, output, *,
|
|||||||
ax.yaxis.set_major_formatter(lambda x, pos:
|
ax.yaxis.set_major_formatter(lambda x, pos:
|
||||||
si(x)+(yunits_ if yunits_ else ''))
|
si(x)+(yunits_ if yunits_ else ''))
|
||||||
if yticklabels_ is not None:
|
if yticklabels_ is not None:
|
||||||
ax.yaxis.set_ticklabels(yticklabels_)
|
ax.yaxis.set_ticklabels([unescape(l) for l in yticklabels_])
|
||||||
if yticks_ is None:
|
if yticks_ is None:
|
||||||
ax.yaxis.set_major_locator(mpl.ticker.AutoLocator())
|
ax.yaxis.set_major_locator(mpl.ticker.AutoLocator())
|
||||||
elif isinstance(yticks_, list):
|
elif isinstance(yticks_, list):
|
||||||
@@ -927,11 +949,11 @@ def main(csv_paths, output, *,
|
|||||||
|
|
||||||
# axes subplot labels
|
# axes subplot labels
|
||||||
if xsublabel is not None:
|
if xsublabel is not None:
|
||||||
ax.set_xlabel(escape(xsublabel))
|
ax.set_xlabel(unescape(xsublabel))
|
||||||
if ysublabel is not None:
|
if ysublabel is not None:
|
||||||
ax.set_ylabel(escape(ysublabel))
|
ax.set_ylabel(unescape(ysublabel))
|
||||||
if subtitle is not None:
|
if subtitle is not None:
|
||||||
ax.set_title(escape(subtitle))
|
ax.set_title(unescape(subtitle))
|
||||||
|
|
||||||
# add a legend? a bit tricky with matplotlib
|
# add a legend? a bit tricky with matplotlib
|
||||||
#
|
#
|
||||||
@@ -1002,7 +1024,7 @@ def main(csv_paths, output, *,
|
|||||||
# big hack to get xlabel above the legend! but hey this
|
# big hack to get xlabel above the legend! but hey this
|
||||||
# works really well actually
|
# works really well actually
|
||||||
if xlabel:
|
if xlabel:
|
||||||
ax.set_title(escape(xlabel),
|
ax.set_title(unescape(xlabel),
|
||||||
size=plt.rcParams['axes.labelsize'],
|
size=plt.rcParams['axes.labelsize'],
|
||||||
weight=plt.rcParams['axes.labelweight'])
|
weight=plt.rcParams['axes.labelweight'])
|
||||||
|
|
||||||
@@ -1032,11 +1054,11 @@ def main(csv_paths, output, *,
|
|||||||
|
|
||||||
# axes labels, NOTE we reposition these below
|
# axes labels, NOTE we reposition these below
|
||||||
if xlabel is not None and not legend_below:
|
if xlabel is not None and not legend_below:
|
||||||
fig.supxlabel(escape(xlabel))
|
fig.supxlabel(unescape(xlabel))
|
||||||
if ylabel is not None:
|
if ylabel is not None:
|
||||||
fig.supylabel(escape(ylabel))
|
fig.supylabel(unescape(ylabel))
|
||||||
if title is not None:
|
if title is not None:
|
||||||
fig.suptitle(escape(title))
|
fig.suptitle(unescape(title))
|
||||||
|
|
||||||
# precompute constrained layout and find midpoints to adjust things
|
# precompute constrained layout and find midpoints to adjust things
|
||||||
# that should be centered so they are actually centered
|
# that should be centered so they are actually centered
|
||||||
@@ -1045,11 +1067,11 @@ def main(csv_paths, output, *,
|
|||||||
ymid = (grid[0,0].ax.get_position().y0 + grid[0,-1].ax.get_position().y1)/2
|
ymid = (grid[0,0].ax.get_position().y0 + grid[0,-1].ax.get_position().y1)/2
|
||||||
|
|
||||||
if xlabel is not None and not legend_below:
|
if xlabel is not None and not legend_below:
|
||||||
fig.supxlabel(escape(xlabel), x=xmid)
|
fig.supxlabel(unescape(xlabel), x=xmid)
|
||||||
if ylabel is not None:
|
if ylabel is not None:
|
||||||
fig.supylabel(escape(ylabel), y=ymid)
|
fig.supylabel(unescape(ylabel), y=ymid)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
fig.suptitle(escape(title), x=xmid)
|
fig.suptitle(unescape(title), x=xmid)
|
||||||
|
|
||||||
|
|
||||||
# write the figure!
|
# write the figure!
|
||||||
@@ -1117,12 +1139,13 @@ if __name__ == "__main__":
|
|||||||
action='append',
|
action='append',
|
||||||
type=lambda x: (
|
type=lambda x: (
|
||||||
lambda k, vs: (
|
lambda k, vs: (
|
||||||
re.sub(r'\\([=\\])', r'\1', k.strip()),
|
k.strip(),
|
||||||
tuple(v.strip() for v in vs.split(',')))
|
tuple(v.strip() for v in vs.split(',')))
|
||||||
)(*re.split(r'(?<!\\)=', x, 1)),
|
)(*re.split(r'(?<!%)=', x, 1)),
|
||||||
help="Use this label for a given group, where a group is roughly "
|
help="Use this label for a given group, where a group is roughly "
|
||||||
"the comma-separated values in the -b/--by, -x, and -y "
|
"the comma-separated values in the -b/--by, -x, and -y "
|
||||||
"fields. Also provides an ordering. Accepts escaped equals.")
|
"fields. Also provides an ordering. Accepts %= and other "
|
||||||
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-.', '--points',
|
'-.', '--points',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -1137,10 +1160,9 @@ if __name__ == "__main__":
|
|||||||
help="Comma-separated hex colors to use.")
|
help="Comma-separated hex colors to use.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--formats',
|
'--formats',
|
||||||
type=lambda x: [re.sub(r'\\([,\\])', r'\1', x.strip())
|
type=lambda x: [x.strip() for x in re.split(r'(?<!%),', x)],
|
||||||
for x in re.split(r'(?<!\\),', x)],
|
help="Comma-separated matplotlib formats to use. Accepts %, and "
|
||||||
help="Comma-separated matplotlib formats to use. Accepts escaped "
|
"other %-escaped codes.")
|
||||||
"commas.")
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-W', '--width',
|
'-W', '--width',
|
||||||
type=lambda x: int(x, 0),
|
type=lambda x: int(x, 0),
|
||||||
@@ -1197,25 +1219,25 @@ if __name__ == "__main__":
|
|||||||
help="Units for the y-axis.")
|
help="Units for the y-axis.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--xlabel',
|
'--xlabel',
|
||||||
help="Add a label to the x-axis.")
|
help="Add a label to the x-axis. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--ylabel',
|
'--ylabel',
|
||||||
help="Add a label to the y-axis.")
|
help="Add a label to the y-axis. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--xticklabels',
|
'--xticklabels',
|
||||||
type=lambda x: [re.sub(r'\\([,\\])', r'\1', x.strip())
|
type=lambda x: [x.strip() for x in re.split(r'(?<!%),', x)]
|
||||||
for x in re.split(r'(?<!\\),', x)]
|
|
||||||
if x.strip() else [],
|
if x.strip() else [],
|
||||||
help="Comma separated xticklabels. Accepts escaped commas.")
|
help="Comma separated xticklabels. Accepts %, and other "
|
||||||
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--yticklabels',
|
'--yticklabels',
|
||||||
type=lambda x: [re.sub(r'\\([,\\])', r'\1', x.strip())
|
type=lambda x: [x.strip() for x in re.split(r'(?<!%),', x)]
|
||||||
for x in re.split(r'(?<!\\),', x)]
|
|
||||||
if x.strip() else [],
|
if x.strip() else [],
|
||||||
help="Comma separated yticklabels. Accepts escaped commas.")
|
help="Comma separated yticklabels. Accepts %, and other "
|
||||||
|
"%-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-t', '--title',
|
'-t', '--title',
|
||||||
help="Add a title.")
|
help="Add a title. Accepts %-escaped codes.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--legend', '--legend-right',
|
'-l', '--legend', '--legend-right',
|
||||||
dest='legend_right',
|
dest='legend_right',
|
||||||
|
|||||||
Reference in New Issue
Block a user