Source code for minos.cli.wizards.questions

from __future__ import (
    annotations,
)

from contextlib import (
    suppress,
)
from functools import (
    partial,
)
from typing import (
    Any,
    Callable,
    Optional,
    Union,
)

from jinja2 import (
    Environment,
)
from rich.prompt import (
    Confirm,
    FloatPrompt,
    IntPrompt,
    Prompt,
)

from ..consoles import (
    console,
)


[docs]class Question: """Question class."""
[docs] def __init__( self, name: str, type_: str, help_: Optional[str] = None, choices: Optional[Union[list[Any], dict[str, Any]]] = None, default: Optional[Any] = None, secret: bool = False, link: dict[Any, str] = None, ): if link is None: link = dict() self.name = name self.type_ = type_ self.help_ = help_ self.choices = choices self.default = default self.secret = secret self.link = link
[docs] @classmethod def from_raw(cls, raw: dict[str, Any]) -> Question: """Build a new instance from raw. :param raw: A dictionary containing the question attributes. :return: A new ``Question`` instance. """ return cls( name=raw.get("name"), type_=raw.get("type", None), help_=raw.get("help", None), choices=raw.get("choices", None), default=raw.get("default", None), secret=raw.get("secret", False), link=raw.get("link", None), )
[docs] def get_template_uri(self, answer: Any, *args, **kwargs) -> Optional[str]: """Get template uri for the given answer. :param answer: The answer value. :return: The template uri. It can be ``None`` if the given response does not have any associated template. """ template = self.link.get(answer) return self._render_value(template, *args, **kwargs)
[docs] def ask(self, *args, **kwargs) -> str: """Perform the ask. :param args: Additional positional arguments. :param kwargs: Additional named arguments. :return: The obtained answer. """ title = self.render_title(*args, **kwargs) default = self.render_default(*args, **kwargs) choices = self.render_choices(*args, **kwargs) answer = self._ask(f":question: {title}\n", default, choices) console.print() return answer
[docs] def render_title(self, *args, **kwargs) -> str: """Render the title value. :param args: Additional positional arguments. :param kwargs: Additional named arguments. :return: A ``str`` value. """ return self._render_value(self.title, *args, **kwargs)
[docs] def render_default(self, *args, **kwargs) -> Any: """Render the default value. :param args: Additional positional arguments. :param kwargs: Additional named arguments. :return: A ``str`` value. """ if self.default is None: return None if self.choices is None or not isinstance(self.choices, dict): return self._render_value(self.default, *args, **kwargs) for key, value in self.choices.items(): if value == self.default: return str(self._render_value(key, *args, **kwargs)) raise ValueError("The default attribute must match with one of the choices.")
[docs] def render_choices(self, *args, **kwargs) -> Optional[Union[list[Any], dict[Any]]]: """Render the choices value. :param args: Additional positional arguments. :param kwargs: Additional named arguments. :return: A ``str`` value. """ if not self.choices: return self.choices if isinstance(self.choices, dict): rendered = self._render_dict(self.choices, *args, **kwargs) rendered = {str(k): v for k, v in rendered.items()} else: rendered = self._render_list(self.choices, *args, **kwargs) rendered = [str(v) for v in rendered] return rendered
def _render_dict(self, data: dict[str, Any], *args, **kwargs) -> dict[str, Any]: return {self._render_value(k, *args, **kwargs): self._render_value(v, *args, **kwargs) for k, v in data.items()} def _render_list(self, data: list[str], *args, **kwargs) -> list[Any]: return [self._render_value(value, *args, **kwargs) for value in data] # noinspection PyUnusedLocal @staticmethod def _render_value( value: Any, env: Optional[Environment] = None, context: Optional[dict[str, Any]] = None, **kwargs ) -> Any: if env and context: with suppress(TypeError): value = env.from_string(value).render(**context) return value def _ask(self, title: str, default: Any, choices: Union[list[Any], dict[str, Any]]) -> Any: answer = self._ask_fn(title, default=default, choices=choices) if choices is not None and isinstance(choices, dict): answer = choices[answer] if isinstance(answer, str): answer = answer.strip() return answer @property def _ask_fn(self) -> Callable: if self.type_ == "int": fn = IntPrompt.ask elif self.type_ == "float": fn = FloatPrompt.ask elif self.type_ == "bool": fn = Confirm.ask else: fn = Prompt.ask return partial(fn, console=console, password=self.secret) @property def title(self) -> str: """Get the title text to be shown during the asking process. :return: A ``str`` value. """ if self.help_ is not None: return self.help_ return self.name def __eq__(self, other: Any) -> bool: return ( isinstance(other, type(self)) and self.name == other.name and self.type_ == other.type_ and self.help_ == other.help_ and self.choices == other.choices and self.default == other.default )