Colify now supports fixing the number of columns.

This commit is contained in:
Todd Gamblin 2014-12-01 21:29:01 -08:00
parent 22e4d11010
commit 72c753b93e

View file

@ -47,7 +47,7 @@ def __repr__(self):
return "<Config: %s>" % ", ".join("%s: %r" % a for a in attrs) return "<Config: %s>" % ", ".join("%s: %r" % a for a in attrs)
def config_variable_cols(elts, console_width, padding): def config_variable_cols(elts, console_width, padding, cols=0):
"""Variable-width column fitting algorithm. """Variable-width column fitting algorithm.
This function determines the most columns that can fit in the This function determines the most columns that can fit in the
@ -55,20 +55,28 @@ def config_variable_cols(elts, console_width, padding):
the width of the longest element in the list, each column takes the width of the longest element in the list, each column takes
the width of its own longest element. This packs elements more the width of its own longest element. This packs elements more
efficiently on screen. efficiently on screen.
If cols is nonzero, force
""" """
if cols < 0:
raise ValueError("cols must be non-negative.")
# Get a bound on the most columns we could possibly have. # Get a bound on the most columns we could possibly have.
lengths = [len(elt) for elt in elts] lengths = [len(elt) for elt in elts]
max_cols = max(1, console_width / (min(lengths) + padding)) max_cols = max(1, console_width / (min(lengths) + padding))
max_cols = min(len(elts), max_cols) max_cols = min(len(elts), max_cols)
# Range of column counts to try. If forced, use the supplied value.
col_range = [cols] if cols else xrange(1, max_cols+1)
# Determine the most columns possible for the console width. # Determine the most columns possible for the console width.
configs = [ColumnConfig(c) for c in xrange(1, max_cols+1)] configs = [ColumnConfig(c) for c in col_range]
for elt, length in enumerate(lengths): for elt, length in enumerate(lengths):
for i, conf in enumerate(configs): for conf in configs:
if conf.valid: if conf.valid:
col = elt / ((len(elts) + i) / (i + 1)) col = elt / ((len(elts) + conf.cols - 1) / conf.cols)
padded = length padded = length
if col < i: if col < (conf.cols - 1):
padded += padding padded += padding
if conf.widths[col] < padded: if conf.widths[col] < padded:
@ -87,14 +95,18 @@ def config_variable_cols(elts, console_width, padding):
return config return config
def config_uniform_cols(elts, console_width, padding): def config_uniform_cols(elts, console_width, padding, cols=0):
"""Uniform-width column fitting algorithm. """Uniform-width column fitting algorithm.
Determines the longest element in the list, and determines how Determines the longest element in the list, and determines how
many columns of that width will fit on screen. Returns a many columns of that width will fit on screen. Returns a
corresponding column config. corresponding column config.
""" """
if cols < 0:
raise ValueError("cols must be non-negative.")
max_len = max(len(elt) for elt in elts) + padding max_len = max(len(elt) for elt in elts) + padding
if cols == 0:
cols = max(1, console_width / max_len) cols = max(1, console_width / max_len)
cols = min(len(elts), cols) cols = min(len(elts), cols)
config = ColumnConfig(cols) config = ColumnConfig(cols)
@ -115,6 +127,10 @@ def colify(elts, **options):
output=<stream> A file object to write to. Default is sys.stdout. output=<stream> A file object to write to. Default is sys.stdout.
indent=<int> Optionally indent all columns by some number of spaces. indent=<int> Optionally indent all columns by some number of spaces.
padding=<int> Spaces between columns. Default is 2. padding=<int> Spaces between columns. Default is 2.
width=<int> Width of the output. Default is 80 if tty is not detected.
cols=<int> Force number of columns. Default is to size to terminal,
or single-column if no tty
tty=<bool> Whether to attempt to write to a tty. Default is to tty=<bool> Whether to attempt to write to a tty. Default is to
autodetect a tty. Set to False to force single-column output. autodetect a tty. Set to False to force single-column output.
@ -123,15 +139,19 @@ def colify(elts, **options):
Variable-width columns are tighter, uniform columns are all the Variable-width columns are tighter, uniform columns are all the
same width and fit less data on the screen. same width and fit less data on the screen.
width=<int> Width of the output. Default is 80 if tty is not detected. decorator=<func> Function to add decoration (such as color) after columns have
already been fitted. Useful for fitting based only on
positive-width characters.
""" """
# Get keyword arguments or set defaults # Get keyword arguments or set defaults
cols = options.pop("cols", 0)
output = options.pop("output", sys.stdout) output = options.pop("output", sys.stdout)
indent = options.pop("indent", 0) indent = options.pop("indent", 0)
padding = options.pop("padding", 2) padding = options.pop("padding", 2)
tty = options.pop('tty', None) tty = options.pop('tty', None)
method = options.pop("method", "variable") method = options.pop("method", "variable")
console_cols = options.pop("width", None) console_cols = options.pop("width", None)
decorator = options.pop("decorator", lambda x:x)
if options: if options:
raise TypeError("'%s' is an invalid keyword argument for this function." raise TypeError("'%s' is an invalid keyword argument for this function."
@ -159,9 +179,9 @@ def colify(elts, **options):
# Choose a method. Variable-width colums vs uniform-width. # Choose a method. Variable-width colums vs uniform-width.
if method == "variable": if method == "variable":
config = config_variable_cols(elts, console_cols, padding) config = config_variable_cols(elts, console_cols, padding, cols)
elif method == "uniform": elif method == "uniform":
config = config_uniform_cols(elts, console_cols, padding) config = config_uniform_cols(elts, console_cols, padding, cols)
else: else:
raise ValueError("method must be one of: " + allowed_methods) raise ValueError("method must be one of: " + allowed_methods)
@ -176,7 +196,7 @@ def colify(elts, **options):
output.write(" " * indent) output.write(" " * indent)
for col in xrange(cols): for col in xrange(cols):
elt = col * rows + row elt = col * rows + row
output.write(formats[col] % elts[elt]) output.write(formats[col] % decorator(elts[elt]))
output.write("\n") output.write("\n")
row += 1 row += 1