General code cleanup (#312)
This commit is contained in:
parent
8068d5f176
commit
2ccbfff223
@ -221,18 +221,20 @@ class App(Base):
|
||||
for route in self.pages:
|
||||
route = "" if route == "index" else route
|
||||
|
||||
if new_route.startswith(route + "/[[..."):
|
||||
if new_route.startswith(f"{route}/[[..."):
|
||||
raise ValueError(
|
||||
f"You cannot define a route with the same specificity as a optional catch-all route ('{route}' and '{new_route}')"
|
||||
)
|
||||
|
||||
route_catchall = utils.catchall_in_route(route)
|
||||
if route_catchall and newroute_catchall:
|
||||
# both route have a catchall, check if preceding path is the same
|
||||
if utils.catchall_prefix(route) == utils.catchall_prefix(new_route):
|
||||
raise ValueError(
|
||||
f"You cannot use multiple catchall for the same dynamic path ({route} !== {new_route})"
|
||||
)
|
||||
if (
|
||||
route_catchall
|
||||
and newroute_catchall
|
||||
and utils.catchall_prefix(route) == utils.catchall_prefix(new_route)
|
||||
):
|
||||
raise ValueError(
|
||||
f"You cannot use multiple catchall for the same dynamic path ({route} !== {new_route})"
|
||||
)
|
||||
|
||||
def add_custom_404_page(self, component, title=None, image=None, description=None):
|
||||
"""Define a custom 404 page for any url having no match.
|
||||
|
@ -35,8 +35,8 @@ def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None)
|
||||
The compiled import statement.
|
||||
"""
|
||||
# Handle the case of direct imports with no libraries.
|
||||
if lib == "":
|
||||
assert default == "", "No default field allowed for empty library."
|
||||
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."
|
||||
return join([IMPORT_LIB(lib=lib) for lib in sorted(rest)])
|
||||
|
||||
@ -45,12 +45,11 @@ def format_import(lib: str, default: str = "", rest: Optional[Set[str]] = None)
|
||||
if len(default) == 0 and len(rest) == 0:
|
||||
# Handle the case of importing a library with no fields.
|
||||
return IMPORT_LIB(lib=lib)
|
||||
else:
|
||||
# Handle importing specific fields from a library.
|
||||
others = f'{{{", ".join(sorted(rest))}}}' if len(rest) > 0 else ""
|
||||
if len(default) > 0 and len(rest) > 0:
|
||||
default += ", "
|
||||
return IMPORT_FIELDS(default=default, others=others, lib=lib)
|
||||
# Handle importing specific fields from a library.
|
||||
others = f'{{{", ".join(sorted(rest))}}}' if len(rest) > 0 else ""
|
||||
if default != "" and len(rest) > 0:
|
||||
default += ", "
|
||||
return IMPORT_FIELDS(default=default, others=others, lib=lib)
|
||||
|
||||
|
||||
# Code to render a NextJS Document root.
|
||||
@ -163,7 +162,7 @@ USE_EFFECT = join(
|
||||
[
|
||||
"useEffect(() => {{",
|
||||
" if(!isReady) {{",
|
||||
f" return;",
|
||||
" return;",
|
||||
" }}",
|
||||
f" if (!{SOCKET}.current) {{{{",
|
||||
f" connect({SOCKET}, {{state}}, {{set_state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {EVENT_ENDPOINT})",
|
||||
|
@ -92,14 +92,13 @@ class Component(Base, ABC):
|
||||
if key in triggers:
|
||||
# Event triggers are bound to event chains.
|
||||
field_type = EventChain
|
||||
else:
|
||||
# If the key is not in the fields, skip it.
|
||||
if key not in props:
|
||||
continue
|
||||
|
||||
elif key in props:
|
||||
# Set the field type.
|
||||
field_type = fields[key].type_
|
||||
|
||||
else:
|
||||
continue
|
||||
|
||||
# Check whether the key is a component prop.
|
||||
if utils._issubclass(field_type, Var):
|
||||
try:
|
||||
@ -292,9 +291,9 @@ class Component(Base, ABC):
|
||||
)
|
||||
|
||||
children = [
|
||||
Bare.create(contents=Var.create(child, is_string=True))
|
||||
if not isinstance(child, Component)
|
||||
else child
|
||||
child
|
||||
if isinstance(child, Component)
|
||||
else Bare.create(contents=Var.create(child, is_string=True))
|
||||
for child in children
|
||||
]
|
||||
return cls(children=children, **props)
|
||||
@ -454,7 +453,7 @@ class CustomComponent(Component):
|
||||
if utils._issubclass(type_, Base):
|
||||
try:
|
||||
value = BaseVar(name=value.json(), type_=type_, is_local=True)
|
||||
except:
|
||||
except Exception:
|
||||
value = Var.create(value)
|
||||
else:
|
||||
value = Var.create(value, is_string=type(value) is str)
|
||||
|
@ -77,7 +77,7 @@ class PinInput(ChakraComponent):
|
||||
Returns:
|
||||
The pin input component.
|
||||
"""
|
||||
if len(children) == 0 and "length" in props:
|
||||
if not children and "length" in props:
|
||||
children = [PinInputField()] * props["length"]
|
||||
return super().create(*children, **props)
|
||||
|
||||
|
@ -68,7 +68,7 @@ class RangeSlider(ChakraComponent):
|
||||
Returns:
|
||||
The RangeSlider component.
|
||||
"""
|
||||
if len(children) == 0:
|
||||
if not children:
|
||||
children = [
|
||||
RangeSliderTrack.create(
|
||||
RangeSliderFilledTrack.create(),
|
||||
|
@ -68,7 +68,7 @@ class Slider(ChakraComponent):
|
||||
Returns:
|
||||
The slider component.
|
||||
"""
|
||||
if len(children) == 0:
|
||||
if not children:
|
||||
children = [
|
||||
SliderTrack.create(
|
||||
SliderFilledTrack.create(),
|
||||
|
@ -42,11 +42,13 @@ const Plot = dynamic(() => import('react-plotly.js'), { ssr: false });
|
||||
"""
|
||||
|
||||
def _render(self) -> Tag:
|
||||
if isinstance(self.data, Figure):
|
||||
if self.layout is None:
|
||||
if self.width is not None:
|
||||
layout = Var.create({"width": self.width, "height": self.height})
|
||||
assert layout is not None
|
||||
self.layout = layout
|
||||
if (
|
||||
isinstance(self.data, Figure)
|
||||
and self.layout is None
|
||||
and self.width is not None
|
||||
):
|
||||
layout = Var.create({"width": self.width, "height": self.height})
|
||||
assert layout is not None
|
||||
self.layout = layout
|
||||
|
||||
return super()._render()
|
||||
|
@ -78,10 +78,9 @@ def format_area(x: List, y: List, y0: Optional[List] = None) -> List:
|
||||
"""
|
||||
if y0 is None:
|
||||
return format_xy(x, y)
|
||||
else:
|
||||
if len(x) != len(y) or len(x) != len(y0):
|
||||
raise ValueError("x, y, and y0 must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "y0": y0[i]} for i in range(len(x))]
|
||||
if len(x) != len(y) or len(x) != len(y0):
|
||||
raise ValueError("x, y, and y0 must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "y0": y0[i]} for i in range(len(x))]
|
||||
|
||||
|
||||
def format_bar(x: List, y: List, y0: Optional[List] = None) -> List:
|
||||
@ -100,10 +99,9 @@ def format_bar(x: List, y: List, y0: Optional[List] = None) -> List:
|
||||
"""
|
||||
if y0 is None:
|
||||
return format_xy(x, y)
|
||||
else:
|
||||
if len(x) != len(y) or len(x) != len(y0):
|
||||
raise ValueError("x, y, and y0 must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "y0": y0[i]} for i in range(len(x))]
|
||||
if len(x) != len(y) or len(x) != len(y0):
|
||||
raise ValueError("x, y, and y0 must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "y0": y0[i]} for i in range(len(x))]
|
||||
|
||||
|
||||
def format_box_plot(
|
||||
@ -135,40 +133,37 @@ def format_box_plot(
|
||||
ValueError: If y is not provided and min, max, median, q1, and q3 are not provided.
|
||||
ValueError: If y is not provided and x, min, max, median, q1, and q3 are not the same length.
|
||||
"""
|
||||
data = []
|
||||
if x is None:
|
||||
raise ValueError("x must be specified")
|
||||
|
||||
if y is not None:
|
||||
return format_xy(x, y)
|
||||
|
||||
else:
|
||||
if min_ is None or max_ is None or median is None or q1 is None or q3 is None:
|
||||
raise ValueError(
|
||||
"min, max, median, q1, and q3 must be specified if y is not provided"
|
||||
)
|
||||
if (
|
||||
len(x) != len(min_)
|
||||
or len(x) != len(max_)
|
||||
or len(x) != len(median)
|
||||
or len(x) != len(q1)
|
||||
or len(x) != len(q3)
|
||||
):
|
||||
raise ValueError(
|
||||
"x, min, max, median, q1, and q3 must be the same length and specified if y is not provided"
|
||||
)
|
||||
for i in range(len(x)):
|
||||
data.append(
|
||||
{
|
||||
"x": x[i],
|
||||
"min": min_[i],
|
||||
"max": max_[i],
|
||||
"median": median[i],
|
||||
"q1": q1[i],
|
||||
"q3": q3[i],
|
||||
}
|
||||
)
|
||||
return data
|
||||
if min_ is None or max_ is None or median is None or q1 is None or q3 is None:
|
||||
raise ValueError(
|
||||
"min, max, median, q1, and q3 must be specified if y is not provided"
|
||||
)
|
||||
if (
|
||||
len(x) != len(min_)
|
||||
or len(x) != len(max_)
|
||||
or len(x) != len(median)
|
||||
or len(x) != len(q1)
|
||||
or len(x) != len(q3)
|
||||
):
|
||||
raise ValueError(
|
||||
"x, min, max, median, q1, and q3 must be the same length and specified if y is not provided"
|
||||
)
|
||||
return [
|
||||
{
|
||||
"x": x[i],
|
||||
"min": min_[i],
|
||||
"max": max_[i],
|
||||
"median": median[i],
|
||||
"q1": q1[i],
|
||||
"q3": q3[i],
|
||||
}
|
||||
for i in range(len(x))
|
||||
]
|
||||
|
||||
|
||||
def format_histogram(x: List) -> List:
|
||||
@ -210,10 +205,9 @@ def format_pie(x: List, y: List, label: Optional[List] = None) -> List:
|
||||
|
||||
if label is None:
|
||||
return format_xy(x, y)
|
||||
else:
|
||||
if len(x) != len(y) or len(x) != len(label):
|
||||
raise ValueError("x, y, and label must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "label": label[i]} for i in range(len(x))]
|
||||
if len(x) != len(y) or len(x) != len(label):
|
||||
raise ValueError("x, y, and label must be the same length")
|
||||
return [{"x": x[i], "y": y[i], "label": label[i]} for i in range(len(x))]
|
||||
|
||||
|
||||
def format_voronoi(x: List, y: List) -> List:
|
||||
@ -314,26 +308,26 @@ def data(graph: str, x: List, y: Optional[List] = None, **kwargs) -> List:
|
||||
ValueError: If graph is not provided.
|
||||
ValueError: If graph is not supported.
|
||||
"""
|
||||
if graph == "box_plot":
|
||||
return format_box_plot(x, y, **kwargs)
|
||||
if graph == "area":
|
||||
return format_area(x, y, **kwargs) # type: ignore
|
||||
elif graph == "bar":
|
||||
return format_bar(x, y) # type: ignore
|
||||
elif graph == "line":
|
||||
return format_line(x, y) # type: ignore
|
||||
elif graph == "scatter":
|
||||
return format_scatter(x, y, **kwargs) # type: ignore
|
||||
elif graph == "area":
|
||||
return format_area(x, y, **kwargs) # type: ignore
|
||||
elif graph == "histogram":
|
||||
return format_histogram(x)
|
||||
elif graph == "pie":
|
||||
return format_pie(x, y, **kwargs) # type: ignore
|
||||
elif graph == "voronoi":
|
||||
return format_voronoi(x, y) # type: ignore
|
||||
elif graph == "box_plot":
|
||||
return format_box_plot(x, y, **kwargs)
|
||||
elif graph == "candlestick":
|
||||
return format_candlestick(x, **kwargs)
|
||||
elif graph == "error_bar":
|
||||
return format_error_bar(x, y, **kwargs) # type: ignore
|
||||
elif graph == "histogram":
|
||||
return format_histogram(x)
|
||||
elif graph == "line":
|
||||
return format_line(x, y) # type: ignore
|
||||
elif graph == "pie":
|
||||
return format_pie(x, y, **kwargs) # type: ignore
|
||||
elif graph == "scatter":
|
||||
return format_scatter(x, y, **kwargs) # type: ignore
|
||||
elif graph == "voronoi":
|
||||
return format_voronoi(x, y) # type: ignore
|
||||
else:
|
||||
raise ValueError("Invalid graph type")
|
||||
|
||||
|
@ -31,7 +31,7 @@ class Foreach(Component):
|
||||
"""
|
||||
try:
|
||||
type_ = iterable.type_.__args__[0]
|
||||
except:
|
||||
except Exception:
|
||||
type_ = Any
|
||||
arg = BaseVar(name="_", type_=type_, is_local=True)
|
||||
return cls(
|
||||
|
@ -34,9 +34,5 @@ class Link(ChakraComponent):
|
||||
Returns:
|
||||
The component.
|
||||
"""
|
||||
kwargs = {}
|
||||
if "href" in props:
|
||||
kwargs["href"] = props.pop("href")
|
||||
else:
|
||||
kwargs["href"] = "#"
|
||||
kwargs = {"href": props.pop("href") if "href" in props else "#"}
|
||||
return NextLink.create(super().create(*children, **props), **kwargs)
|
||||
|
@ -91,7 +91,7 @@ class IterTag(Tag):
|
||||
"""
|
||||
try:
|
||||
type_ = self.iterable.type_.__args__[0]
|
||||
except:
|
||||
except Exception:
|
||||
type_ = Any
|
||||
arg = BaseVar(
|
||||
name=utils.get_unique_variable_name(),
|
||||
|
@ -195,11 +195,11 @@ class Endpoint(Enum):
|
||||
class PathArgType(SimpleNamespace):
|
||||
"""Type of pathArg extracted from URI path."""
|
||||
|
||||
# Typecast to str is needed for Enum to work.
|
||||
SINGLE = str("arg_single")
|
||||
LIST = str("arg_list")
|
||||
|
||||
|
||||
# ROUTE REGEXs
|
||||
class RouteRegex(SimpleNamespace):
|
||||
"""Regex used for extracting path args in path."""
|
||||
|
||||
|
@ -65,10 +65,10 @@ class EventHandler(Base):
|
||||
# Otherwise, convert to JSON.
|
||||
try:
|
||||
values.append(json.dumps(arg))
|
||||
except TypeError:
|
||||
except TypeError as e:
|
||||
raise TypeError(
|
||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||
)
|
||||
) from e
|
||||
payload = tuple(zip(fn_args, values))
|
||||
|
||||
# Return the event spec.
|
||||
|
@ -157,7 +157,7 @@ class State(Base, ABC):
|
||||
Returns:
|
||||
The substates of the state.
|
||||
"""
|
||||
return {subclass for subclass in cls.__subclasses__()}
|
||||
return set(cls.__subclasses__())
|
||||
|
||||
@classmethod
|
||||
@functools.lru_cache()
|
||||
@ -408,7 +408,7 @@ class State(Base, ABC):
|
||||
events = await fn(**event.payload)
|
||||
else:
|
||||
events = fn(**event.payload)
|
||||
except:
|
||||
except Exception:
|
||||
error = traceback.format_exc()
|
||||
print(error)
|
||||
return StateUpdate(
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""General utility functions."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
@ -87,14 +89,11 @@ def is_generic_alias(cls: GenericType) -> bool:
|
||||
if isinstance(cls, _GenericAlias):
|
||||
return True
|
||||
|
||||
try:
|
||||
with contextlib.suppress(ImportError):
|
||||
from typing import _SpecialGenericAlias # type: ignore
|
||||
|
||||
if isinstance(cls, _SpecialGenericAlias):
|
||||
return True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# For newer versions of Python.
|
||||
try:
|
||||
from types import GenericAlias # type: ignore
|
||||
@ -113,17 +112,11 @@ def is_union(cls: GenericType) -> bool:
|
||||
Returns:
|
||||
Whether the class is a Union.
|
||||
"""
|
||||
try:
|
||||
with contextlib.suppress(ImportError):
|
||||
from typing import _UnionGenericAlias # type: ignore
|
||||
|
||||
return isinstance(cls, _UnionGenericAlias)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if is_generic_alias(cls):
|
||||
return cls.__origin__ == Union
|
||||
|
||||
return False
|
||||
return cls.__origin__ == Union if is_generic_alias(cls) else False
|
||||
|
||||
|
||||
def get_base_class(cls: GenericType) -> Type:
|
||||
@ -138,10 +131,7 @@ def get_base_class(cls: GenericType) -> Type:
|
||||
if is_union(cls):
|
||||
return tuple(get_base_class(arg) for arg in get_args(cls))
|
||||
|
||||
if is_generic_alias(cls):
|
||||
return get_base_class(cls.__origin__)
|
||||
|
||||
return cls
|
||||
return get_base_class(cls.__origin__) if is_generic_alias(cls) else cls
|
||||
|
||||
|
||||
def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
|
||||
@ -157,7 +147,7 @@ def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
|
||||
# Special check for Any.
|
||||
if cls_check == Any:
|
||||
return True
|
||||
if cls == Any or cls == Callable:
|
||||
if cls in [Any, Callable]:
|
||||
return False
|
||||
cls_base = get_base_class(cls)
|
||||
cls_check_base = get_base_class(cls_check)
|
||||
@ -361,8 +351,7 @@ def get_app() -> ModuleType:
|
||||
config = get_config()
|
||||
module = ".".join([config.app_name, config.app_name])
|
||||
sys.path.insert(0, os.getcwd())
|
||||
app = __import__(module, fromlist=(constants.APP_VAR,))
|
||||
return app
|
||||
return __import__(module, fromlist=(constants.APP_VAR,))
|
||||
|
||||
|
||||
def create_config(app_name: str):
|
||||
@ -570,12 +559,7 @@ def get_num_workers() -> int:
|
||||
Returns:
|
||||
The number of backend worker processes.
|
||||
"""
|
||||
if get_redis() is None:
|
||||
# If there is no redis, then just use 1 worker.
|
||||
return 1
|
||||
|
||||
# Use the number of cores * 2 + 1.
|
||||
return (os.cpu_count() or 1) * 2 + 1
|
||||
return 1 if get_redis() is None else (os.cpu_count() or 1) * 2 + 1
|
||||
|
||||
|
||||
def get_api_port() -> int:
|
||||
@ -888,9 +872,7 @@ def format_route(route: str) -> str:
|
||||
"""
|
||||
route = route.strip(os.path.sep)
|
||||
route = to_snake_case(route).replace("_", "-")
|
||||
if route == "":
|
||||
return constants.INDEX_ROUTE
|
||||
return route
|
||||
return constants.INDEX_ROUTE if route == "" else route
|
||||
|
||||
|
||||
def format_cond(
|
||||
@ -936,7 +918,7 @@ def format_event_handler(handler: EventHandler) -> str:
|
||||
try:
|
||||
# Try to get the state from the module.
|
||||
state = vars(sys.modules[handler.fn.__module__])[state_name]
|
||||
except:
|
||||
except Exception:
|
||||
# If the state isn't in the module, just return the function name.
|
||||
return handler.fn.__qualname__
|
||||
return ".".join([state.get_full_name(), name])
|
||||
@ -1128,10 +1110,7 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[str, str],
|
||||
The handler args.
|
||||
"""
|
||||
args = inspect.getfullargspec(event_spec.handler.fn).args
|
||||
if len(args) > 2:
|
||||
return event_spec.args
|
||||
else:
|
||||
return ((args[1], arg.name),)
|
||||
return event_spec.args if len(args) > 2 else ((args[1], arg.name),)
|
||||
|
||||
|
||||
def fix_events(events: Optional[List[Event]], token: str) -> List[Event]:
|
||||
|
@ -64,7 +64,7 @@ class Var(ABC):
|
||||
value = json.loads(to_json(value))["data"]
|
||||
type_ = Figure
|
||||
|
||||
name = json.dumps(value) if not isinstance(value, str) else value
|
||||
name = value if isinstance(value, str) else json.dumps(value)
|
||||
|
||||
return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
|
||||
|
||||
@ -118,10 +118,7 @@ class Var(ABC):
|
||||
Returns:
|
||||
The wrapped var, i.e. {state.var}.
|
||||
"""
|
||||
if self.is_local:
|
||||
out = self.full_name
|
||||
else:
|
||||
out = utils.wrap(self.full_name, "{")
|
||||
out = self.full_name if self.is_local else utils.wrap(self.full_name, "{")
|
||||
if self.is_string:
|
||||
out = utils.format_string(out)
|
||||
return out
|
||||
@ -263,7 +260,7 @@ class Var(ABC):
|
||||
if other is None:
|
||||
name = f"{op}{self.full_name}"
|
||||
else:
|
||||
props = (self, other) if not flip else (other, self)
|
||||
props = (other, self) if flip else (self, other)
|
||||
name = f"{props[0].full_name} {op} {props[1].full_name}"
|
||||
if fn is None:
|
||||
name = utils.wrap(name, "(")
|
||||
@ -620,9 +617,7 @@ class Var(ABC):
|
||||
Returns:
|
||||
The full name of the var.
|
||||
"""
|
||||
if self.state == "":
|
||||
return self.name
|
||||
return ".".join([self.state, self.name])
|
||||
return self.name if self.state == "" else ".".join([self.state, self.name])
|
||||
|
||||
def set_state(self, state: Type[State]) -> Any:
|
||||
"""Set the state of the var.
|
||||
@ -685,9 +680,7 @@ class BaseVar(Var, Base):
|
||||
return {}
|
||||
if issubclass(type_, tuple):
|
||||
return ()
|
||||
if issubclass(type_, set):
|
||||
return set()
|
||||
return None
|
||||
return set() if issubclass(type_, set) else None
|
||||
|
||||
def get_setter_name(self, include_state: bool = True) -> str:
|
||||
"""Get the name of the var's generated setter function.
|
||||
|
@ -300,4 +300,4 @@ def test_custom_component_hash(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}
|
||||
assert {component1, component2} == {component1}
|
||||
|
@ -195,9 +195,9 @@ def test_var_list_slicing():
|
||||
"""Test that we can slice into list vars."""
|
||||
lst = BaseVar(name="lst", type_=List[int])
|
||||
|
||||
assert str(lst[0:1]) == "{lst.slice(0, 1)}"
|
||||
assert str(lst[:1]) == "{lst.slice(0, 1)}"
|
||||
assert str(lst[0:]) == "{lst.slice(0, undefined)}"
|
||||
assert str(lst[:1]) == "{lst.slice(0, 1)}"
|
||||
assert str(lst[:]) == "{lst.slice(0, undefined)}"
|
||||
|
||||
|
||||
def test_dict_indexing():
|
||||
|
Loading…
Reference in New Issue
Block a user