Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let row_sep take a "policy" that decides for which definition lists a row separator must be used #37

Closed
janluke opened this issue Jun 6, 2021 · 0 comments · Fixed by #42
Labels
enhancement New feature or request
Milestone

Comments

@janluke
Copy link
Owner

janluke commented Jun 6, 2021

Problems with row_sep

  1. It affects the "Commands" section, which generally doesn't need the extra empty line / separator.

  2. It affects all definition lists indiscriminately, including those that don't benefit from the extra empty line between definitions. Some people may like this consistency. Others, including me, may want to avoid the extra spacing when unneded.

  3. Probably nobody is going to use a row separator other than '\n' (an extra new line), and probably shouldn't. Horizontal lines obtained by repeating one of the unicode "Box drawing" characters like '─' seem too much for separating definitions. This is not a real issue, just something I want to keep in mind: I'm open to "sacrifice" this rather useless flexibility.

Chosen solution: RowSepPolicy

Allow to pass a policy as row_sep:

class RowSepPolicy(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __call__(  # noqa E704
        self, rows: Sequence[Sequence[str]],
        col_widths: Sequence[int],
        col_spacing: int,
    ) -> Optional[str]:
        """Decides which row separator to use (eventually none) in the given
        definition list."""

In practice, the row separator should be the same for all definition lists that satisfy a given condition (see RowSepIf below). So why this class is designed as it is? Mainly for one reason: it leaves open the door to policies that can decide a row separator for each individual row (feature I'm personally against to for now), without breaking changes. This would allow to implement the old Click 7.2 behavior, which inserted an empty line only after options with a long help (which I don't like). Adding this feature would be possible without breaking changes, by extending the return type to Union[None, str, Sequence[str]].

The concrete implementation that the user will actually use is much more pragmatic:

RowSepIf(condition: RowSepCondition, sep: Union[str, SepGenerator] = '\n')

where:

  • RowSepCondition is a function (rows, col_widths, col_spacing) -> bool
  • SepGenerator is a function (width: int) -> str that generates a separator given a line width.

Cloup will provide:

  • a function multiline_rows_are_at_least(count_or_percentage) that returns a RowSepCondition

  • an Hline(pattern: str) class that implements SepGenerator and has different static members for different kinds of line: Hline.solid, Hline.dashed, Hline.densely_dashed, Hline.dotted

So, in the end I decided to allow separators other than '\n' and Hline makes it trivial to use one. Plus, it may be useful in other parts of the program (e.g. print(Hline.solid(width), end='')).

For consistency, row_sep will also accept a SepGenerator (#39). So after this is implemented, row_sep will accept either:

  • a string (e.g. '\n')
  • a SepGenerator (e.g. Hline.dotted)
  • a RowSepPolicy (e.g. RowSepIf(multiline_rows_are_at_least(2))).

Limitations

  • A policy can't take decisions based on all sections. This would require serious refactoring.
  • Intentionally, a policy can't decide a different row_sep for each definition. Nonetheless, the interface was designed to be flexible enough for extensions in this regard without breaking changes.

Evaluated solutions

The quick and easy one

I could just add row_sep to write_dl() so that the caller, e.g. format_commands, can set it to '' if opportune. This is also enough to allow format_options for eventually forcing row_sep to '' only for definition lists that don't need the extra spacing. Nonetheless this would either be hard-coded behaviour (difficult to override) or would require other formatting parameters in mixins, which I want to avoid.

Similar to chosen one, but allowing only for empty lines, not arbitrary strings

This would have required to replace row_sep with some other parameter.

@janluke janluke added this to the v0.9.0 milestone Jun 13, 2021
@janluke janluke added the enhancement New feature or request label Jun 13, 2021
@janluke janluke changed the title Use row_sep only in definition lists that benefit from it Let row_sep take a "policy" that decides for which definition lists a row separator must be used Jun 14, 2021
janluke added a commit that referenced this issue Jun 16, 2021
1. Set it to `None` by default and write a `\n` after it (closes #41).
2. Insert `row_sep` only in between rows (closes #36).
3. Define `SepGenerator` as a protocol and allow to pass a generator
   to `row_sep` (closes #39).
4. Implements the `Hline` SepGenerator.
5. Define a `RowSepPolicy` base class and implement it with `RowSepIf`
   (closes #37).
janluke added a commit that referenced this issue Jun 16, 2021
Redesign row_sep

1. Set it to `None` by default and write a `\n` after it (closes #41).
2. Insert `row_sep` only in between rows (closes #36).
3. Define `SepGenerator` as a protocol and allow to pass a generator
   to `row_sep` (closes #39).
4. Implements the `Hline` SepGenerator.
5. Define a `RowSepPolicy` base class and implement it with `RowSepIf`
   (closes #37).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant