diff --git a/pynecone/compiler/compiler.py b/pynecone/compiler/compiler.py index a8ff4dc15..b4ea03672 100644 --- a/pynecone/compiler/compiler.py +++ b/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"}, diff --git a/pynecone/compiler/utils.py b/pynecone/compiler/utils.py index de723d859..7623bcc0c 100644 --- a/pynecone/compiler/utils.py +++ b/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() - ] + # Render the component. + render = component.get_component() - # Compile the component. - render = component.component_fn(*props) + # 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, ) diff --git a/pynecone/components/component.py b/pynecone/components/component.py index 8dea8294f..3797a2176 100644 --- a/pynecone/components/component.py +++ b/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] diff --git a/pynecone/utils.py b/pynecone/utils.py index 47e722b1b..2b6c09995 100644 --- a/pynecone/utils.py +++ b/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 diff --git a/pynecone/var.py b/pynecone/var.py index 263f91f9c..7ea673018 100644 --- a/pynecone/var.py +++ b/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}]", diff --git a/tests/components/test_component.py b/tests/components/test_component.py index 069418bb4..aba4fcbff 100644 --- a/tests/components/test_component.py +++ b/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}