Skip to content

Commit d501d01

Browse files
authored
Merge pull request #201 from devdanzin/column_widths
Allow output from commands to be wrapped to terminal width (fix #81)
2 parents b0411ae + fd5511b commit d501d01

File tree

13 files changed

+314
-22
lines changed

13 files changed

+314
-22
lines changed

src/wily/__main__.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,13 @@ def build(ctx, max_revisions, targets, operators, archiver):
154154
@click.option(
155155
"-m", "--message/--no-message", default=False, help=_("Include revision message")
156156
)
157-
def index(ctx, message):
157+
@click.option(
158+
"-w",
159+
"--wrap/--no-wrap",
160+
default=True,
161+
help=_("Wrap index text to fit in terminal"),
162+
)
163+
def index(ctx, message, wrap):
158164
"""Show the history archive in the .wily/ folder."""
159165
config = ctx.obj["CONFIG"]
160166

@@ -163,7 +169,7 @@ def index(ctx, message):
163169

164170
from wily.commands.index import index
165171

166-
index(config=config, include_message=message)
172+
index(config=config, include_message=message, wrap=wrap)
167173

168174

169175
@cli.command(
@@ -206,8 +212,14 @@ def index(ctx, message):
206212
help=_("Return a non-zero exit code under the specified threshold"),
207213
type=click.INT,
208214
)
215+
@click.option(
216+
"-w",
217+
"--wrap/--no-wrap",
218+
default=True,
219+
help=_("Wrap rank text to fit in terminal"),
220+
)
209221
@click.pass_context
210-
def rank(ctx, path, metric, revision, limit, desc, threshold):
222+
def rank(ctx, path, metric, revision, limit, desc, threshold, wrap):
211223
"""Rank files, methods and functions in order of any metrics, e.g. complexity."""
212224
config = ctx.obj["CONFIG"]
213225

@@ -225,6 +237,7 @@ def rank(ctx, path, metric, revision, limit, desc, threshold):
225237
limit=limit,
226238
threshold=threshold,
227239
descending=desc,
240+
wrap=wrap,
228241
)
229242

230243

@@ -260,9 +273,15 @@ def rank(ctx, path, metric, revision, limit, desc, threshold):
260273
default=False,
261274
help=_("Only show revisions that have changes"),
262275
)
276+
@click.option(
277+
"-w",
278+
"--wrap/--no-wrap",
279+
default=True,
280+
help=_("Wrap report text to fit in terminal"),
281+
)
263282
@click.pass_context
264283
def report(
265-
ctx, file, metrics, number, message, format, console_format, output, changes
284+
ctx, file, metrics, number, message, format, console_format, output, changes, wrap
266285
):
267286
"""Show metrics for a given file."""
268287
config = ctx.obj["CONFIG"]
@@ -297,6 +316,7 @@ def report(
297316
format=ReportFormat[format],
298317
console_format=style,
299318
changes_only=changes,
319+
wrap=wrap,
300320
)
301321

302322

@@ -322,8 +342,14 @@ def report(
322342
@click.option(
323343
"-r", "--revision", help=_("Compare against specific revision"), type=click.STRING
324344
)
345+
@click.option(
346+
"-w",
347+
"--wrap/--no-wrap",
348+
default=True,
349+
help=_("Wrap diff text to fit in terminal"),
350+
)
325351
@click.pass_context
326-
def diff(ctx, files, metrics, all, detail, revision):
352+
def diff(ctx, files, metrics, all, detail, revision, wrap):
327353
"""Show the differences in metrics for each file."""
328354
config = ctx.obj["CONFIG"]
329355

@@ -347,6 +373,7 @@ def diff(ctx, files, metrics, all, detail, revision):
347373
changes_only=not all,
348374
detail=detail,
349375
revision=revision,
376+
wrap=wrap,
350377
)
351378

352379

@@ -432,8 +459,14 @@ def clean(ctx, yes):
432459

433460

434461
@cli.command("list-metrics", help=_("""List the available metrics."""))
462+
@click.option(
463+
"-w",
464+
"--wrap/--no-wrap",
465+
default=True,
466+
help=_("Wrap metrics text to fit in terminal"),
467+
)
435468
@click.pass_context
436-
def list_metrics(ctx):
469+
def list_metrics(ctx, wrap):
437470
"""List the available metrics."""
438471
config = ctx.obj["CONFIG"]
439472

@@ -442,7 +475,7 @@ def list_metrics(ctx):
442475

443476
from wily.commands.list_metrics import list_metrics
444477

445-
list_metrics()
478+
list_metrics(wrap)
446479

447480

448481
@cli.command("setup", help=_("""Run a guided setup to build the wily cache."""))

src/wily/commands/diff.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from wily.archivers import resolve_archiver
1616
from wily.commands.build import run_operator
1717
from wily.config import DEFAULT_PATH
18-
from wily.helper import get_style
18+
from wily.helper import get_maxcolwidth, get_style
1919
from wily.operators import (
2020
BAD_COLORS,
2121
GOOD_COLORS,
@@ -27,7 +27,9 @@
2727
from wily.state import State
2828

2929

30-
def diff(config, files, metrics, changes_only=True, detail=True, revision=None):
30+
def diff(
31+
config, files, metrics, changes_only=True, detail=True, revision=None, wrap=False
32+
):
3133
"""
3234
Show the differences in metrics for each of the files.
3335
@@ -161,8 +163,15 @@ def diff(config, files, metrics, changes_only=True, detail=True, revision=None):
161163
descriptions = [metric.description for operator, metric in metrics]
162164
headers = ("File", *descriptions)
163165
if len(results) > 0:
166+
maxcolwidth = get_maxcolwidth(headers, wrap)
164167
style = get_style()
165168
print(
166169
# But it still makes more sense to show the newest at the top, so reverse again
167-
tabulate.tabulate(headers=headers, tabular_data=results, tablefmt=style)
170+
tabulate.tabulate(
171+
headers=headers,
172+
tabular_data=results,
173+
tablefmt=style,
174+
maxcolwidths=maxcolwidth,
175+
maxheadercolwidths=maxcolwidth,
176+
)
168177
)

src/wily/commands/index.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import tabulate
77

88
from wily import MAX_MESSAGE_WIDTH, format_date, format_revision, logger
9-
from wily.helper import get_style
9+
from wily.helper import get_maxcolwidth, get_style
1010
from wily.state import State
1111

1212

13-
def index(config, include_message=False):
13+
def index(config, include_message=False, wrap=False):
1414
"""
1515
Show information about the cache and runtime.
1616
@@ -54,6 +54,14 @@ def index(config, include_message=False):
5454
headers = ("Revision", "Author", "Message", "Date")
5555
else:
5656
headers = ("Revision", "Author", "Date")
57-
57+
maxcolwidth = get_maxcolwidth(headers, wrap)
5858
style = get_style()
59-
print(tabulate.tabulate(headers=headers, tabular_data=data, tablefmt=style))
59+
print(
60+
tabulate.tabulate(
61+
headers=headers,
62+
tabular_data=data,
63+
tablefmt=style,
64+
maxcolwidths=maxcolwidth,
65+
maxheadercolwidths=maxcolwidth,
66+
)
67+
)

src/wily/commands/list_metrics.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55
"""
66
import tabulate
77

8-
from wily.helper import get_style
8+
from wily.helper import get_maxcolwidth, get_style
99
from wily.operators import ALL_OPERATORS
1010

1111

12-
def list_metrics():
12+
def list_metrics(wrap):
1313
"""List metrics available."""
14+
headers = ("Name", "Description", "Type", "Measure", "Aggregate")
15+
maxcolwidth = get_maxcolwidth(headers, wrap)
1416
style = get_style()
1517
for name, operator in ALL_OPERATORS.items():
1618
print(f"{name} operator:")
1719
if len(operator.cls.metrics) > 0:
1820
print(
1921
tabulate.tabulate(
20-
headers=("Name", "Description", "Type"),
22+
headers=headers,
2123
tabular_data=operator.cls.metrics,
2224
tablefmt=style,
25+
maxcolwidths=maxcolwidth,
26+
maxheadercolwidths=maxcolwidth,
2327
)
2428
)

src/wily/commands/rank.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
from wily import format_date, format_revision, logger
1919
from wily.archivers import resolve_archiver
2020
from wily.config import DEFAULT_PATH
21-
from wily.helper import get_style
21+
from wily.helper import get_maxcolwidth, get_style
2222
from wily.operators import resolve_metric_as_tuple
2323
from wily.state import State
2424

2525

26-
def rank(config, path, metric, revision_index, limit, threshold, descending):
26+
def rank(config, path, metric, revision_index, limit, threshold, descending, wrap):
2727
"""
2828
Rank command ordering files, methods or functions using metrics.
2929
@@ -121,8 +121,17 @@ def rank(config, path, metric, revision_index, limit, threshold, descending):
121121
data.append(["Total", total])
122122

123123
headers = ("File", metric.description)
124+
maxcolwidth = get_maxcolwidth(headers, wrap)
124125
style = get_style()
125-
print(tabulate.tabulate(headers=headers, tabular_data=data, tablefmt=style))
126+
print(
127+
tabulate.tabulate(
128+
headers=headers,
129+
tabular_data=data,
130+
tablefmt=style,
131+
maxcolwidths=maxcolwidth,
132+
maxheadercolwidths=maxcolwidth,
133+
)
134+
)
126135

127136
if threshold and total < threshold:
128137
logger.error(

src/wily/commands/report.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import tabulate
1212

1313
from wily import MAX_MESSAGE_WIDTH, format_date, format_revision, logger
14+
from wily.helper import get_maxcolwidth
1415
from wily.helper.custom_enums import ReportFormat
1516
from wily.lang import _
1617
from wily.operators import MetricType, resolve_metric_as_tuple
@@ -31,6 +32,7 @@ def report(
3132
format=ReportFormat.CONSOLE,
3233
console_format=None,
3334
changes_only=False,
35+
wrap=False,
3436
):
3537
"""
3638
Show metrics for a given file.
@@ -211,8 +213,13 @@ def report(
211213

212214
logger.info(f"wily report was saved to {report_path}")
213215
else:
216+
maxcolwidth = get_maxcolwidth(headers, wrap)
214217
print(
215218
tabulate.tabulate(
216-
headers=headers, tabular_data=data[::-1], tablefmt=console_format
219+
headers=headers,
220+
tabular_data=data[::-1],
221+
tablefmt=console_format,
222+
maxcolwidths=maxcolwidth,
223+
maxheadercolwidths=maxcolwidth,
217224
)
218225
)

src/wily/helper/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
"""Helper package for wily."""
2+
import shutil
23
import sys
34

45
from wily.config import DEFAULT_GRID_STYLE
56

67

8+
def get_maxcolwidth(headers, wrap=True):
9+
"""Calculate the maximum column width for a given terminal width."""
10+
if not wrap:
11+
return
12+
width = shutil.get_terminal_size()[0]
13+
columns = len(headers)
14+
if width < 80:
15+
padding = columns + 2
16+
elif width < 120:
17+
padding = columns - 2
18+
else:
19+
padding = columns - 4
20+
maxcolwidth = (width // columns) - padding
21+
return max(maxcolwidth, 1)
22+
23+
724
def get_style(style=DEFAULT_GRID_STYLE):
825
"""Select the tablefmt style for tabulate according to what sys.stdout can handle."""
926
if style == DEFAULT_GRID_STYLE:

test/integration/test_diff.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ def test_diff_output_all(builddir):
4545
assert "test.py" in result.stdout
4646

4747

48+
def test_diff_output_all_wrapped(builddir):
49+
"""Test the diff feature with wrapping"""
50+
runner = CliRunner()
51+
result = runner.invoke(
52+
main.cli,
53+
["--debug", "--path", builddir, "diff", _path, "--all", "--wrap"],
54+
catch_exceptions=False,
55+
)
56+
assert result.exit_code == 0, result.stdout
57+
assert "test.py" in result.stdout
58+
59+
4860
def test_diff_output_bad_path(builddir):
4961
"""Test the diff feature with no changes"""
5062
runner = CliRunner()

test/integration/test_index.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,18 @@ def test_index_with_messages(builddir):
3434
assert "add line" in result.stdout
3535
assert "remove line" in result.stdout
3636
assert result.exit_code == 0, result.stdout
37+
38+
39+
def test_index_with_messages_wrapped(builddir):
40+
"""
41+
Test that index works with a build with git commit messages and wrapping
42+
"""
43+
runner = CliRunner()
44+
result = runner.invoke(
45+
main.cli, ["--path", builddir, "index", "--message", "--wrap"]
46+
)
47+
assert result.stdout.count("An author") == 3
48+
assert "basic test" in result.stdout
49+
assert "add line" in result.stdout
50+
assert "remove line" in result.stdout
51+
assert result.exit_code == 0, result.stdout
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from click.testing import CliRunner
2+
3+
import wily.__main__ as main
4+
5+
6+
def test_list_metrics(builddir):
7+
"""
8+
Test that list-metrics works and is ordered
9+
"""
10+
runner = CliRunner()
11+
result = runner.invoke(main.cli, ["list-metrics"])
12+
assert result.stdout.count("operator") == 4
13+
assert "cyclomatic" in result.stdout
14+
assert "maintainability" in result.stdout
15+
assert "raw" in result.stdout
16+
assert "halstead" in result.stdout
17+
# Test ordering
18+
i = result.stdout.index
19+
assert i("cyclomatic") < i("maintainability") < i("raw") < i("halstead")
20+
21+
22+
def test_list_metrics_wrapped(builddir):
23+
"""
24+
Test that list-metrics works with wrapping
25+
"""
26+
runner = CliRunner()
27+
result = runner.invoke(main.cli, ["list-metrics", "--wrap"])
28+
assert result.stdout.count("operator") == 4
29+
assert "cyclomatic" in result.stdout
30+
assert "maintainability" in result.stdout
31+
assert "raw" in result.stdout
32+
assert "halstead" in result.stdout

0 commit comments

Comments
 (0)