Skip to content

Commit

Permalink
Smarter minimum column count, help positioning
Browse files Browse the repository at this point in the history
  • Loading branch information
brentyi committed Sep 21, 2022
1 parent 9c133bf commit 3df0e54
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 25 deletions.
39 changes: 22 additions & 17 deletions dcargs/_argparse_formatter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import argparse
import contextlib
import dataclasses
import functools
import shutil
from typing import Any, ContextManager, Dict, Generator, List, Optional
from typing import Any, ContextManager, Generator, List, Optional

from rich.columns import Columns
from rich.console import Console, Group, RenderableType
Expand Down Expand Up @@ -114,22 +113,12 @@ def str_from_rich(
return out.get().rstrip("\n")


def make_formatter_class(field_count: int) -> Any:
return functools.partial(_ArgparseHelpFormatter, field_count=field_count)


class _ArgparseHelpFormatter(argparse.RawDescriptionHelpFormatter):
def __init__(self, prog, *, field_count: int):
class DcargsArgparseHelpFormatter(argparse.RawDescriptionHelpFormatter):
def __init__(self, prog: str):
indent_increment = 4
width = shutil.get_terminal_size().columns - 2

# Try to make helptext more concise when we have a lot of fields!
if field_count > 64: # pragma: no cover
# When there are more fields, make helptext more compact.
max_help_position = 8
else:
max_help_position = 36 # Usual is 24.

max_help_position = 24
self._fixed_help_position = False
super().__init__(prog, indent_increment, max_help_position, width)

def _format_args(self, action, default_metavar):
Expand Down Expand Up @@ -172,6 +161,18 @@ def _split_lines(self, text, width):
def _fill_text(self, text, width, indent):
return "".join(indent + line for line in text.splitlines(keepends=True))

def format_help(self):
# Try with and without a fixed help position, then return the shorter help
# message.
# For dense multi-column layouts, the fixed help position is often shorter.
# For wider layouts, using the default help position settings can be more
# efficient.
self._fixed_help_position = False
help1 = super().format_help()
self._fixed_help_position = True
help2 = super().format_help()
return help1 if help1.count("\n") < help2.count("\n") else help2

class _Section(object):
def __init__(self, formatter, parent, heading=None):
self.formatter = formatter
Expand Down Expand Up @@ -222,6 +223,7 @@ def _dcargs_format_root(self):
min(
sum(column_parts_lines) // height_breakpoint + 1,
self.formatter._width // min_column_width,
len(column_parts),
),
)
if column_count > 1:
Expand Down Expand Up @@ -259,6 +261,8 @@ def _format_action(self, action: argparse.Action):
help_position = min(
self.formatter._action_max_length + 4, self.formatter._max_help_position
)
if self.formatter._fixed_help_position:
help_position = 4
indent = self.formatter._current_indent

item_parts: List[RenderableType] = []
Expand All @@ -273,6 +277,7 @@ def _format_action(self, action: argparse.Action):
if (
action.help
and len(_strings.strip_ansi_sequences(invocation)) < help_position - 1
and not self.formatter._fixed_help_position
):
table = Table(show_header=False, box=None, padding=0)
table.add_column(width=help_position - indent)
Expand Down Expand Up @@ -329,7 +334,7 @@ def _dcargs_format_nonroot(self):
item_content = func(*args)
if (
getattr(func, "__func__", None)
is _ArgparseHelpFormatter._format_action
is DcargsArgparseHelpFormatter._format_action
):
(action,) = args
assert isinstance(action, argparse.Action)
Expand Down
4 changes: 1 addition & 3 deletions dcargs/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,7 @@ def fix_arg(arg: str) -> str:
with _argparse_formatter.ansi_context():
parser = argparse.ArgumentParser(
prog=prog,
formatter_class=_argparse_formatter.make_formatter_class(
len(parser_definition.args)
),
formatter_class=_argparse_formatter.DcargsArgparseHelpFormatter,
)
parser_definition.apply(parser)

Expand Down
6 changes: 2 additions & 4 deletions dcargs/_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,17 +434,15 @@ def apply(
if self.can_be_none:
subparser = argparse_subparsers.add_parser(
name=_strings.subparser_name_from_type(self.prefix, None),
formatter_class=_argparse_formatter.make_formatter_class(0),
formatter_class=_argparse_formatter.DcargsArgparseHelpFormatter,
help="",
)
subparser_tree_nodes.append(subparser)

for name, subparser_def in self.parser_from_name.items():
subparser = argparse_subparsers.add_parser(
name,
formatter_class=_argparse_formatter.make_formatter_class(
len(subparser_def.args)
),
formatter_class=_argparse_formatter.DcargsArgparseHelpFormatter,
help=subparser_def.description,
)
subparser_def.apply(subparser)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dcargs"
version = "0.3.7"
version = "0.3.8"
description = "Strongly typed, zero-effort CLI interfaces"
authors = ["brentyi <[email protected]>"]
include = ["./dcargs/**/*"]
Expand Down

0 comments on commit 3df0e54

Please sign in to comment.