Skip to content

Commit

Permalink
Merge pull request #6274 from cylc/8.3.x-sync
Browse files Browse the repository at this point in the history
🤖 Merge 8.3.x-sync into master
  • Loading branch information
MetRonnie authored Jul 31, 2024
2 parents d46059d + 0dbe2b6 commit 9e991dd
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 32 deletions.
56 changes: 32 additions & 24 deletions cylc/flow/option_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,7 @@ def format_help_headings(string):
"""Put "headings" in bold.
Where "headings" are lines with no indentation which are followed by a
colon e.g:
Examples:
...
colon.
"""
return cparse(
re.sub(
Expand All @@ -200,6 +196,9 @@ def take_action(self, action, dest, opt, value, values, parser):


class CylcHelpFormatter(IndentedHelpFormatter):
"""This formatter handles colour in help text, and automatically
colourises headings & shell examples."""

def _format(self, text: str) -> str:
"""Format help (usage) text on the fly to handle coloring.
Expand All @@ -214,21 +213,28 @@ def _format(self, text: str) -> str:
"""
if should_use_color(self.parser.values):
# Add color formatting to examples text.
text = format_shell_examples(
return format_shell_examples(
format_help_headings(text)
)
else:
# Strip any hardwired formatting
text = cstrip(text)
return text
# Else strip any hardwired formatting
return cstrip(text)

def format_usage(self, usage):
def format_usage(self, usage: str) -> str:
return super().format_usage(self._format(usage))

# If we start using "description" as well as "usage" (also epilog):
# def format_description(self, description):
# return super().format_description(self._format(description))

def format_option(self, option: Option) -> str:
"""Format help text for options."""
if option.help:
if should_use_color(self.parser.values):
option.help = cparse(option.help)
else:
option.help = cstrip(option.help)
return super().format_option(option)


class CylcOptionParser(OptionParser):

Expand Down Expand Up @@ -693,20 +699,21 @@ def combine_options_pair(first_list, second_list):
return output


def add_sources_to_helps(options, modify=None):
"""Prettify format of list of CLI commands this option applies to
def add_sources_to_helps(
options: Iterable[OptionSettings], modify: Optional[dict] = None
) -> None:
"""Get list of CLI commands this option applies to
and prepend that list to the start of help.
Arguments:
Options:
Options dicts to modify help upon.
options:
List of OptionSettings to modify help upon.
modify:
Dict of items to substitute: Intended to allow one
to replace cylc-rose with the names of the sub-commands
cylc rose options apply to.
"""
modify = {} if modify is None else modify
cformat = cparse if should_use_color(options) else cstrip
for option in options:
if hasattr(option, 'sources'):
sources = list(option.sources)
Expand All @@ -715,25 +722,26 @@ def add_sources_to_helps(options, modify=None):
sources.append(sub)
sources.remove(match)

option.kwargs['help'] = cformat(
option.kwargs['help'] = (
f'<cyan>[{", ".join(sources)}]</cyan>'
f' {option.kwargs["help"]}'
)
return options


def combine_options(*args, modify=None):
"""Combine a list of argument dicts.
def combine_options(
*args: List[OptionSettings], modify: Optional[dict] = None
) -> List[OptionSettings]:
"""Combine lists of Cylc options.
Ordering should be irrelevant because combine_options_pair should
be commutative, and the overall order of args is not relevant.
"""
list_ = list(args)
output = list_[0]
for arg in list_[1:]:
output = args[0]
for arg in args[1:]:
output = combine_options_pair(arg, output)

return add_sources_to_helps(output, modify)
add_sources_to_helps(output, modify)
return output


def cleanup_sysargv(
Expand Down
55 changes: 48 additions & 7 deletions cylc/flow/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,44 @@

"""Functionality to assist working with terminals"""

from functools import wraps
import inspect
import json
import logging
import os
from subprocess import PIPE, Popen # nosec
import sys
from functools import wraps
from subprocess import PIPE, Popen # nosec
from textwrap import wrap
from typing import Any, Callable, List, Optional, TYPE_CHECKING
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
List,
Optional,
Sequence,
TypeVar,
Union,
cast,
overload,
)

from ansimarkup import parse as cparse
from colorama import init as color_init

import cylc.flow.flags
from cylc.flow import CYLC_LOG
from cylc.flow.exceptions import CylcError
import cylc.flow.flags
from cylc.flow.loggingutil import CylcLogFormatter
from cylc.flow.parsec.exceptions import ParsecError


if TYPE_CHECKING:
from optparse import OptionParser, Values

T = TypeVar('T')
StrFunc = Callable[[str], str]


# CLI exception message format
EXC_EXIT = cparse('<red><bold>{name}: </bold>{exc}</red>')
Expand Down Expand Up @@ -341,7 +357,32 @@ def wrapper(*api_args: str) -> None:
return inner


def prompt(message, options, default=None, process=None):
@overload
def prompt(
message: str,
options: Sequence[str],
default: Optional[str] = None,
process: Optional['StrFunc'] = None,
) -> str:
...


@overload
def prompt(
message: str,
options: Dict[str, 'T'],
default: Optional[str] = None,
process: Optional['StrFunc'] = None,
) -> 'T':
...


def prompt(
message: str,
options: Union[Sequence[str], Dict[str, 'T']],
default: Optional[str] = None,
process: Optional['StrFunc'] = None,
) -> Union[str, 'T']:
"""Dead simple CLI textual prompting.
Args:
Expand Down Expand Up @@ -369,10 +410,10 @@ def prompt(message, options, default=None, process=None):
if default:
default_ = f'[{default}] '
message += f': {default_}{",".join(options)}? '
usr = None
usr = cast('str', None)
while usr not in options:
usr = input(f'{message}')
if default is not None and usr == '':
if default is not None and not usr:
usr = default
if process:
usr = process(usr)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ template = "changes.d/changelog-template.jinja"
underlines = ["", "", ""]
title_format = "## __cylc-{version} (Released {project_date})__"
issue_format = "[#{issue}](https://github.com/cylc/cylc-flow/pull/{issue})"
ignore = ["changelog-template.jinja"]

# These changelog sections will be shown in the defined order:
[[tool.towncrier.type]]
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ tests =
pytest-mock>=3.7
pytest>=6
testfixtures>=6.11.0
towncrier>=23
towncrier>=24.7.0; python_version > "3.7"
# Type annotation stubs
# http://mypy-lang.blogspot.com/2021/05/the-upcoming-switch-to-modular-typeshed.html
types-Jinja2>=0.1.3
Expand Down

0 comments on commit 9e991dd

Please sign in to comment.