1
0

console.py 7.7 KB

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