Add is_dataframe check.

This commit is contained in:
Nikhil Rao 2022-11-18 17:09:19 -08:00
parent 2008417bd7
commit 03d0aca366
8 changed files with 110 additions and 47 deletions

2
.gitignore vendored
View File

@ -7,4 +7,4 @@
bun.lockb
poetry.lock
dist/*
pynetree/
examples/

View File

@ -196,7 +196,7 @@ class App(Base):
v = BaseVar(
name=match.groups()[0],
type_=str,
state="router.query",
state=f"{constants.ROUTER}.query",
)
args.append(v)

View File

@ -224,7 +224,7 @@ class Component(Base, ABC):
# Add component props to the tag.
props = {attr: getattr(self, attr) for attr in self.get_props()}
# Special case for props named `type_`.
if hasattr(self, "type_"):
props["type"] = getattr(self, "type_")

View File

@ -1,7 +1,8 @@
"""Table components."""
from typing import Any, Dict, List, Union
from typing import Any, List
from pynecone import utils
from pynecone.components.component import Component
from pynecone.components.tags import Tag
from pynecone.var import Var
@ -44,7 +45,7 @@ import "gridjs/dist/theme/mermaid.css";
"""
def _render(self) -> Tag:
if type(self.data).__name__ == "DataFrame":
if utils.is_dataframe(type(self.data)):
self.columns = Var.create(list(self.data.columns.values.tolist())) # type: ignore
self.data = Var.create(list(self.data.values.tolist())) # type: ignore

View File

@ -94,6 +94,8 @@ CONFIG_MODULE = "pcconfig"
CONFIG_FILE = f"{CONFIG_MODULE}.{PY_EXT}"
# The deployment URL.
PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
# Token expiration time in seconds.
TOKEN_EXPIRATION = 60 * 60
# Env modes

View File

@ -8,7 +8,7 @@ import traceback
from abc import ABC
from typing import Any, Callable, ClassVar, Dict, List, Optional, Sequence, Set, Type
from pynecone import utils
from pynecone import constants, utils
from pynecone.base import Base
from pynecone.event import Event, EventHandler, EventSpec, window_alert
from pynecone.var import BaseVar, ComputedVar, Var
@ -320,35 +320,6 @@ class State(Base, ABC):
Returns:
The state update after processing the event.
"""
def fix_events(events):
if events is None:
return []
if not isinstance(events, List):
events = [events]
out = []
for e in events:
if isinstance(e, Event):
out.append(
Event(
token=event.token,
name=e.name,
payload=e.payload,
)
)
else:
if isinstance(e, EventHandler):
e = e()
assert isinstance(e, EventSpec)
out.append(
Event(
token=event.token,
name=utils.to_snake_case(e.handler.fn.__qualname__),
payload=dict(e.args),
)
)
return out
# Get the event handler.
path = event.name.split(".")
path, name = path[:-1], path[-1]
@ -367,10 +338,16 @@ class State(Base, ABC):
print(error)
return StateUpdate(events=[window_alert(error)])
# Return the substate and the delta.
events = fix_events(events)
# Fix the returned events.
events = utils.fix_events(events, event.token)
# Get the delta after processing the event.
delta = self.get_delta()
# Reset the dirty vars.
self.clean()
# Return the state update.
return StateUpdate(delta=delta, events=events)
def get_delta(self) -> Delta:
@ -379,15 +356,21 @@ class State(Base, ABC):
Returns:
The delta for the state.
"""
# Return the dirty vars, as well as all computed vars.
delta = {
self.get_full_name(): {
prop: getattr(self, prop)
for prop in self.dirty_vars | set(self.computed_vars.keys())
}
}
# Recursively find the substate deltas.
for substate in self.dirty_substates:
delta.update(self.substates[substate].get_delta())
# Format the delta.
delta = utils.format_state(delta)
# Return the delta.
return delta
def mark_dirty(self):
@ -398,8 +381,11 @@ class State(Base, ABC):
def clean(self):
"""Reset the dirty vars."""
# Recursively clean the substates.
for substate in self.dirty_substates:
self.substates[substate].clean()
# Clean this state.
self.dirty_vars = set()
self.dirty_substates = set()
@ -463,7 +449,7 @@ class StateManager(Base):
states: Dict[str, State] = {}
# The token expiration time (s).
token_expiration: int = 60 * 60
token_expiration: int = constants.TOKEN_EXPIRATION
def __init__(self, *args, **kwargs):
"""Initialize the state manager.

View File

@ -36,7 +36,7 @@ from pynecone import constants
if TYPE_CHECKING:
from pynecone.components.component import ImportDict
from pynecone.event import EventHandler, EventSpec
from pynecone.event import Event, EventHandler, EventSpec
from pynecone.var import Var
@ -621,6 +621,18 @@ def get_default_app_name() -> str:
return os.getcwd().split(os.path.sep)[-1].replace("-", "_")
def is_dataframe(value: Type) -> bool:
"""Check if the given value is a dataframe.
Args:
value: The value to check.
Returns:
Whether the value is a dataframe.
"""
return value.__name__ == "DataFrame"
def format_state(value: Dict) -> Dict:
"""Recursively format values in the given state.
@ -632,9 +644,8 @@ def format_state(value: Dict) -> Dict:
"""
if isinstance(value, go.Figure):
return json.loads(to_json(value))["data"]
import pandas as pd
if isinstance(value, pd.DataFrame):
if is_dataframe(type(value)):
return {
"columns": value.columns.tolist(),
"data": value.values.tolist(),
@ -657,6 +668,26 @@ def get_event(state, event):
return f"{state.get_name()}.{event}"
def format_string(string: str) -> str:
"""Format the given string as a JS string literal..
Args:
string: The string to format.
Returns:
The formatted string.
"""
# Escale backticks.
string = string.replace("\`", "`") # type: ignore
string = string.replace("`", "\`") # type: ignore
# Wrap the string so it looks like {`string`}.
string = wrap(string, "`")
string = wrap(string, "{")
return string
def call_event_handler(event_handler: EventHandler, arg: Var) -> EventSpec:
"""Call an event handler to get the event spec.
@ -723,6 +754,53 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[str, str],
return ((args[1], arg.name),)
def fix_events(events: Optional[List[Event]], token: str) -> List[Event]:
"""Fix a list of events returned by an event handler.
Args:
events: The events to fix.
token: The user token.
Returns:
The fixed events.
"""
# If the event handler returns nothing, return an empty list.
if events is None:
return []
# If the handler returns a single event, wrap it in a list.
if not isinstance(events, List):
events = [events]
# Fix the events created by the handler.
out = []
for e in events:
# If it is already an event, don't modify it.
if isinstance(e, Event):
name = e.name
payload = e.payload
# Otherwise, create an event from the event spec.
else:
if isinstance(e, EventHandler):
e = e()
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
name = to_snake_case(e.handler.fn.__qualname__)
payload = dict(e.args)
# Create an event and append it to the list.
out.append(
Event(
token=token,
name=name,
payload=payload,
)
)
return out
def merge_imports(*imports) -> ImportDict:
"""Merge two import dicts together.

View File

@ -123,10 +123,7 @@ class Var(ABC):
else:
out = utils.wrap(self.full_name, "{")
if self.is_string:
out = out.replace("\`", "`") # type: ignore
out = out.replace("`", "\`") # type: ignore
out = utils.wrap(out, "`")
out = utils.wrap(out, "{")
out = utils.format_string(out)
return out
def __getitem__(self, i) -> Var:
@ -140,7 +137,6 @@ class Var(ABC):
"""
# The type of the indexed var.
type_ = str
import pandas as pd
# Convert any vars to local vars.
if isinstance(i, Var):
@ -154,7 +150,7 @@ class Var(ABC):
type_ = utils.get_args(self.type_)[0]
else:
type_ = Any
elif utils._issubclass(self.type_, Union[dict, pd.DataFrame]):
elif utils.is_dataframe(self.type_):
if isinstance(i, str):
i = utils.wrap(i, '"')
if isinstance(self.type_, _GenericAlias):