Requesting review from valuable guests with example, fallbacks, hotfixes

This commit is contained in:
MaXFeeD
2025-04-18 16:58:29 +03:00
parent fb017da746
commit 3278abfd20
2 changed files with 121 additions and 13 deletions
+119 -12
View File
@@ -1,5 +1,5 @@
from abc import ABCMeta, abstractmethod
from typing import Any, Callable, Iterable, Optional, Sequence, Union
from typing import Any, Callable, Dict, Iterable, Optional, Sequence, Union
from prompt_toolkit import Application
from prompt_toolkit.buffer import Buffer
@@ -75,6 +75,10 @@ class Feedback(metaclass=ABCMeta):
self._application = self.create_application()
return self._application
def get_result(self) -> Optional[object]:
self.inform_if_already_busy()
return self.result if hasattr(self, "result") else self.fallback
def pre_run(self) -> None:
pass
@@ -84,11 +88,13 @@ class Feedback(metaclass=ABCMeta):
async def request_async(self) -> Any:
self.inform_if_already_busy()
return await self.application.run_async(pre_run=self.pre_run)
self.result = await self.application.run_async(pre_run=self.pre_run)
return self.result
def request(self) -> Any:
self.inform_if_already_busy()
return self.application.run(pre_run=self.pre_run)
self.result = self.application.run(pre_run=self.pre_run)
return self.result
async def request_async_safe(self, prints_abort: bool = True) -> Any:
try:
@@ -122,10 +128,10 @@ class Feedback(metaclass=ABCMeta):
pretty_print(result, style="class:print.answer")
def complete(self, *, result: object = None, print_result: object = None) -> None:
if result is not None:
self.print_result(print_result if print_result else result)
if self.application.is_running:
self.application.exit(result=result)
if result is not None:
self.print_result(print_result if print_result else result)
class Input(Feedback):
def __init__(
@@ -135,11 +141,13 @@ class Input(Feedback):
explanation: AnyFormattedText = None,
default_text: Optional[str] = None,
use_hint_as_fallback: bool = True,
fallback: object = None,
on_input: Optional[Callable[['Input', str], None]] = None,
on_validate: Optional[Callable[['Input', str], Optional[bool]]] = None,
on_accept: Optional[Callable[['Input', str], Optional[bool]]] = None,
):
Feedback.__init__(self, prompt=prompt, fallback=default_text or hint)
fallback = fallback if fallback is not None else (default_text or hint)
Feedback.__init__(self, prompt=prompt, fallback=fallback)
self.control_prompt = prompt
self.hint = hint
self.use_hint_as_fallback = use_hint_as_fallback
@@ -199,7 +207,7 @@ class Confirm(Input):
yes_or_no: AnyFormattedText = " (Y/n)",
no_or_yes: AnyFormattedText = " (N/y)",
):
Input.__init__(self, prompt=prompt, hint="", explanation=explanation)
Input.__init__(self, prompt=prompt, hint="", explanation=explanation, fallback=default_value)
self.control_prompt = merge_formatted_text([prompt, yes_or_no if default_value else no_or_yes])
self.default_value = default_value
self.read_only = True
@@ -228,14 +236,14 @@ class Select(Feedback):
def __init__(
self,
prompt: AnyFormattedText = "What do you like?",
variants: Iterable[Optional[str]] = ["Rides", "Guide", "Rides", "Guide", "Rides", "Guide", "Rides", "Guide", "Rides", "Guide", "Rides", "Guide", "Rides", "Guide", "Both"],
variants: Iterable[Optional[str]] = ["Rides", "Guide", "Both"],
selected_variant: Optional[Union[int, str]] = None,
default_variant: Optional[Union[int, str]] = None,
returns_what: bool = True,
returns_what: bool = False,
explanation: AnyFormattedText = None,
on_accept: Optional[Callable[['Select', str], Optional[bool]]] = None,
):
Feedback.__init__(self, prompt=prompt)
Feedback.__init__(self, prompt=prompt, fallback=default_variant)
self.variants = variants
self.selected_variant = selected_variant
self.default_variant = default_variant
@@ -308,7 +316,106 @@ class Select(Feedback):
return bindings
# REVIEW
class Review:
def __init__(self, **feedback: Union[Optional[Feedback], Callable[['Review'], Optional[Feedback]]]) -> None:
self.current_feedback = None
self.feedback_keys = [entry for entry in feedback]
self.feedback_callables = {
entry: feedback[entry] for entry in feedback
}
def request_feedback(self) -> Optional[Feedback]:
if not hasattr(self, "current_offset"):
raise ValueError("Review#request_feedback: Review.current_offset")
if hasattr(self, "current_feedback") and self.current_feedback is not None:
self.current_feedback.inform_if_already_busy()
self.current_feedback = None
self.current_key = None
while self.current_feedback is None:
self.current_offset += 1
if len(self.feedback_keys) <= self.current_offset:
break
feedback_key = self.feedback_keys[self.current_offset]
if not feedback_key or not feedback_key in self.feedback_callables:
continue
feedback = self.feedback_callables[feedback_key]
if callable(feedback):
feedback = feedback(self)
if feedback is not None:
self.current_key = feedback_key
self.current_feedback = feedback
return self.current_feedback
def receive_feedback(self, returns_empty_properties: bool = False) -> Optional[object]:
if not hasattr(self, "current_feedback") or self.current_feedback is None or \
not hasattr(self, "current_key") or self.current_key is None:
raise ValueError("Review#receive_feedback: Review.current_feedback or Review.current_key")
if not hasattr(self, "results") or self.results is None:
raise ValueError("Review#receive_feedback: Review.results")
self.current_feedback.inform_if_already_busy()
result = self.current_feedback.get_result()
if result is not None or returns_empty_properties:
self.results[self.current_key] = result
return result
def request(self, returns_empty_properties: bool = False) -> Dict[str, Any]:
assert not self.current_feedback
self.results = {}
self.current_offset = -1
while True:
feedback = self.request_feedback()
if not feedback:
break
feedback.request()
self.receive_feedback(returns_empty_properties=returns_empty_properties)
self.current_feedback = None
return self.results
async def request_async(self, returns_empty_properties: bool = False) -> Any:
assert not self.current_feedback
self.results = {}
self.current_offset = -1
while True:
feedback = self.request_feedback()
if not feedback:
break
await feedback.request_async()
self.receive_feedback(returns_empty_properties=returns_empty_properties)
self.current_feedback = None
return self.results
def request_safe(self, prints_abort: bool = True, returns_empty_properties: bool = False) -> Optional[Dict[str, Any]]:
try:
return self.request(returns_empty_properties=returns_empty_properties)
except (KeyboardInterrupt, EOFError):
if prints_abort:
pretty_print_attention("Abort.")
async def request_async_safe(self, prints_abort: bool = True, returns_empty_properties: bool = False) -> Optional[Dict[str, Any]]:
try:
return await self.request_async(returns_empty_properties=returns_empty_properties)
except (KeyboardInterrupt, EOFError):
if prints_abort:
pretty_print_attention("Abort.")
if __name__ == "__main__":
Select(selected_variant="Both", default_variant="Guide").request_safe()
from random import randint
templates_available = randint(0, 1) == 0
results = Review(
template=lambda review: Select(
"Which template should be used?",
variants=["Mod Template (../toolchain-mod-template)", "Modding Tools Template (../template-assistant)"],
explanation="Templates are used to provide initial project description, create initial files, and more."
) if templates_available else None,
name=Input("Decide a name for your project:", hint="Template Mod"),
author=Input("Author who crafted this creation:", hint="Reider746"),
version=Input("What version a project starts from:", hint="1.0"),
description=Input("Describe this masterpiece in one sentence:"),
client_side=Confirm("Is it a client mod that not requires server?", default_value=False)
).request_safe()
print(results)
+2 -1
View File
@@ -697,8 +697,9 @@ def pretty_print(*values: object, style: str = "", sep: Optional[str] = " ", end
if print_something and sep is not None and len(sep) > 0:
print_formatted_text(to_formatted_text(sep, style=style), sep="", end="", file=file, flush=flush, style=baked_style, include_default_pygments_style=include_default_pygments_style)
text = to_formatted_text(value, style=style, auto_convert=True) # type: ignore
print_formatted_text(text, end=end if end is not None else "\n", file=file, flush=flush, style=baked_style, include_default_pygments_style=include_default_pygments_style)
print_formatted_text(text, end="", file=file, flush=flush, style=baked_style, include_default_pygments_style=include_default_pygments_style)
print_something = True
print_formatted_text(end if end is not None else "\n", end="", file=file, flush=flush, style=baked_style, include_default_pygments_style=include_default_pygments_style)
def debug(*values: object, sep: Optional[str] = " ", end: Optional[str] = "\n", file: Optional[Any] = None, flush: bool = False, include_default_pygments_style: bool = False) -> None:
pretty_print(*values, sep=sep, end=end, file=file, flush=flush, style="class:print.debug", include_default_pygments_style=include_default_pygments_style)