فهرست منبع

Fix component decorator bugs (#203)

Nikhil Rao 2 سال پیش
والد
کامیت
3f27ce3442
6فایلهای تغییر یافته به همراه52 افزوده شده و 35 حذف شده
  1. 0 1
      pynecone/compiler/compiler.py
  2. 14 24
      pynecone/compiler/utils.py
  3. 29 3
      pynecone/components/component.py
  4. 2 2
      pynecone/utils.py
  5. 3 1
      pynecone/var.py
  6. 4 4
      tests/components/test_component.py

+ 0 - 1
pynecone/compiler/compiler.py

@@ -11,7 +11,6 @@ from pynecone.components.component import Component, CustomComponent, ImportDict
 from pynecone.state import State
 from pynecone.style import Style
 
-
 # Imports to be included in every Pynecone app.
 DEFAULT_IMPORTS: ImportDict = {
     "react": {"useEffect", "useRef", "useState"},

+ 14 - 24
pynecone/compiler/utils.py

@@ -1,11 +1,8 @@
 """Common utility functions used in the compiler."""
 
-from __future__ import annotations
-
-import inspect
 import json
 import os
-from typing import TYPE_CHECKING, Any, Dict, List, Set, Type
+from typing import Dict, List, Set, Tuple, Type
 
 from pynecone import constants, utils
 from pynecone.compiler import templates
@@ -21,14 +18,9 @@ from pynecone.components.base import (
     Script,
     Title,
 )
-from pynecone.components.component import ImportDict
+from pynecone.components.component import Component, CustomComponent, ImportDict
 from pynecone.state import State
 from pynecone.style import Style
-from pynecone.var import BaseVar, Var
-
-if TYPE_CHECKING:
-    from pynecone.components.component import Component, CustomComponent
-
 
 # To re-export this function.
 merge_imports = utils.merge_imports
@@ -170,7 +162,7 @@ def compile_render(component: Component) -> str:
     return component.render()
 
 
-def compile_custom_component(component: CustomComponent) -> tuple[str, ImportDict]:
+def compile_custom_component(component: CustomComponent) -> Tuple[str, ImportDict]:
     """Compile a custom component.
 
     Args:
@@ -179,20 +171,18 @@ def compile_custom_component(component: CustomComponent) -> tuple[str, ImportDic
     Returns:
         A tuple of the compiled component and the imports required by the component.
     """
-    props = [
-        BaseVar(
-            name=name,
-            type_=prop.type_ if utils._isinstance(prop, Var) else type(prop),
-            is_local=True,
-        )
-        for name, prop in component.props.items()
-    ]
-
-    # Compile the component.
-    render = component.component_fn(*props)
+    # Render the component.
+    render = component.get_component()
+
+    # Get the imports.
+    imports = {
+        lib: fields
+        for lib, fields in render.get_imports().items()
+        if lib != component.library
+    }
 
     # Concatenate the props.
-    props = ", ".join([prop.name for prop in props])
+    props = ", ".join([prop.name for prop in component.get_prop_vars()])
 
     # Compile the component.
     return (
@@ -201,7 +191,7 @@ def compile_custom_component(component: CustomComponent) -> tuple[str, ImportDic
             props=props,
             render=render,
         ),
-        render.get_imports(),
+        imports,
     )
 
 

+ 29 - 3
pynecone/components/component.py

@@ -18,7 +18,7 @@ from pynecone.event import (
     EventSpec,
 )
 from pynecone.style import Style
-from pynecone.var import Var
+from pynecone.var import BaseVar, Var
 
 ImportDict = Dict[str, Set[str]]
 
@@ -440,7 +440,7 @@ class CustomComponent(Component):
             if utils._issubclass(type_, EventChain):
                 value = self._create_event_chain(key, value)
             else:
-                value = Var.create(value)
+                value = Var.create(value, is_string=type(value) is str)
             self.props[utils.to_camel_case(key)] = value
 
     def __eq__(self, other) -> bool:
@@ -477,7 +477,11 @@ class CustomComponent(Component):
         Returns:
             The set of custom components.
         """
-        return {self} | super().get_custom_components()
+        return (
+            {self}
+            | super().get_custom_components()
+            | self.get_component().get_custom_components()
+        )
 
     def _render(self) -> Tag:
         """Define how to render the component in React.
@@ -487,6 +491,28 @@ class CustomComponent(Component):
         """
         return Tag(name=self.tag).add_props(**self.props)
 
+    def get_prop_vars(self) -> List[BaseVar]:
+        """Get the prop vars.
+
+        Returns:
+            The prop vars.
+        """
+        return [
+            BaseVar(
+                name=name,
+                type_=prop.type_ if utils._isinstance(prop, Var) else type(prop),
+            )
+            for name, prop in self.props.items()
+        ]
+
+    def get_component(self) -> Component:
+        """Render the component.
+
+        Returns:
+            The code to render the component.
+        """
+        return self.component_fn(*self.get_prop_vars())
+
 
 def custom_component(
     component_fn: Callable[..., Component]

+ 2 - 2
pynecone/utils.py

@@ -13,8 +13,6 @@ import signal
 import string
 import subprocess
 import sys
-import uvicorn
-from urllib.parse import urlparse
 from collections import defaultdict
 from subprocess import PIPE
 from types import ModuleType
@@ -30,9 +28,11 @@ from typing import (
     Type,
     Union,
 )
+from urllib.parse import urlparse
 
 import plotly.graph_objects as go
 import typer
+import uvicorn
 from plotly.io import to_json
 from redis import Redis
 from rich.console import Console

+ 3 - 1
pynecone/var.py

@@ -161,7 +161,9 @@ class Var(ABC):
             else:
                 type_ = Any
         else:
-            raise TypeError("Var does not support indexing.")
+            raise TypeError(
+                f"Var {self.name} of type {self.type_} does not support indexing."
+            )
 
         return BaseVar(
             name=f"{self.name}[{i}]",

+ 4 - 4
tests/components/test_component.py

@@ -110,7 +110,7 @@ def my_component():
         A test component function.
     """
 
-    def my_component(prop1: str, prop2: int):
+    def my_component(prop1: Var[str], prop2: Var[int]):
         return Box.create(prop1, prop2)
 
     return my_component
@@ -286,7 +286,7 @@ def test_create_custom_component(my_component):
     Args:
         my_component: A test custom component.
     """
-    component = CustomComponent(component_fn=my_component)
+    component = CustomComponent(component_fn=my_component, prop1="test", prop2=1)
     assert component.tag == "MyComponent"
     assert component.get_props() == set()
     assert component.get_custom_components() == {component}
@@ -298,6 +298,6 @@ def test_custom_component_hash(my_component):
     Args:
         my_component: A test custom component.
     """
-    component1 = CustomComponent(component_fn=my_component)
-    component2 = CustomComponent(component_fn=my_component)
+    component1 = CustomComponent(component_fn=my_component, prop1="test", prop2=1)
+    component2 = CustomComponent(component_fn=my_component, prop1="test", prop2=2)
     assert set([component1, component2]) == {component1}