Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into masenf/proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
masenf committed Dec 17, 2024
2 parents 67b6735 + f71e6f9 commit ce153ee
Show file tree
Hide file tree
Showing 61 changed files with 1,277 additions and 332 deletions.
3 changes: 2 additions & 1 deletion benchmarks/benchmark_compile_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import argparse
import json
import os
from pathlib import Path

from utils import send_data_to_posthog

Expand All @@ -18,7 +19,7 @@ def extract_stats_from_json(json_file: str) -> list[dict]:
Returns:
list[dict]: The stats for each test.
"""
with open(json_file, "r") as file:
with Path(json_file).open() as file:
json_data = json.load(file)

# Load the JSON data if it is a string, otherwise assume it's already a dictionary
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/benchmark_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import argparse
import json
import os
from pathlib import Path

from utils import send_data_to_posthog

Expand All @@ -18,7 +19,7 @@ def extract_stats_from_json(json_file: str) -> dict:
Returns:
dict: The stats for each test.
"""
with open(json_file, "r") as file:
with Path(json_file).open() as file:
json_data = json.load(file)

# Load the JSON data if it is a string, otherwise assume it's already a dictionary
Expand Down
34 changes: 14 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,19 @@ version = "0.6.7dev1"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [
"Nikhil Rao <nikhil@reflex.dev>",
"Alek Petuskey <alek@reflex.dev>",
"Masen Furer <masen@reflex.dev>",
"Elijah Ahianyo <elijah@reflex.dev>",
"Thomas Brandého <thomas@reflex.dev>",
"Nikhil Rao <nikhil@reflex.dev>",
"Alek Petuskey <alek@reflex.dev>",
"Masen Furer <masen@reflex.dev>",
"Elijah Ahianyo <elijah@reflex.dev>",
"Thomas Brandého <thomas@reflex.dev>",
]
readme = "README.md"
homepage = "https://reflex.dev"
repository = "https://github.com/reflex-dev/reflex"
documentation = "https://reflex.dev/docs/getting-started/introduction"
keywords = [
"web",
"framework",
]
classifiers = [
"Development Status :: 4 - Beta",
]
packages = [
{include = "reflex"}
]
keywords = ["web", "framework"]
classifiers = ["Development Status :: 4 - Beta"]
packages = [{ include = "reflex" }]

[tool.poetry.dependencies]
python = "^3.9"
Expand All @@ -42,11 +35,11 @@ uvicorn = ">=0.20.0"
starlette-admin = ">=0.11.0,<1.0"
alembic = ">=1.11.1,<2.0"
platformdirs = ">=3.10.0,<5.0"
distro = {version = ">=1.8.0,<2.0", platform = "linux"}
distro = { version = ">=1.8.0,<2.0", platform = "linux" }
python-engineio = "!=4.6.0"
wrapt = [
{version = ">=1.14.0,<2.0", python = ">=3.11"},
{version = ">=1.11.0,<2.0", python = "<3.11"},
{ version = ">=1.14.0,<2.0", python = ">=3.11" },
{ version = ">=1.11.0,<2.0", python = "<3.11" },
]
packaging = ">=23.1,<25.0"
reflex-hosting-cli = ">=0.1.29,<2.0"
Expand Down Expand Up @@ -97,14 +90,15 @@ build-backend = "poetry.core.masonry.api"

[tool.ruff]
target-version = "py39"
output-format = "concise"
lint.isort.split-on-trailing-comma = false
lint.select = ["B", "D", "E", "F", "I", "SIM", "W", "RUF", "FURB", "ERA"]
lint.select = ["B", "C4", "D", "E", "ERA", "F", "FURB", "I", "PERF", "PTH", "RUF", "SIM", "W"]
lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF012"]
lint.pydocstyle.convention = "google"

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"tests/*.py" = ["D100", "D103", "D104", "B018"]
"tests/*.py" = ["D100", "D103", "D104", "B018", "PERF"]
"reflex/.templates/*.py" = ["D100", "D103", "D104"]
"*.pyi" = ["D301", "D415", "D417", "D418", "E742"]
"*/blank.py" = ["I001"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ export function {{tag_name}} () {
{{ hook }}
{% endfor %}

{% for hook, data in component._get_all_hooks().items() if not data.position or data.position == const.hook_position.PRE_TRIGGER %}
{{ hook }}
{% endfor %}

{% for hook in memo_trigger_hooks %}
{{ hook }}
{% endfor %}

{% for hook in component._get_all_hooks() %}
{% for hook, data in component._get_all_hooks().items() if data.position and data.position == const.hook_position.POST_TRIGGER %}
{{ hook }}
{% endfor %}

Expand Down
4 changes: 2 additions & 2 deletions reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def _add_cors(self):
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
allow_origins=["*"],
allow_origins=get_config().cors_allowed_origins,
)

@property
Expand Down Expand Up @@ -1301,7 +1301,7 @@ async def process(
await asyncio.create_task(
app.event_namespace.emit(
"reload",
data=format.json_dumps(event),
data=event,
to=sid,
)
)
Expand Down
15 changes: 8 additions & 7 deletions reflex/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None

# can't use reflex.config.environment here cause of circular import
reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
for base in bases:
try:
base = None
try:
for base in bases:
if not reload and getattr(base, field_name, None):
pass
except TypeError as te:
raise VarNameError(
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
f'use a different field name instead".'
) from te
except TypeError as te:
raise VarNameError(
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
f'use a different field name instead".'
) from te


# monkeypatch pydantic validate_field_name method to skip validating
Expand Down
1 change: 1 addition & 0 deletions reflex/compiler/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self) -> None:
"on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
"update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
"frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL,
"hook_position": constants.Hooks.HookPosition,
}


Expand Down
5 changes: 2 additions & 3 deletions reflex/compiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,15 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
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}): # type: ignore
if not any(f.render for f in fields): # type: ignore
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.")
for module in sorted(rest):
import_dicts.append(get_import_dict(module))
import_dicts.extend(get_import_dict(module) for module in sorted(rest))
continue

# remove the version before rendering the package imports
Expand Down
70 changes: 47 additions & 23 deletions reflex/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,7 @@ def _iter_parent_classes_with_method(cls, method: str) -> Iterator[Type[Componen
Yields:
The parent classes that define the method (differently than the base).
"""
seen_methods = set([getattr(Component, method)])
seen_methods = {getattr(Component, method)}
for clz in cls.mro():
if clz is Component:
break
Expand Down Expand Up @@ -1368,7 +1368,9 @@ def _get_hooks_imports(self) -> ParsedImportDict:
if user_hooks_data is not None:
other_imports.append(user_hooks_data.imports)
other_imports.extend(
hook_imports for hook_imports in self._get_added_hooks().values()
hook_vardata.imports
for hook_vardata in self._get_added_hooks().values()
if hook_vardata is not None
)

return imports.merge_imports(_imports, *other_imports)
Expand All @@ -1390,15 +1392,9 @@ def _get_imports(self) -> ParsedImportDict:

# Collect imports from Vars used directly by this component.
var_datas = [var._get_all_var_data() for var in self._get_vars()]
var_imports: List[ImmutableParsedImportDict] = list(
map(
lambda var_data: var_data.imports,
filter(
None,
var_datas,
),
)
)
var_imports: List[ImmutableParsedImportDict] = [
var_data.imports for var_data in var_datas if var_data is not None
]

added_import_dicts: list[ParsedImportDict] = []
for clz in self._iter_parent_classes_with_method("add_imports"):
Expand All @@ -1407,8 +1403,9 @@ def _get_imports(self) -> ParsedImportDict:
if not isinstance(list_of_import_dict, list):
list_of_import_dict = [list_of_import_dict]

for import_dict in list_of_import_dict:
added_import_dicts.append(parse_imports(import_dict))
added_import_dicts.extend(
[parse_imports(import_dict) for import_dict in list_of_import_dict]
)

return imports.merge_imports(
*self._get_props_imports(),
Expand Down Expand Up @@ -1522,7 +1519,7 @@ def _get_hooks_internal(self) -> dict[str, None]:
**self._get_special_hooks(),
}

def _get_added_hooks(self) -> dict[str, ImportDict]:
def _get_added_hooks(self) -> dict[str, VarData | None]:
"""Get the hooks added via `add_hooks` method.
Returns:
Expand All @@ -1531,17 +1528,15 @@ def _get_added_hooks(self) -> dict[str, ImportDict]:
code = {}

def extract_var_hooks(hook: Var):
_imports = {}
var_data = VarData.merge(hook._get_all_var_data())
if var_data is not None:
for sub_hook in var_data.hooks:
code[sub_hook] = {}
if var_data.imports:
_imports = var_data.imports
code[sub_hook] = None

if str(hook) in code:
code[str(hook)] = imports.merge_imports(code[str(hook)], _imports)
code[str(hook)] = VarData.merge(var_data, code[str(hook)])
else:
code[str(hook)] = _imports
code[str(hook)] = var_data

# Add the hook code from add_hooks for each parent class (this is reversed to preserve
# the order of the hooks in the final output)
Expand All @@ -1550,7 +1545,7 @@ def extract_var_hooks(hook: Var):
if isinstance(hook, Var):
extract_var_hooks(hook)
else:
code[hook] = {}
code[hook] = None

return code

Expand Down Expand Up @@ -1592,8 +1587,7 @@ def _get_all_hooks(self) -> dict[str, None]:
if hooks is not None:
code[hooks] = None

for hook in self._get_added_hooks():
code[hook] = None
code.update(self._get_added_hooks())

# Add the hook code for the children.
for child in self.children:
Expand Down Expand Up @@ -2195,6 +2189,31 @@ def _get_hook_deps(hook: str) -> list[str]:
]
return [var_name]

@staticmethod
def _get_deps_from_event_trigger(event: EventChain | EventSpec | Var) -> set[str]:
"""Get the dependencies accessed by event triggers.
Args:
event: The event trigger to extract deps from.
Returns:
The dependencies accessed by the event triggers.
"""
events: list = [event]
deps = set()

if isinstance(event, EventChain):
events.extend(event.events)

for ev in events:
if isinstance(ev, EventSpec):
for arg in ev.args:
for a in arg:
var_datas = VarData.merge(a._get_all_var_data())
if var_datas and var_datas.deps is not None:
deps |= {str(dep) for dep in var_datas.deps}
return deps

@classmethod
def _get_memoized_event_triggers(
cls,
Expand Down Expand Up @@ -2231,6 +2250,11 @@ def _get_memoized_event_triggers(

# Calculate Var dependencies accessed by the handler for useCallback dep array.
var_deps = ["addEvents", "Event"]

# Get deps from event trigger var data.
var_deps.extend(cls._get_deps_from_event_trigger(event))

# Get deps from hooks.
for arg in event_args:
var_data = arg._get_all_var_data()
if var_data is None:
Expand Down
18 changes: 10 additions & 8 deletions reflex/components/core/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

from reflex.components.base.fragment import Fragment
from reflex.components.tags.tag import Tag
from reflex.constants.compiler import Hooks
from reflex.event import EventChain, EventHandler, passthrough_event_spec
from reflex.utils.format import format_prop, wrap
from reflex.utils.imports import ImportVar
from reflex.vars import get_unique_variable_name
from reflex.vars.base import Var
from reflex.vars.base import Var, VarData


class Clipboard(Fragment):
Expand Down Expand Up @@ -72,7 +73,7 @@ def add_imports(self) -> dict[str, ImportVar]:
),
}

def add_hooks(self) -> list[str]:
def add_hooks(self) -> list[str | Var[str]]:
"""Add hook to register paste event listener.
Returns:
Expand All @@ -83,13 +84,14 @@ def add_hooks(self) -> list[str]:
return []
if isinstance(on_paste, EventChain):
on_paste = wrap(str(format_prop(on_paste)).strip("{}"), "(")
hook_expr = f"usePasteHandler({self.targets!s}, {self.on_paste_event_actions!s}, {on_paste!s})"

return [
"usePasteHandler(%s, %s, %s)"
% (
str(self.targets),
str(self.on_paste_event_actions),
on_paste,
)
Var(
hook_expr,
_var_type="str",
_var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER),
),
]


Expand Down
2 changes: 1 addition & 1 deletion reflex/components/core/clipboard.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@ class Clipboard(Fragment):
...

def add_imports(self) -> dict[str, ImportVar]: ...
def add_hooks(self) -> list[str]: ...
def add_hooks(self) -> list[str | Var[str]]: ...

clipboard = Clipboard.create
7 changes: 5 additions & 2 deletions reflex/components/datadisplay/dataeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,11 @@ def add_hooks(self) -> list[str]:
editor_id = get_unique_variable_name()

# Define the name of the getData callback associated with this component and assign to get_cell_content.
data_callback = f"getData_{editor_id}"
self.get_cell_content = Var(_js_expr=data_callback) # type: ignore
if self.get_cell_content is not None:
data_callback = self.get_cell_content._js_expr
else:
data_callback = f"getData_{editor_id}"
self.get_cell_content = Var(_js_expr=data_callback) # type: ignore

code = [f"function {data_callback}([col, row])" "{"]

Expand Down
Loading

0 comments on commit ce153ee

Please sign in to comment.