Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation to function vars #4379

Open
wants to merge 96 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
56f0d63
add typing to function vars
adhami3310 Nov 13, 2024
9b06d68
import ParamSpec from typing_extensions
adhami3310 Nov 13, 2024
7aa9245
remove ellipsis as they are not supported in 3.9
adhami3310 Nov 13, 2024
48951db
try importing everything from extensions
adhami3310 Nov 13, 2024
7ada0ea
special case 3.9
adhami3310 Nov 13, 2024
6745d6c
don't use Any from extensions
adhami3310 Nov 13, 2024
f9b24fe
get typevar from extensions
adhami3310 Nov 13, 2024
05bd41c
add validation
adhami3310 Nov 13, 2024
1e9743d
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Nov 13, 2024
ebc8181
fix silly mistakes
adhami3310 Nov 13, 2024
f4aa1f5
implement type computers
adhami3310 Nov 14, 2024
9d7e353
fix pyright issues
adhami3310 Nov 14, 2024
3cdd209
fix pyright issues outside of vars
adhami3310 Nov 14, 2024
a9db61b
get it right pyright
adhami3310 Nov 14, 2024
2c04153
special case ellipsis types
adhami3310 Nov 14, 2024
5f0546f
what am i doing anymore
adhami3310 Nov 15, 2024
53b9854
default factory
adhami3310 Nov 15, 2024
88cfb3b
handle var at top level
adhami3310 Nov 15, 2024
702670f
add components to var data
adhami3310 Nov 15, 2024
eac54d6
call guess type
adhami3310 Nov 15, 2024
7f1dc7c
remove .bool
adhami3310 Nov 15, 2024
92b1232
would this fix it? no clue
adhami3310 Nov 16, 2024
079cc56
more types
adhami3310 Nov 16, 2024
7d0a4f7
make safe issubclass
adhami3310 Nov 16, 2024
ed1ae0d
add missing return
adhami3310 Nov 16, 2024
2b05ee9
make it handle slice
adhami3310 Nov 16, 2024
2e1bc05
aaaaa
adhami3310 Nov 16, 2024
06eb04f
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Dec 12, 2024
8830d5a
update poetry
adhami3310 Dec 12, 2024
bd2ea5b
update poetry version
adhami3310 Dec 12, 2024
99a3090
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 2, 2025
a5526af
update pyright once again
adhami3310 Jan 2, 2025
d31510c
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 15, 2025
056de9e
poetry update
adhami3310 Jan 15, 2025
2a02e96
make the thing compile again
adhami3310 Jan 15, 2025
45dde00
handle vars with var_type being BaseComponent
adhami3310 Jan 15, 2025
f0f84d5
fix some tests
adhami3310 Jan 16, 2025
f257122
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 16, 2025
990bf13
fix that test
adhami3310 Jan 16, 2025
713f907
silly me
adhami3310 Jan 16, 2025
1aa728e
use infallible guy
adhami3310 Jan 16, 2025
19b6fe9
handle component at largest scope
adhami3310 Jan 16, 2025
d0208e6
change range a bit
adhami3310 Jan 16, 2025
076cfea
fix and and or
adhami3310 Jan 16, 2025
a488fe0
i missed up that
adhami3310 Jan 16, 2025
f42d1f4
fix component state
adhami3310 Jan 16, 2025
4300f33
add wrap components override
adhami3310 Jan 16, 2025
94c9e52
fix convert to component logic
adhami3310 Jan 16, 2025
f9d45d5
fix tests for cond and var
adhami3310 Jan 17, 2025
94b4443
what if we simply didn't have match
adhami3310 Jan 17, 2025
72f1fa7
make the var type actually work
adhami3310 Jan 17, 2025
3d73f56
can't have ohio
adhami3310 Jan 17, 2025
112b2ed
solve some but not all pyright issues
adhami3310 Jan 17, 2025
57d8ea0
down to only two pyright error
adhami3310 Jan 17, 2025
270fcb9
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 17, 2025
0798cb8
fix version python 3.10
adhami3310 Jan 17, 2025
a7230f1
resolve pyright issues
adhami3310 Jan 17, 2025
0e539a2
fix syntax for soy 3.10
adhami3310 Jan 17, 2025
c6f05bb
fix lineno
adhami3310 Jan 17, 2025
9a987ca
dang it darglint
adhami3310 Jan 17, 2025
f4aa122
don't delete thomas code that's rude
adhami3310 Jan 17, 2025
fa6c12e
remove unnecessary comment
adhami3310 Jan 17, 2025
aadd8b5
do silly things
adhami3310 Jan 17, 2025
36af825
remove unused format functions
adhami3310 Jan 18, 2025
00019da
get rid of match class
adhami3310 Jan 18, 2025
b11fc5a
update pyi
adhami3310 Jan 18, 2025
392c5b5
who likes cond
adhami3310 Jan 18, 2025
b7579f4
why not, remove cond
adhami3310 Jan 18, 2025
9e7eeb2
fix imports
adhami3310 Jan 18, 2025
db89a71
reformat color_mode pyi
adhami3310 Jan 18, 2025
b10f6e8
remove even more cond and match
adhami3310 Jan 18, 2025
5d6b51c
what if i deleted rx.foreach
adhami3310 Jan 18, 2025
be92063
dang it darglint
adhami3310 Jan 18, 2025
5e45ca5
remove iterable from jinja
adhami3310 Jan 18, 2025
68b8c12
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 22, 2025
1f9fbd8
resolve merge mistakes
adhami3310 Jan 22, 2025
8173e10
ok we can unbreak foreach just for you
adhami3310 Jan 22, 2025
84d3a2b
unbreak cond why not
adhami3310 Jan 22, 2025
e10cf07
make icon to static methods
adhami3310 Jan 22, 2025
c6e2368
callable vars are not good
adhami3310 Jan 22, 2025
540382d
kill callable var
adhami3310 Jan 22, 2025
b2a27cb
fix list of component is a component
adhami3310 Jan 23, 2025
6a50b3a
i was a bit silly
adhami3310 Jan 23, 2025
19dd15b
Banner components that return Fragment inherit from Fragment
masenf Jan 22, 2025
29fc4b0
make the match logic better
adhami3310 Jan 23, 2025
24f341d
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 23, 2025
6604784
update pydantic requirement
adhami3310 Jan 23, 2025
d3b12a8
optimize var operation output
adhami3310 Jan 23, 2025
0d746bf
handle default args
adhami3310 Jan 23, 2025
2ffa698
improve match and cond checking
adhami3310 Jan 23, 2025
8fab30e
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 31, 2025
46c66b2
lock poetry
adhami3310 Jan 31, 2025
749577f
precommit
adhami3310 Jan 31, 2025
06b751f
remove caching things
adhami3310 Jan 31, 2025
1bb8ba5
Merge branch 'main' into add-validation-to-function-vars
adhami3310 Jan 31, 2025
aef6ace
clear cache on add style recursive
adhami3310 Jan 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
gunicorn = ">=20.1.0,<24.0"
jinja2 = ">=3.1.2,<4.0"
psutil = ">=5.9.4,<7.0"
pydantic = ">=1.10.2,<3.0"
pydantic = ">=1.10.17,<3.0"
python-multipart = ">=0.0.5,<0.1"
python-socketio = ">=5.7.0,<6.0"
redis = ">=4.3.5,<6.0"
Expand Down Expand Up @@ -55,7 +55,7 @@ typing_extensions = ">=4.6.0"
[tool.poetry.group.dev.dependencies]
pytest = ">=7.1.2,<9.0"
pytest-mock = ">=3.10.0,<4.0"
pyright = ">=1.1.392, <1.2"
pyright = ">=1.1.392.post0,<1.2"
darglint = ">=1.8.1,<2.0"
dill = ">=0.3.8"
toml = ">=0.10.2,<1.0"
Expand Down
53 changes: 0 additions & 53 deletions reflex/.templates/jinja/web/pages/utils.js.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
{% filter indent(width=indent_width) %}
{%- if component is not mapping %}
{{- component }}
{%- elif "iterable" in component %}
{{- render_iterable_tag(component) }}
{%- elif component.name == "match"%}
{{- render_match_tag(component) }}
{%- elif "cond" in component %}
{{- render_condition_tag(component) }}
{%- elif component.children|length %}
{{- render_tag(component) }}
{%- else %}
Expand Down Expand Up @@ -44,60 +38,13 @@
{%- endmacro %}


{# Rendering condition component. #}
{# Args: #}
{# component: component dictionary #}
{% macro render_condition_tag(component) %}
{ {{- component.cond_state }} ? (
{{ render(component.true_value) }}
) : (
{{ render(component.false_value) }}
)}
{%- endmacro %}


{# Rendering iterable component. #}
{# Args: #}
{# component: component dictionary #}
{% macro render_iterable_tag(component) %}
<>{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
{% for child in component.children %}
{{ render(child) }}
{% endfor %}
))}</>
{%- endmacro %}


{# Rendering props of a component. #}
{# Args: #}
{# component: component dictionary #}
{% macro render_props(props) %}
{% if props|length %} {{ props|join(" ") }}{% endif %}
{% endmacro %}

{# Rendering Match component. #}
{# Args: #}
{# component: component dictionary #}
{% macro render_match_tag(component) %}
{
(() => {
switch (JSON.stringify({{ component.cond._js_expr }})) {
{% for case in component.match_cases %}
{% for condition in case[:-1] %}
case JSON.stringify({{ condition._js_expr }}):
{% endfor %}
return {{ render(case[-1]) }};
break;
{% endfor %}
default:
return {{ render(component.default) }};
break;
}
})()
}
{%- endmacro %}


{# Rendering content with args. #}
{# Args: #}
{# component: component dictionary #}
Expand Down
60 changes: 30 additions & 30 deletions reflex/.templates/web/utils/helpers/range.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
/**
* Simulate the python range() builtin function.
* inspired by https://dev.to/guyariely/using-python-range-in-javascript-337p
*
*
* If needed outside of an iterator context, use `Array.from(range(10))` or
* spread syntax `[...range(10)]` to get an array.
*
*
* @param {number} start: the start or end of the range.
* @param {number} stop: the end of the range.
* @param {number} step: the step of the range.
* @returns {object} an object with a Symbol.iterator method over the range
*/
export default function range(start, stop, step) {
return {
[Symbol.iterator]() {
if (stop === undefined) {
stop = start;
start = 0;
}
if (step === undefined) {
step = 1;
}

let i = start - step;

return {
next() {
i += step;
if ((step > 0 && i < stop) || (step < 0 && i > stop)) {
return {
value: i,
done: false,
};
}
return {
[Symbol.iterator]() {
if ((stop ?? undefined) === undefined) {
stop = start;
start = 0;
}
if ((step ?? undefined) === undefined) {
step = 1;
}

let i = start - step;

return {
next() {
i += step;
if ((step > 0 && i < stop) || (step < 0 && i > stop)) {
return {
value: undefined,
done: true,
value: i,
done: false,
};
},
};
},
};
}
}
return {
value: undefined,
done: true,
};
},
};
},
};
}
39 changes: 39 additions & 0 deletions reflex/.templates/web/utils/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,45 @@ export const isTrue = (val) => {
return Boolean(val);
};

/**
* Returns a copy of a section of an array.
* @param {Array | string} arrayLike The array to slice.
* @param {[number, number, number]} slice The slice to apply.
* @returns The sliced array.
*/
export const atSlice = (arrayLike, slice) => {
const array = [...arrayLike];
const [startSlice, endSlice, stepSlice] = slice;
if (stepSlice ?? null === null) {
return array.slice(startSlice ?? undefined, endSlice ?? undefined);
}
const step = stepSlice ?? 1;
if (step > 0) {
return array
.slice(startSlice ?? undefined, endSlice ?? undefined)
.filter((_, i) => i % step === 0);
}
const actualStart = (endSlice ?? null) === null ? 0 : endSlice + 1;
const actualEnd =
(startSlice ?? null) === null ? array.length : startSlice + 1;
return array
.slice(actualStart, actualEnd)
.reverse()
.filter((_, i) => i % step === 0);
};

/**
* Get the value at a slice or index.
* @param {Array | string} arrayLike The array to get the value from.
* @param {number | [number, number, number]} sliceOrIndex The slice or index to get the value at.
* @returns The value at the slice or index.
*/
export const atSliceOrIndex = (arrayLike, sliceOrIndex) => {
return Array.isArray(sliceOrIndex)
? atSlice(arrayLike, sliceOrIndex)
: arrayLike.at(sliceOrIndex);
};

/**
* Get the value from a ref.
* @param ref The ref to get the value from.
Expand Down
14 changes: 4 additions & 10 deletions reflex/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@
import os
from typing import TYPE_CHECKING, Any, List, Type

try:
import pydantic.v1.main as pydantic_main
from pydantic.v1 import BaseModel
from pydantic.v1.fields import ModelField
except ModuleNotFoundError:
if not TYPE_CHECKING:
import pydantic.main as pydantic_main
from pydantic import BaseModel
from pydantic.fields import ModelField
import pydantic.v1.main as pydantic_main
from pydantic.v1 import BaseModel
from pydantic.v1.fields import ModelField


def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None:
Expand Down Expand Up @@ -50,7 +44,7 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
from reflex.vars import Var


class Base(BaseModel): # pyright: ignore [reportPossiblyUnboundVariable]
class Base(BaseModel):
"""The base class subclassed by all Reflex classes.

This class wraps Pydantic and provides common methods such as
Expand Down
34 changes: 25 additions & 9 deletions reflex/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Optional, Tuple, Type, Union

from reflex import constants
from reflex.compiler import templates, utils
Expand Down Expand Up @@ -538,6 +538,29 @@ def purge_web_pages_dir():
if TYPE_CHECKING:
from reflex.app import UnevaluatedPage

COMPONENT_TYPE = Union[Component, Var, Tuple[Union[Component, Var], ...]]
COMPONENT_TYPE_OR_CALLABLE = Union[COMPONENT_TYPE, Callable[[], COMPONENT_TYPE]]


def componentify_unevaluated(
possible_component: COMPONENT_TYPE_OR_CALLABLE,
) -> Component:
"""Convert a possible component to a component.

Args:
possible_component: The possible component to convert.

Returns:
The component.
"""
if isinstance(possible_component, Var):
return Fragment.create(possible_component)
if isinstance(possible_component, tuple):
return Fragment.create(*possible_component)
if isinstance(possible_component, Component):
return possible_component
return componentify_unevaluated(possible_component())


def compile_unevaluated_page(
route: str,
Expand All @@ -559,12 +582,7 @@ def compile_unevaluated_page(
The compiled component and whether state should be enabled.
"""
# Generate the component if it is a callable.
component = page.component
component = component if isinstance(component, Component) else component()

# unpack components that return tuples in an rx.fragment.
if isinstance(component, tuple):
component = Fragment.create(*component)
component = componentify_unevaluated(page.component)

component._add_style_recursive(style or {}, theme)

Expand Down Expand Up @@ -671,8 +689,6 @@ def compile_unevaluated_page(
component, enable_state = compile_unevaluated_page(
route, cls.UNCOMPILED_PAGES[route]
)
component = component if isinstance(component, Component) else component()
component._add_style_recursive(style, theme)
return route, component, compile_page(route, component, cls.STATE)

@classmethod
Expand Down
Loading
Loading