console.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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. # Warnings which have been printed.
  16. _EMIITED_WARNINGS = set()
  17. # Errors which have been printed.
  18. _EMITTED_ERRORS = set()
  19. # Success messages which have been printed.
  20. _EMITTED_SUCCESS = set()
  21. # Debug messages which have been printed.
  22. _EMITTED_DEBUG = set()
  23. # Logs which have been printed.
  24. _EMITTED_LOGS = set()
  25. # Prints which have been printed.
  26. _EMITTED_PRINTS = set()
  27. def set_log_level(log_level: LogLevel):
  28. """Set the log level.
  29. Args:
  30. log_level: The log level to set.
  31. Raises:
  32. ValueError: If the log level is invalid.
  33. """
  34. if not isinstance(log_level, LogLevel):
  35. deprecate(
  36. feature_name="Passing a string to set_log_level",
  37. reason="use reflex.constants.LogLevel enum instead",
  38. deprecation_version="0.6.6",
  39. removal_version="0.7.0",
  40. )
  41. try:
  42. log_level = getattr(LogLevel, log_level.upper())
  43. except AttributeError as ae:
  44. raise ValueError(f"Invalid log level: {log_level}") from ae
  45. global _LOG_LEVEL
  46. _LOG_LEVEL = log_level
  47. def is_debug() -> bool:
  48. """Check if the log level is debug.
  49. Returns:
  50. True if the log level is debug.
  51. """
  52. return _LOG_LEVEL <= LogLevel.DEBUG
  53. def print(msg: str, dedupe: bool = False, **kwargs):
  54. """Print a message.
  55. Args:
  56. msg: The message to print.
  57. dedupe: If True, suppress multiple console logs of print message.
  58. kwargs: Keyword arguments to pass to the print function.
  59. """
  60. if dedupe:
  61. if msg in _EMITTED_PRINTS:
  62. return
  63. else:
  64. _EMITTED_PRINTS.add(msg)
  65. _console.print(msg, **kwargs)
  66. def debug(msg: str, dedupe: bool = False, **kwargs):
  67. """Print a debug message.
  68. Args:
  69. msg: The debug message.
  70. dedupe: If True, suppress multiple console logs of debug message.
  71. kwargs: Keyword arguments to pass to the print function.
  72. """
  73. if is_debug():
  74. msg_ = f"[purple]Debug: {msg}[/purple]"
  75. if dedupe:
  76. if msg_ in _EMITTED_DEBUG:
  77. return
  78. else:
  79. _EMITTED_DEBUG.add(msg_)
  80. if progress := kwargs.pop("progress", None):
  81. progress.console.print(msg_, **kwargs)
  82. else:
  83. print(msg_, **kwargs)
  84. def info(msg: str, dedupe: bool = False, **kwargs):
  85. """Print an info message.
  86. Args:
  87. msg: The info message.
  88. dedupe: If True, suppress multiple console logs of info message.
  89. kwargs: Keyword arguments to pass to the print function.
  90. """
  91. if _LOG_LEVEL <= LogLevel.INFO:
  92. if dedupe:
  93. if msg in _EMITTED_INFO:
  94. return
  95. else:
  96. _EMITTED_INFO.add(msg)
  97. print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
  98. def success(msg: str, dedupe: bool = False, **kwargs):
  99. """Print a success message.
  100. Args:
  101. msg: The success message.
  102. dedupe: If True, suppress multiple console logs of success message.
  103. kwargs: Keyword arguments to pass to the print function.
  104. """
  105. if _LOG_LEVEL <= LogLevel.INFO:
  106. if dedupe:
  107. if msg in _EMITTED_SUCCESS:
  108. return
  109. else:
  110. _EMITTED_SUCCESS.add(msg)
  111. print(f"[green]Success: {msg}[/green]", **kwargs)
  112. def log(msg: str, dedupe: bool = False, **kwargs):
  113. """Takes a string and logs it to the console.
  114. Args:
  115. msg: The message to log.
  116. dedupe: If True, suppress multiple console logs of log message.
  117. kwargs: Keyword arguments to pass to the print function.
  118. """
  119. if _LOG_LEVEL <= LogLevel.INFO:
  120. if dedupe:
  121. if msg in _EMITTED_LOGS:
  122. return
  123. else:
  124. _EMITTED_LOGS.add(msg)
  125. _console.log(msg, **kwargs)
  126. def rule(title: str, **kwargs):
  127. """Prints a horizontal rule with a title.
  128. Args:
  129. title: The title of the rule.
  130. kwargs: Keyword arguments to pass to the print function.
  131. """
  132. _console.rule(title, **kwargs)
  133. def warn(msg: str, dedupe: bool = False, **kwargs):
  134. """Print a warning message.
  135. Args:
  136. msg: The warning message.
  137. dedupe: If True, suppress multiple console logs of warning message.
  138. kwargs: Keyword arguments to pass to the print function.
  139. """
  140. if _LOG_LEVEL <= LogLevel.WARNING:
  141. if dedupe:
  142. if msg in _EMIITED_WARNINGS:
  143. return
  144. else:
  145. _EMIITED_WARNINGS.add(msg)
  146. print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
  147. def deprecate(
  148. feature_name: str,
  149. reason: str,
  150. deprecation_version: str,
  151. removal_version: str,
  152. dedupe: bool = True,
  153. **kwargs,
  154. ):
  155. """Print a deprecation warning.
  156. Args:
  157. feature_name: The feature to deprecate.
  158. reason: The reason for deprecation.
  159. deprecation_version: The version the feature was deprecated
  160. removal_version: The version the deprecated feature will be removed
  161. dedupe: If True, suppress multiple console logs of deprecation message.
  162. kwargs: Keyword arguments to pass to the print function.
  163. """
  164. if feature_name not in _EMITTED_DEPRECATION_WARNINGS:
  165. msg = (
  166. f"{feature_name} has been deprecated in version {deprecation_version} {reason.rstrip('.')}. It will be completely "
  167. f"removed in {removal_version}"
  168. )
  169. if _LOG_LEVEL <= LogLevel.WARNING:
  170. print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
  171. if dedupe:
  172. _EMITTED_DEPRECATION_WARNINGS.add(feature_name)
  173. def error(msg: str, dedupe: bool = False, **kwargs):
  174. """Print an error message.
  175. Args:
  176. msg: The error message.
  177. dedupe: If True, suppress multiple console logs of error message.
  178. kwargs: Keyword arguments to pass to the print function.
  179. """
  180. if _LOG_LEVEL <= LogLevel.ERROR:
  181. if dedupe:
  182. if msg in _EMITTED_ERRORS:
  183. return
  184. else:
  185. _EMITTED_ERRORS.add(msg)
  186. print(f"[red]{msg}[/red]", **kwargs)
  187. def ask(
  188. question: str,
  189. choices: list[str] | None = None,
  190. default: str | None = None,
  191. show_choices: bool = True,
  192. ) -> str:
  193. """Takes a prompt question and optionally a list of choices
  194. and returns the user input.
  195. Args:
  196. question: The question to ask the user.
  197. choices: A list of choices to select from.
  198. default: The default option selected.
  199. show_choices: Whether to show the choices.
  200. Returns:
  201. A string with the user input.
  202. """
  203. return Prompt.ask(
  204. question, choices=choices, default=default, show_choices=show_choices
  205. ) # type: ignore
  206. def progress():
  207. """Create a new progress bar.
  208. Returns:
  209. A new progress bar.
  210. """
  211. return Progress(
  212. *Progress.get_default_columns()[:-1],
  213. MofNCompleteColumn(),
  214. TimeElapsedColumn(),
  215. )
  216. def status(*args, **kwargs):
  217. """Create a status with a spinner.
  218. Args:
  219. *args: Args to pass to the status.
  220. **kwargs: Kwargs to pass to the status.
  221. Returns:
  222. A new status.
  223. """
  224. return _console.status(*args, **kwargs)