瀏覽代碼

remove all runtime asserts (#4019)

* remove all runtime asserts

* Update reflex/testing.py

Co-authored-by: Masen Furer <m_github@0x26.net>

---------

Co-authored-by: Masen Furer <m_github@0x26.net>
Thomas Brandého 7 月之前
父節點
當前提交
23e979717f

+ 9 - 5
reflex/app.py

@@ -482,9 +482,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
         """
         # If the route is not set, get it from the callable.
         if route is None:
-            assert isinstance(
-                component, Callable
-            ), "Route must be set if component is not a callable."
+            if not isinstance(component, Callable):
+                raise ValueError("Route must be set if component is not a callable.")
             # Format the route.
             route = format.format_route(component.__name__)
         else:
@@ -1528,6 +1527,9 @@ class EventNamespace(AsyncNamespace):
     async def on_event(self, sid, data):
         """Event for receiving front-end websocket events.
 
+        Raises:
+            RuntimeError: If the Socket.IO is badly initialized.
+
         Args:
             sid: The Socket.IO session id.
             data: The event data.
@@ -1540,9 +1542,11 @@ class EventNamespace(AsyncNamespace):
         self.sid_to_token[sid] = event.token
 
         # Get the event environment.
-        assert self.app.sio is not None
+        if self.app.sio is None:
+            raise RuntimeError("Socket.IO is not initialized.")
         environ = self.app.sio.get_environ(sid, self.namespace)
-        assert environ is not None
+        if environ is None:
+            raise RuntimeError("Socket.IO environ is not initialized.")
 
         # Get the client headers.
         headers = {

+ 12 - 3
reflex/compiler/utils.py

@@ -44,6 +44,9 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
     Args:
         fields: The set of fields to import from the library.
 
+    Raises:
+        ValueError: If there is more than one default import.
+
     Returns:
         The libraries for default and rest.
         default: default library. When install "import def from library".
@@ -54,7 +57,8 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
 
     # Check for default imports.
     defaults = {field for field in fields_set if field.is_default}
-    assert len(defaults) < 2
+    if len(defaults) >= 2:
+        raise ValueError("Only one default import is allowed.")
 
     # Get the default import, and the specific imports.
     default = next(iter({field.name for field in defaults}), "")
@@ -92,6 +96,9 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
     Args:
         import_dict: The import dict to compile.
 
+    Raises:
+        ValueError: If an import in the dict is invalid.
+
     Returns:
         The list of import dict.
     """
@@ -106,8 +113,10 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
             continue
 
         if not lib:
-            assert not default, "No default field allowed for empty library."
-            assert rest is not None and len(rest) > 0, "No fields to import."
+            if default:
+                raise ValueError("No default field allowed for empty library.")
+            if rest is None or len(rest) == 0:
+                raise ValueError("No fields to import.")
             for module in sorted(rest):
                 import_dicts.append(get_import_dict(module))
             continue

+ 5 - 3
reflex/components/base/meta.py

@@ -16,13 +16,15 @@ class Title(Component):
     def render(self) -> dict:
         """Render the title component.
 
+        Raises:
+            ValueError: If the title is not a single string.
+
         Returns:
             The rendered title component.
         """
         # Make sure the title is a single string.
-        assert len(self.children) == 1 and isinstance(
-            self.children[0], Bare
-        ), "Title must be a single string."
+        if len(self.children) != 1 or not isinstance(self.children[0], Bare):
+            raise ValueError("Title must be a single string.")
         return super().render()
 
 

+ 5 - 1
reflex/components/component.py

@@ -1744,10 +1744,14 @@ class CustomComponent(Component):
         Args:
             seen: The tags of the components that have already been seen.
 
+        Raises:
+            ValueError: If the tag is not set.
+
         Returns:
             The set of custom components.
         """
-        assert self.tag is not None, "The tag must be set."
+        if self.tag is None:
+            raise ValueError("The tag must be set.")
 
         # Store the seen components in a set to avoid infinite recursion.
         if seen is None:

+ 4 - 4
reflex/components/core/cond.py

@@ -138,13 +138,13 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
     """
     # Convert the condition to a Var.
     cond_var = LiteralVar.create(condition)
-    assert cond_var is not None, "The condition must be set."
+    if cond_var is None:
+        raise ValueError("The condition must be set.")
 
     # If the first component is a component, create a Cond component.
     if isinstance(c1, BaseComponent):
-        assert c2 is None or isinstance(
-            c2, BaseComponent
-        ), "Both arguments must be components."
+        if c2 is not None and not isinstance(c2, BaseComponent):
+            raise ValueError("Both arguments must be components.")
         return Cond.create(cond_var, c1, c2)
 
     # Otherwise, create a conditional Var.

+ 2 - 1
reflex/components/gridjs/datatable.py

@@ -124,7 +124,8 @@ class DataTable(Gridjs):
         if types.is_dataframe(type(self.data)):
             # If given a pandas df break up the data and columns
             data = serialize(self.data)
-            assert isinstance(data, dict), "Serialized dataframe should be a dict."
+            if not isinstance(data, dict):
+                raise ValueError("Serialized dataframe should be a dict.")
             self.columns = LiteralVar.create(data["columns"])
             self.data = LiteralVar.create(data["data"])
 

+ 7 - 3
reflex/components/markdown/markdown.py

@@ -95,12 +95,16 @@ class Markdown(Component):
             *children: The children of the component.
             **props: The properties of the component.
 
+        Raises:
+            ValueError: If the children are not valid.
+
         Returns:
             The markdown component.
         """
-        assert (
-            len(children) == 1 and types._isinstance(children[0], Union[str, Var])
-        ), "Markdown component must have exactly one child containing the markdown source."
+        if len(children) != 1 or not types._isinstance(children[0], Union[str, Var]):
+            raise ValueError(
+                "Markdown component must have exactly one child containing the markdown source."
+            )
 
         # Update the base component map with the custom component map.
         component_map = {**get_base_component_map(), **props.pop("component_map", {})}

+ 3 - 0
reflex/components/markdown/markdown.pyi

@@ -93,6 +93,9 @@ class Markdown(Component):
             custom_attrs: custom attribute
             **props: The properties of the component.
 
+        Raises:
+            ValueError: If the children are not valid.
+
         Returns:
             The markdown component.
         """

+ 5 - 1
reflex/components/tags/iter_tag.py

@@ -114,6 +114,9 @@ class IterTag(Tag):
     def render_component(self) -> Component:
         """Render the component.
 
+        Raises:
+            ValueError: If the render function takes more than 2 arguments.
+
         Returns:
             The rendered component.
         """
@@ -132,7 +135,8 @@ class IterTag(Tag):
             component = self.render_fn(arg)
         else:
             # If the render function takes the index as an argument.
-            assert len(args) == 2
+            if len(args) != 2:
+                raise ValueError("The render function must take 2 arguments.")
             component = self.render_fn(arg, index)
 
         # Nested foreach components or cond must be wrapped in fragments.

+ 5 - 1
reflex/event.py

@@ -1062,6 +1062,9 @@ def fix_events(
         token: The user token.
         router_data: The optional router data to set in the event.
 
+    Raises:
+        ValueError: If the event type is not what was expected.
+
     Returns:
         The fixed events.
     """
@@ -1085,7 +1088,8 @@ def fix_events(
         # Otherwise, create an event from the event spec.
         if isinstance(e, EventHandler):
             e = e()
-        assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
+        if not isinstance(e, EventSpec):
+            raise ValueError(f"Unexpected event type, {type(e)}.")
         name = format.format_event_handler(e.handler)
         payload = {k._js_expr: v._decode() for k, v in e.args}  # type: ignore
 

+ 3 - 1
reflex/experimental/assets.py

@@ -24,6 +24,7 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
 
     Raises:
         FileNotFoundError: If the file does not exist.
+        ValueError: If the module is None.
 
     Returns:
         The relative URL to the copied asset.
@@ -31,7 +32,8 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
     # Determine the file by which the asset is exposed.
     calling_file = inspect.stack()[1].filename
     module = inspect.getmodule(inspect.stack()[1][0])
-    assert module is not None
+    if module is None:
+        raise ValueError("Module is None")
     caller_module_path = module.__name__.replace(".", "/")
 
     subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path

+ 5 - 1
reflex/experimental/client_state.py

@@ -91,12 +91,16 @@ class ClientStateVar(Var):
             default: The default value of the variable.
             global_ref: Whether the state should be accessible in any Component and on the backend.
 
+        Raises:
+            ValueError: If the var_name is not a string.
+
         Returns:
             ClientStateVar
         """
         if var_name is None:
             var_name = get_unique_variable_name()
-        assert isinstance(var_name, str), "var_name must be a string."
+        if not isinstance(var_name, str):
+            raise ValueError("var_name must be a string.")
         if default is NoValue:
             default_var = Var(_js_expr="")
         elif not isinstance(default, Var):

+ 5 - 3
reflex/experimental/misc.py

@@ -12,10 +12,12 @@ async def run_in_thread(func) -> Any:
     Args:
         func (callable): The non-async function to run.
 
+    Raises:
+        ValueError: If the function is an async function.
+
     Returns:
         Any: The return value of the function.
     """
-    assert not asyncio.coroutines.iscoroutinefunction(
-        func
-    ), "func must be a non-async function"
+    if asyncio.coroutines.iscoroutinefunction(func):
+        raise ValueError("func must be a non-async function")
     return await asyncio.get_event_loop().run_in_executor(None, func)

+ 2 - 1
reflex/reflex.py

@@ -230,7 +230,8 @@ def _run(
             exec.run_frontend_prod,
             exec.run_backend_prod,
         )
-    assert setup_frontend and frontend_cmd and backend_cmd, "Invalid env"
+    if not setup_frontend or not frontend_cmd or not backend_cmd:
+        raise ValueError("Invalid env")
 
     # Post a telemetry event.
     telemetry.send(f"run-{env.value}")

+ 5 - 3
reflex/state.py

@@ -870,6 +870,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
     def get_parent_state(cls) -> Type[BaseState] | None:
         """Get the parent state.
 
+        Raises:
+            ValueError: If more than one parent state is found.
+
         Returns:
             The parent state.
         """
@@ -878,9 +881,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
             for base in cls.__bases__
             if issubclass(base, BaseState) and base is not BaseState and not base._mixin
         ]
-        assert (
-            len(parent_states) < 2
-        ), f"Only one parent state is allowed {parent_states}."
+        if len(parent_states) >= 2:
+            raise ValueError(f"Only one parent state is allowed {parent_states}.")
         return parent_states[0] if len(parent_states) == 1 else None  # type: ignore
 
     @classmethod

+ 8 - 4
reflex/testing.py

@@ -340,6 +340,9 @@ class AppHarness:
 
         This is necessary when the backend is restarted and the state manager is a
         StateManagerRedis instance.
+
+        Raises:
+            RuntimeError: when the state manager cannot be reset
         """
         if (
             self.app_instance is not None
@@ -354,7 +357,8 @@ class AppHarness:
             self.app_instance._state_manager = StateManagerRedis.create(
                 state=self.app_instance.state,
             )
-            assert isinstance(self.app_instance.state_manager, StateManagerRedis)
+            if not isinstance(self.app_instance.state_manager, StateManagerRedis):
+                raise RuntimeError("Failed to reset state manager.")
 
     def _start_frontend(self):
         # Set up the frontend.
@@ -787,13 +791,13 @@ class AppHarness:
         Raises:
             RuntimeError: when the app hasn't started running
             TimeoutError: when the timeout expires before any states are seen
+            ValueError: when the state_manager is not a memory state manager
         """
         if self.app_instance is None:
             raise RuntimeError("App is not running.")
         state_manager = self.app_instance.state_manager
-        assert isinstance(
-            state_manager, (StateManagerMemory, StateManagerDisk)
-        ), "Only works with memory state manager"
+        if not isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
+            raise ValueError("Only works with memory or disk state manager")
         if not self._poll_for(
             target=lambda: state_manager.states,
             timeout=timeout,

+ 3 - 1
reflex/utils/format.py

@@ -345,6 +345,7 @@ def format_prop(
     Raises:
         exceptions.InvalidStylePropError: If the style prop value is not a valid type.
         TypeError: If the prop is not valid.
+        ValueError: If the prop is not a string.
     """
     # import here to avoid circular import.
     from reflex.event import EventChain
@@ -391,7 +392,8 @@ def format_prop(
         raise TypeError(f"Could not format prop: {prop} of type {type(prop)}") from e
 
     # Wrap the variable in braces.
-    assert isinstance(prop, str), "The prop must be a string."
+    if not isinstance(prop, str):
+        raise ValueError(f"Invalid prop: {prop}. Expected a string.")
     return wrap(prop, "{", check_first=False)