console.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """Functions to communicate to the user via console."""
  2. from __future__ import annotations
  3. from rich.console import Console
  4. from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
  5. from rich.prompt import Prompt
  6. from reflex.constants import LogLevel
  7. # Console for pretty printing.
  8. _console = Console()
  9. # The current log level.
  10. _LOG_LEVEL = LogLevel.INFO
  11. # Deprecated features who's warning has been printed.
  12. _EMITTED_DEPRECATION_WARNINGS = set()
  13. # Info messages which have been printed.
  14. _EMITTED_INFO = set()
  15. def set_log_level(log_level: LogLevel):
  16. """Set the log level.
  17. Args:
  18. log_level: The log level to set.
  19. Raises:
  20. ValueError: If the log level is invalid.
  21. """
  22. if not isinstance(log_level, LogLevel):
  23. deprecate(
  24. feature_name="Passing a string to set_log_level",
  25. reason="use reflex.constants.LogLevel enum instead",
  26. deprecation_version="0.6.6",
  27. removal_version="0.7.0",
  28. )
  29. try:
  30. log_level = getattr(LogLevel, log_level.upper())
  31. except AttributeError as ae:
  32. raise ValueError(f"Invalid log level: {log_level}") from ae
  33. global _LOG_LEVEL
  34. _LOG_LEVEL = log_level
  35. def is_debug() -> bool:
  36. """Check if the log level is debug.
  37. Returns:
  38. True if the log level is debug.
  39. """
  40. return _LOG_LEVEL <= LogLevel.DEBUG
  41. def print(msg: str, **kwargs):
  42. """Print a message.
  43. Args:
  44. msg: The message to print.
  45. kwargs: Keyword arguments to pass to the print function.
  46. """
  47. _console.print(msg, **kwargs)
  48. def debug(msg: str, **kwargs):
  49. """Print a debug message.
  50. Args:
  51. msg: The debug message.
  52. kwargs: Keyword arguments to pass to the print function.
  53. """
  54. if is_debug():
  55. msg_ = f"[purple]Debug: {msg}[/purple]"
  56. if progress := kwargs.pop("progress", None):
  57. progress.console.print(msg_, **kwargs)
  58. else:
  59. print(msg_, **kwargs)
  60. def info(msg: str, dedupe: bool = False, **kwargs):
  61. """Print an info message.
  62. Args:
  63. msg: The info message.
  64. dedupe: If True, suppress multiple console logs of info message.
  65. kwargs: Keyword arguments to pass to the print function.
  66. """
  67. if _LOG_LEVEL <= LogLevel.INFO:
  68. if dedupe:
  69. if msg in _EMITTED_INFO:
  70. return
  71. else:
  72. _EMITTED_INFO.add(msg)
  73. print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
  74. def success(msg: str, **kwargs):
  75. """Print a success message.
  76. Args:
  77. msg: The success message.
  78. kwargs: Keyword arguments to pass to the print function.
  79. """
  80. if _LOG_LEVEL <= LogLevel.INFO:
  81. print(f"[green]Success: {msg}[/green]", **kwargs)
  82. def log(msg: str, **kwargs):
  83. """Takes a string and logs it to the console.
  84. Args:
  85. msg: The message to log.
  86. kwargs: Keyword arguments to pass to the print function.
  87. """
  88. if _LOG_LEVEL <= LogLevel.INFO:
  89. _console.log(msg, **kwargs)
  90. def rule(title: str, **kwargs):
  91. """Prints a horizontal rule with a title.
  92. Args:
  93. title: The title of the rule.
  94. kwargs: Keyword arguments to pass to the print function.
  95. """
  96. _console.rule(title, **kwargs)
  97. def warn(msg: str, **kwargs):
  98. """Print a warning message.
  99. Args:
  100. msg: The warning message.
  101. kwargs: Keyword arguments to pass to the print function.
  102. """
  103. if _LOG_LEVEL <= LogLevel.WARNING:
  104. print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
  105. def deprecate(
  106. feature_name: str,
  107. reason: str,
  108. deprecation_version: str,
  109. removal_version: str,
  110. dedupe: bool = True,
  111. **kwargs,
  112. ):
  113. """Print a deprecation warning.
  114. Args:
  115. feature_name: The feature to deprecate.
  116. reason: The reason for deprecation.
  117. deprecation_version: The version the feature was deprecated
  118. removal_version: The version the deprecated feature will be removed
  119. dedupe: If True, suppress multiple console logs of deprecation message.
  120. kwargs: Keyword arguments to pass to the print function.
  121. """
  122. if feature_name not in _EMITTED_DEPRECATION_WARNINGS:
  123. msg = (
  124. f"{feature_name} has been deprecated in version {deprecation_version} {reason.rstrip('.')}. It will be completely "
  125. f"removed in {removal_version}"
  126. )
  127. if _LOG_LEVEL <= LogLevel.WARNING:
  128. print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
  129. if dedupe:
  130. _EMITTED_DEPRECATION_WARNINGS.add(feature_name)
  131. def error(msg: str, **kwargs):
  132. """Print an error message.
  133. Args:
  134. msg: The error message.
  135. kwargs: Keyword arguments to pass to the print function.
  136. """
  137. if _LOG_LEVEL <= LogLevel.ERROR:
  138. print(f"[red]{msg}[/red]", **kwargs)
  139. def ask(
  140. question: str,
  141. choices: list[str] | None = None,
  142. default: str | None = None,
  143. show_choices: bool = True,
  144. ) -> str:
  145. """Takes a prompt question and optionally a list of choices
  146. and returns the user input.
  147. Args:
  148. question: The question to ask the user.
  149. choices: A list of choices to select from.
  150. default: The default option selected.
  151. show_choices: Whether to show the choices.
  152. Returns:
  153. A string with the user input.
  154. """
  155. return Prompt.ask(
  156. question, choices=choices, default=default, show_choices=show_choices
  157. ) # type: ignore
  158. def progress():
  159. """Create a new progress bar.
  160. Returns:
  161. A new progress bar.
  162. """
  163. return Progress(
  164. *Progress.get_default_columns()[:-1],
  165. MofNCompleteColumn(),
  166. TimeElapsedColumn(),
  167. )
  168. def status(*args, **kwargs):
  169. """Create a status with a spinner.
  170. Args:
  171. *args: Args to pass to the status.
  172. **kwargs: Kwargs to pass to the status.
  173. Returns:
  174. A new status.
  175. """
  176. return _console.status(*args, **kwargs)