bare.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. """A bare component."""
  2. from __future__ import annotations
  3. from collections.abc import Iterator, Sequence
  4. from typing import Any
  5. from reflex.components.component import BaseComponent, Component, ComponentStyle
  6. from reflex.components.tags import Tag
  7. from reflex.components.tags.tagless import Tagless
  8. from reflex.config import PerformanceMode, environment
  9. from reflex.utils import console
  10. from reflex.utils.decorator import once
  11. from reflex.utils.imports import ParsedImportDict
  12. from reflex.vars import BooleanVar, ObjectVar, Var
  13. from reflex.vars.base import GLOBAL_CACHE, VarData
  14. from reflex.vars.sequence import LiteralStringVar
  15. @once
  16. def get_performance_mode():
  17. """Get the performance mode.
  18. Returns:
  19. The performance mode.
  20. """
  21. return environment.REFLEX_PERF_MODE.get()
  22. def validate_str(value: str):
  23. """Validate a string value.
  24. Args:
  25. value: The value to validate.
  26. Raises:
  27. ValueError: If the value is a Var and the performance mode is set to raise.
  28. """
  29. perf_mode = get_performance_mode()
  30. if perf_mode != PerformanceMode.OFF and value.startswith("reflex___state"):
  31. if perf_mode == PerformanceMode.WARN:
  32. console.warn(
  33. f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead."
  34. )
  35. elif perf_mode == PerformanceMode.RAISE:
  36. raise ValueError(
  37. f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead."
  38. )
  39. def _components_from_var(var: Var) -> Sequence[BaseComponent]:
  40. var_data = var._get_all_var_data()
  41. return var_data.components if var_data else ()
  42. class Bare(Component):
  43. """A component with no tag."""
  44. contents: Var[Any]
  45. @classmethod
  46. def create(cls, contents: Any) -> Component:
  47. """Create a Bare component, with no tag.
  48. Args:
  49. contents: The contents of the component.
  50. Returns:
  51. The component.
  52. """
  53. if isinstance(contents, Var):
  54. if isinstance(contents, LiteralStringVar):
  55. validate_str(contents._var_value)
  56. return cls._unsafe_create(children=[], contents=contents)
  57. else:
  58. if isinstance(contents, str):
  59. validate_str(contents)
  60. contents = Var.create(contents if contents is not None else "")
  61. return cls._unsafe_create(children=[], contents=contents)
  62. def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
  63. """Include the hooks for the component.
  64. Returns:
  65. The hooks for the component.
  66. """
  67. hooks = super()._get_all_hooks_internal()
  68. if isinstance(self.contents, Var):
  69. for component in _components_from_var(self.contents):
  70. hooks |= component._get_all_hooks_internal()
  71. return hooks
  72. def _get_all_hooks(self) -> dict[str, VarData | None]:
  73. """Include the hooks for the component.
  74. Returns:
  75. The hooks for the component.
  76. """
  77. hooks = super()._get_all_hooks()
  78. if isinstance(self.contents, Var):
  79. for component in _components_from_var(self.contents):
  80. hooks |= component._get_all_hooks()
  81. return hooks
  82. def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict:
  83. """Include the imports for the component.
  84. Args:
  85. collapse: Whether to collapse the imports.
  86. Returns:
  87. The imports for the component.
  88. """
  89. imports = super()._get_all_imports(collapse=collapse)
  90. if isinstance(self.contents, Var):
  91. var_data = self.contents._get_all_var_data()
  92. if var_data:
  93. imports |= {k: list(v) for k, v in var_data.imports}
  94. return imports
  95. def _get_all_dynamic_imports(self) -> set[str]:
  96. """Get dynamic imports for the component.
  97. Returns:
  98. The dynamic imports.
  99. """
  100. dynamic_imports = super()._get_all_dynamic_imports()
  101. if isinstance(self.contents, Var):
  102. for component in _components_from_var(self.contents):
  103. dynamic_imports |= component._get_all_dynamic_imports()
  104. return dynamic_imports
  105. def _get_all_custom_code(self) -> set[str]:
  106. """Get custom code for the component.
  107. Returns:
  108. The custom code.
  109. """
  110. custom_code = super()._get_all_custom_code()
  111. if isinstance(self.contents, Var):
  112. for component in _components_from_var(self.contents):
  113. custom_code |= component._get_all_custom_code()
  114. return custom_code
  115. def _get_all_app_wrap_components(self) -> dict[tuple[int, str], Component]:
  116. """Get the components that should be wrapped in the app.
  117. Returns:
  118. The components that should be wrapped in the app.
  119. """
  120. app_wrap_components = super()._get_all_app_wrap_components()
  121. if isinstance(self.contents, Var):
  122. for component in _components_from_var(self.contents):
  123. if isinstance(component, Component):
  124. app_wrap_components |= component._get_all_app_wrap_components()
  125. return app_wrap_components
  126. def _get_all_refs(self) -> set[str]:
  127. """Get the refs for the children of the component.
  128. Returns:
  129. The refs for the children.
  130. """
  131. refs = super()._get_all_refs()
  132. if isinstance(self.contents, Var):
  133. for component in _components_from_var(self.contents):
  134. refs |= component._get_all_refs()
  135. return refs
  136. def _render(self) -> Tag:
  137. if isinstance(self.contents, Var):
  138. if isinstance(self.contents, (BooleanVar, ObjectVar)):
  139. return Tagless(contents=f"{{{self.contents.to_string()!s}}}")
  140. return Tagless(contents=f"{{{self.contents!s}}}")
  141. return Tagless(contents=str(self.contents))
  142. def _add_style_recursive(
  143. self, style: ComponentStyle, theme: Component | None = None
  144. ) -> Component:
  145. """Add style to the component and its children.
  146. Args:
  147. style: The style to add.
  148. theme: The theme to add.
  149. Returns:
  150. The component with the style added.
  151. """
  152. new_self = super()._add_style_recursive(style, theme)
  153. are_components_touched = False
  154. if isinstance(self.contents, Var):
  155. for component in _components_from_var(self.contents):
  156. if isinstance(component, Component):
  157. component._add_style_recursive(style, theme)
  158. are_components_touched = True
  159. if are_components_touched:
  160. GLOBAL_CACHE.clear()
  161. return new_self
  162. def _get_vars(
  163. self, include_children: bool = False, ignore_ids: set[int] | None = None
  164. ) -> Iterator[Var]:
  165. """Walk all Vars used in this component.
  166. Args:
  167. include_children: Whether to include Vars from children.
  168. ignore_ids: The ids to ignore.
  169. Yields:
  170. The contents if it is a Var, otherwise nothing.
  171. """
  172. yield self.contents