Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into state-compression
Browse files Browse the repository at this point in the history
  • Loading branch information
benedikt-bartscher committed Feb 8, 2025
2 parents ad7dbc2 + 8b2c729 commit 86342c7
Show file tree
Hide file tree
Showing 18 changed files with 1,304 additions and 99 deletions.
66 changes: 45 additions & 21 deletions reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,15 @@
_substate_key,
code_uses_state_contexts,
)
from reflex.utils import codespaces, console, exceptions, format, prerequisites, types
from reflex.utils import (
codespaces,
console,
exceptions,
format,
path_ops,
prerequisites,
types,
)
from reflex.utils.exec import is_prod_mode, is_testing_env
from reflex.utils.imports import ImportVar

Expand Down Expand Up @@ -991,9 +999,10 @@ def get_compilation_time() -> str:
should_compile = self._should_compile()

if not should_compile:
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
self._compile_page(route, save_page=should_compile)
with console.timing("Evaluate Pages (Backend)"):
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
self._compile_page(route, save_page=should_compile)

# Add the optional endpoints (_upload)
self._add_optional_endpoints()
Expand All @@ -1019,10 +1028,11 @@ def get_compilation_time() -> str:
+ adhoc_steps_without_executor,
)

for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
self._compile_page(route, save_page=should_compile)
progress.advance(task)
with console.timing("Evaluate Pages (Frontend)"):
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
self._compile_page(route, save_page=should_compile)
progress.advance(task)

# Add the optional endpoints (_upload)
self._add_optional_endpoints()
Expand Down Expand Up @@ -1057,13 +1067,13 @@ def get_compilation_time() -> str:
custom_components |= component._get_all_custom_components()

# Perform auto-memoization of stateful components.
(
stateful_components_path,
stateful_components_code,
page_components,
) = compiler.compile_stateful_components(self._pages.values())

progress.advance(task)
with console.timing("Auto-memoize StatefulComponents"):
(
stateful_components_path,
stateful_components_code,
page_components,
) = compiler.compile_stateful_components(self._pages.values())
progress.advance(task)

# Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
if code_uses_state_contexts(stateful_components_code) and self._state is None:
Expand All @@ -1086,6 +1096,17 @@ def get_compilation_time() -> str:

progress.advance(task)

# Copy the assets.
assets_src = Path.cwd() / constants.Dirs.APP_ASSETS
if assets_src.is_dir():
with console.timing("Copy assets"):
path_ops.update_directory_tree(
src=assets_src,
dest=(
Path.cwd() / prerequisites.get_web_dir() / constants.Dirs.PUBLIC
),
)

# Use a forking process pool, if possible. Much faster, especially for large sites.
# Fallback to ThreadPoolExecutor as something that will always work.
executor = None
Expand Down Expand Up @@ -1138,9 +1159,10 @@ def _submit_work(fn: Callable, *args, **kwargs):
_submit_work(compiler.remove_tailwind_from_postcss)

# Wait for all compilation tasks to complete.
for future in concurrent.futures.as_completed(result_futures):
compile_results.append(future.result())
progress.advance(task)
with console.timing("Compile to Javascript"):
for future in concurrent.futures.as_completed(result_futures):
compile_results.append(future.result())
progress.advance(task)

app_root = self._app_root(app_wrappers=app_wrappers)

Expand Down Expand Up @@ -1175,7 +1197,8 @@ def _submit_work(fn: Callable, *args, **kwargs):
progress.stop()

# Install frontend packages.
self._get_frontend_packages(all_imports)
with console.timing("Install Frontend Packages"):
self._get_frontend_packages(all_imports)

# Setup the next.config.js
transpile_packages = [
Expand All @@ -1201,8 +1224,9 @@ def _submit_work(fn: Callable, *args, **kwargs):
# Remove pages that are no longer in the app.
p.unlink()

for output_path, code in compile_results:
compiler_utils.write_page(output_path, code)
with console.timing("Write to Disk"):
for output_path, code in compile_results:
compiler_utils.write_page(output_path, code)

@contextlib.asynccontextmanager
async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
Expand Down
34 changes: 22 additions & 12 deletions reflex/compiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,34 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
validate_imports(collapsed_import_dict)
import_dicts = []
for lib, fields in collapsed_import_dict.items():
default, rest = compile_import_statement(fields)

# prevent lib from being rendered on the page if all imports are non rendered kind
if not any(f.render for f in fields):
continue

if not lib:
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.")
import_dicts.extend(get_import_dict(module) for module in sorted(rest))
continue
lib_paths: dict[str, list[ImportVar]] = {}

for field in fields:
lib_paths.setdefault(field.package_path, []).append(field)

# remove the version before rendering the package imports
lib = format.format_library_name(lib)
compiled = {
path: compile_import_statement(fields) for path, fields in lib_paths.items()
}

for path, (default, rest) in compiled.items():
if not lib:
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.")
import_dicts.extend(get_import_dict(module) for module in sorted(rest))
continue

# remove the version before rendering the package imports
formatted_lib = format.format_library_name(lib) + (
path if path != "/" else ""
)

import_dicts.append(get_import_dict(lib, default, rest))
import_dicts.append(get_import_dict(formatted_lib, default, rest))
return import_dicts


Expand Down
4 changes: 1 addition & 3 deletions reflex/components/core/sticky.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ def add_style(self):
default=True,
global_ref=False,
)
localhost_hostnames = Var.create(
["localhost", "127.0.0.1", "[::1]"]
).guess_type()
localhost_hostnames = Var.create(["localhost", "127.0.0.1", "[::1]"])
is_localhost_expr = localhost_hostnames.contains(
Var("window.location.hostname", _var_type=str).guess_type(),
)
Expand Down
14 changes: 11 additions & 3 deletions reflex/components/lucide/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from reflex.utils import format
from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralStringVar
from reflex.vars.sequence import LiteralStringVar, StringVar


class LucideIconComponent(Component):
Expand Down Expand Up @@ -40,7 +40,12 @@ def create(cls, *children, **props) -> Component:
The created component.
"""
if children:
if len(children) == 1 and isinstance(children[0], str):
if len(children) == 1:
child = Var.create(children[0]).guess_type()
if not isinstance(child, StringVar):
raise AttributeError(
f"Icon name must be a string, got {children[0]._var_type if isinstance(children[0], Var) else children[0]}"
)
props["tag"] = children[0]
else:
raise AttributeError(
Expand All @@ -56,7 +61,10 @@ def create(cls, *children, **props) -> Component:
else:
raise TypeError(f"Icon name must be a string, got {type(tag)}")
elif isinstance(tag, Var):
return DynamicIcon.create(name=tag, **props)
tag_stringified = tag.guess_type()
if not isinstance(tag_stringified, StringVar):
raise TypeError(f"Icon name must be a string, got {tag._var_type}")
return DynamicIcon.create(name=tag_stringified.replace("_", "-"), **props)

if (
not isinstance(tag, str)
Expand Down
31 changes: 29 additions & 2 deletions reflex/components/plotly/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
"""Plotly components."""

from .plotly import Plotly
from reflex.components.component import ComponentNamespace

plotly = Plotly.create
from .plotly import (
Plotly,
PlotlyBasic,
PlotlyCartesian,
PlotlyFinance,
PlotlyGeo,
PlotlyGl2d,
PlotlyGl3d,
PlotlyMapbox,
PlotlyStrict,
)


class PlotlyNamespace(ComponentNamespace):
"""Plotly namespace."""

__call__ = Plotly.create
basic = PlotlyBasic.create
cartesian = PlotlyCartesian.create
geo = PlotlyGeo.create
gl2d = PlotlyGl2d.create
gl3d = PlotlyGl3d.create
finance = PlotlyFinance.create
mapbox = PlotlyMapbox.create
strict = PlotlyStrict.create


plotly = PlotlyNamespace()
Loading

0 comments on commit 86342c7

Please sign in to comment.