From 5b802c2c9e4827e7fa0a7957f4569634ad9a479c Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 15 Oct 2024 10:54:14 -0700 Subject: [PATCH 01/27] bump version to 0.6.4dev1 for further development (#4178) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 17ee4d132..ae636a1c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "reflex" -version = "0.6.3dev1" +version = "0.6.4dev1" description = "Web apps in pure Python." license = "Apache-2.0" authors = [ From 2018be8e085fe59863bcb7ffa397cedb6f531689 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 15 Oct 2024 12:21:03 -0700 Subject: [PATCH 02/27] only treat dict object vars as key value mapping (#4177) --- reflex/style.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reflex/style.py b/reflex/style.py index 4ebd42ac3..8e24e9b6b 100644 --- a/reflex/style.py +++ b/reflex/style.py @@ -10,6 +10,7 @@ from reflex.event import EventChain, EventHandler from reflex.utils import format from reflex.utils.exceptions import ReflexError from reflex.utils.imports import ImportVar +from reflex.utils.types import get_origin from reflex.vars import VarData from reflex.vars.base import CallableVar, LiteralVar, Var from reflex.vars.function import FunctionVar @@ -196,6 +197,10 @@ def convert( isinstance(value, Breakpoints) and all(not isinstance(v, dict) for v in value.values()) ) + or ( + isinstance(value, ObjectVar) + and not issubclass(get_origin(value._var_type) or value._var_type, dict) + ) else (key,) ) From 7565ae15efa83ebe204eaacdad13a613ceebc36f Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 16 Oct 2024 11:31:05 -0700 Subject: [PATCH 03/27] disk is memory is disk (#4185) --- reflex/state.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/state.py b/reflex/state.py index 7b338b4d6..0d6eed0ed 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2566,9 +2566,9 @@ class StateManager(Base, ABC): The state manager (either disk, memory or redis). """ config = get_config() - if config.state_manager_mode == constants.StateManagerMode.DISK: - return StateManagerMemory(state=state) if config.state_manager_mode == constants.StateManagerMode.MEMORY: + return StateManagerMemory(state=state) + if config.state_manager_mode == constants.StateManagerMode.DISK: return StateManagerDisk(state=state) if config.state_manager_mode == constants.StateManagerMode.REDIS: redis = prerequisites.get_redis() From 4422515f4baca8d1a6ef1822f889792cd5cf2f85 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 16 Oct 2024 11:35:09 -0700 Subject: [PATCH 04/27] LiteralEventChainVar becomes an ArgsFunctionOperation (#4174) * LiteralEventChainVar becomes an ArgsFunctionOperation Instead of using the ArgsFunctionOperation to create the string representation of the _js_expr, make the identity of the var an ArgsFunctionOperation so the _args_names and _return_expr remain accessible. Rely on the default behavior of ArgsFunctionOperation to create the _cached_var_name / _js_expr value. This allows the compat shim in `format_event_chain` to remain functional, as it does special handling for ArgsFunctionOperation to retain the previous behavior of that function (this was a regression introduced in 0.6.2). * _var_type is EventChain; fix parent class order * Re-fix LiteralEventChainVar inheritence list w/ comment * [ENG-3942] LiteralEventVar becomes VarCallOperation instead of using `.call` when constructing the `_js_expr`, have the identity of a LiteralEventVar as a VarCallOperation to take advantage of the _var_data carrying. * add event overlords * EventCallback descriptor always returns EventSpec from class Relax actual `__get__` definition to support the multitude of overloads * test case for event related vars carrying _var_data --------- Co-authored-by: Khaleel Al-Adhami --- reflex/event.py | 173 +++++++++++++++++++++++--------------- tests/units/test_event.py | 38 ++++++++- 2 files changed, 138 insertions(+), 73 deletions(-) diff --git a/reflex/event.py b/reflex/event.py index 04879add3..4b0bf96e2 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -22,6 +22,7 @@ from typing import ( TypeVar, Union, get_type_hints, + overload, ) from typing_extensions import ParamSpec, get_args, get_origin @@ -32,14 +33,17 @@ from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch from reflex.utils.types import ArgsSpec, GenericType from reflex.vars import VarData from reflex.vars.base import ( - CachedVarOperation, LiteralNoneVar, LiteralVar, ToOperation, Var, - cached_property_no_lock, ) -from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar, FunctionVar +from reflex.vars.function import ( + ArgsFunctionOperation, + FunctionStringVar, + FunctionVar, + VarOperationCall, +) from reflex.vars.object import ObjectVar try: @@ -1258,7 +1262,7 @@ class EventVar(ObjectVar): frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar): +class LiteralEventVar(VarOperationCall, LiteralVar, EventVar): """A literal event var.""" _var_value: EventSpec = dataclasses.field(default=None) # type: ignore @@ -1271,35 +1275,6 @@ class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar): """ return hash((self.__class__.__name__, self._js_expr)) - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. - - Returns: - The name of the var. - """ - return str( - FunctionStringVar("Event").call( - # event handler name - ".".join( - filter( - None, - format.get_event_handler_parts(self._var_value.handler), - ) - ), - # event handler args - {str(name): value for name, value in self._var_value.args}, - # event actions - self._var_value.event_actions, - # client handler name - *( - [self._var_value.client_handler_name] - if self._var_value.client_handler_name - else [] - ), - ) - ) - @classmethod def create( cls, @@ -1320,6 +1295,22 @@ class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar): _var_type=EventSpec, _var_data=_var_data, _var_value=value, + _func=FunctionStringVar("Event"), + _args=( + # event handler name + ".".join( + filter( + None, + format.get_event_handler_parts(value.handler), + ) + ), + # event handler args + {str(name): value for name, value in value.args}, + # event actions + value.event_actions, + # client handler name + *([value.client_handler_name] if value.client_handler_name else []), + ), ) @@ -1332,7 +1323,10 @@ class EventChainVar(FunctionVar): frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar): +# Note: LiteralVar is second in the inheritance list allowing it act like a +# CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the +# _cached_var_name property. +class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar): """A literal event chain var.""" _var_value: EventChain = dataclasses.field(default=None) # type: ignore @@ -1345,41 +1339,6 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar): """ return hash((self.__class__.__name__, self._js_expr)) - @cached_property_no_lock - def _cached_var_name(self) -> str: - """The name of the var. - - Returns: - The name of the var. - """ - sig = inspect.signature(self._var_value.args_spec) # type: ignore - if sig.parameters: - arg_def = tuple((f"_{p}" for p in sig.parameters)) - arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def]) - else: - # add a default argument for addEvents if none were specified in value.args_spec - # used to trigger the preventDefault() on the event. - arg_def = ("...args",) - arg_def_expr = Var(_js_expr="args") - - if self._var_value.invocation is None: - invocation = FunctionStringVar.create("addEvents") - else: - invocation = self._var_value.invocation - - return str( - ArgsFunctionOperation.create( - arg_def, - invocation.call( - LiteralVar.create( - [LiteralVar.create(event) for event in self._var_value.events] - ), - arg_def_expr, - self._var_value.event_actions, - ), - ) - ) - @classmethod def create( cls, @@ -1395,10 +1354,31 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar): Returns: The created LiteralEventChainVar instance. """ + sig = inspect.signature(value.args_spec) # type: ignore + if sig.parameters: + arg_def = tuple((f"_{p}" for p in sig.parameters)) + arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def]) + else: + # add a default argument for addEvents if none were specified in value.args_spec + # used to trigger the preventDefault() on the event. + arg_def = ("...args",) + arg_def_expr = Var(_js_expr="args") + + if value.invocation is None: + invocation = FunctionStringVar.create("addEvents") + else: + invocation = value.invocation + return cls( _js_expr="", _var_type=EventChain, _var_data=_var_data, + _args_names=arg_def, + _return_expr=invocation.call( + LiteralVar.create([LiteralVar.create(event) for event in value.events]), + arg_def_expr, + value.event_actions, + ), _var_value=value, ) @@ -1437,6 +1417,11 @@ EventType = Union[IndividualEventType[G], List[IndividualEventType[G]]] P = ParamSpec("P") T = TypeVar("T") +V = TypeVar("V") +V2 = TypeVar("V2") +V3 = TypeVar("V3") +V4 = TypeVar("V4") +V5 = TypeVar("V5") if sys.version_info >= (3, 10): from typing import Concatenate @@ -1452,7 +1437,55 @@ if sys.version_info >= (3, 10): """ self.func = func - def __get__(self, instance, owner) -> Callable[P, T]: + @overload + def __get__( + self: EventCallback[[V], T], instance: None, owner + ) -> Callable[[Union[Var[V], V]], EventSpec]: ... + + @overload + def __get__( + self: EventCallback[[V, V2], T], instance: None, owner + ) -> Callable[[Union[Var[V], V], Union[Var[V2], V2]], EventSpec]: ... + + @overload + def __get__( + self: EventCallback[[V, V2, V3], T], instance: None, owner + ) -> Callable[ + [Union[Var[V], V], Union[Var[V2], V2], Union[Var[V3], V3]], + EventSpec, + ]: ... + + @overload + def __get__( + self: EventCallback[[V, V2, V3, V4], T], instance: None, owner + ) -> Callable[ + [ + Union[Var[V], V], + Union[Var[V2], V2], + Union[Var[V3], V3], + Union[Var[V4], V4], + ], + EventSpec, + ]: ... + + @overload + def __get__( + self: EventCallback[[V, V2, V3, V4, V5], T], instance: None, owner + ) -> Callable[ + [ + Union[Var[V], V], + Union[Var[V2], V2], + Union[Var[V3], V3], + Union[Var[V4], V4], + Union[Var[V5], V5], + ], + EventSpec, + ]: ... + + @overload + def __get__(self, instance, owner) -> Callable[P, T]: ... + + def __get__(self, instance, owner) -> Callable: """Get the function with the instance bound to it. Args: diff --git a/tests/units/test_event.py b/tests/units/test_event.py index 3996a6101..d7b7cf7a2 100644 --- a/tests/units/test_event.py +++ b/tests/units/test_event.py @@ -2,11 +2,18 @@ from typing import List import pytest -from reflex import event -from reflex.event import Event, EventHandler, EventSpec, call_event_handler, fix_events +from reflex.event import ( + Event, + EventChain, + EventHandler, + EventSpec, + call_event_handler, + event, + fix_events, +) from reflex.state import BaseState from reflex.utils import format -from reflex.vars.base import LiteralVar, Var +from reflex.vars.base import Field, LiteralVar, Var, field def make_var(value) -> Var: @@ -388,3 +395,28 @@ def test_event_actions_on_state(): assert sp_handler.event_actions == {"stopPropagation": True} # should NOT affect other references to the handler assert not handler.event_actions + + +def test_event_var_data(): + class S(BaseState): + x: Field[int] = field(0) + + @event + def s(self, value: int): + pass + + # Handler doesn't have any _var_data because it's just a str + handler_var = Var.create(S.s) + assert handler_var._get_all_var_data() is None + + # Ensure spec carries _var_data + spec_var = Var.create(S.s(S.x)) + assert spec_var._get_all_var_data() == S.x._get_all_var_data() + + # Needed to instantiate the EventChain + def _args_spec(value: Var[int]) -> tuple[Var[int]]: + return (value,) + + # Ensure chain carries _var_data + chain_var = Var.create(EventChain(events=[S.s(S.x)], args_spec=_args_spec)) + assert chain_var._get_all_var_data() == S.x._get_all_var_data() From da9d7eabddfeda76291f0a1e425b62250e2528e5 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 16 Oct 2024 11:35:27 -0700 Subject: [PATCH 05/27] Arbitrary arg access two levels deep for untyped handler (#4180) * Arbitrary arg access two levels deep for untyped handler Provide drop-in compatibility with existing component wrapping code that was accessing attributes on the default handler arg type. * py3.9 compat --- reflex/event.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/event.py b/reflex/event.py index 4b0bf96e2..58053408d 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -1045,7 +1045,8 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str): deprecation_version="0.6.3", removal_version="0.7.0", ) - return JavascriptInputEvent + # Allow arbitrary attribute access two levels deep until removed. + return Dict[str, dict] return annotation From 13c909434386f00a3db4669b496da3980b0d4b48 Mon Sep 17 00:00:00 2001 From: Alek Petuskey Date: Wed, 16 Oct 2024 11:35:56 -0700 Subject: [PATCH 06/27] Remove demo command (#4176) * Remove demo command * Format --------- Co-authored-by: Alek Petuskey --- reflex/reflex.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/reflex/reflex.py b/reflex/reflex.py index 4608ed171..bd6904d06 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -4,7 +4,6 @@ from __future__ import annotations import atexit import os -import webbrowser from pathlib import Path from typing import List, Optional @@ -586,18 +585,6 @@ def deploy( ) -@cli.command() -def demo( - frontend_port: str = typer.Option( - "3001", help="Specify a different frontend port." - ), - backend_port: str = typer.Option("8001", help="Specify a different backend port."), -): - """Run the demo app.""" - # Open the demo app in a terminal. - webbrowser.open("https://demo.reflex.run") - - cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.") cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.") cli.add_typer( From d8ea2fc79543d4abaeec09839f83a3e4219415ac Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 16 Oct 2024 13:39:49 -0700 Subject: [PATCH 07/27] fix pyi for untyped event handlers (#4186) * fix pyi for untyped event handlers * no more empty lambdas --- reflex/components/base/error_boundary.pyi | 2 +- reflex/components/component.py | 35 +++++++------- reflex/components/datadisplay/dataeditor.pyi | 16 +++---- reflex/components/react_player/audio.pyi | 2 +- .../components/react_player/react_player.pyi | 2 +- reflex/components/react_player/video.pyi | 2 +- reflex/components/recharts/cartesian.py | 6 +-- reflex/components/recharts/charts.py | 22 ++++----- reflex/components/recharts/polar.py | 48 +++++++++---------- reflex/components/suneditor/editor.pyi | 20 ++++---- reflex/event.py | 10 ++-- reflex/utils/pyi_generator.py | 2 +- 12 files changed, 84 insertions(+), 83 deletions(-) diff --git a/reflex/components/base/error_boundary.pyi b/reflex/components/base/error_boundary.pyi index 65109b0fe..aaf5584e4 100644 --- a/reflex/components/base/error_boundary.pyi +++ b/reflex/components/base/error_boundary.pyi @@ -31,7 +31,7 @@ class ErrorBoundary(Component): on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_error: Optional[EventType[[]]] = None, + on_error: Optional[EventType] = None, on_focus: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, diff --git a/reflex/components/component.py b/reflex/components/component.py index 364353b9d..a0d9c93b0 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -45,6 +45,7 @@ from reflex.event import ( EventVar, call_event_fn, call_event_handler, + empty_event, get_handler_args, ) from reflex.style import Style, format_as_emotion @@ -623,21 +624,21 @@ class Component(BaseComponent, ABC): """ default_triggers = { - EventTriggers.ON_FOCUS: lambda: [], - EventTriggers.ON_BLUR: lambda: [], - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_CONTEXT_MENU: lambda: [], - EventTriggers.ON_DOUBLE_CLICK: lambda: [], - EventTriggers.ON_MOUSE_DOWN: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], - EventTriggers.ON_MOUSE_MOVE: lambda: [], - EventTriggers.ON_MOUSE_OUT: lambda: [], - EventTriggers.ON_MOUSE_OVER: lambda: [], - EventTriggers.ON_MOUSE_UP: lambda: [], - EventTriggers.ON_SCROLL: lambda: [], - EventTriggers.ON_MOUNT: lambda: [], - EventTriggers.ON_UNMOUNT: lambda: [], + EventTriggers.ON_FOCUS: empty_event, + EventTriggers.ON_BLUR: empty_event, + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_CONTEXT_MENU: empty_event, + EventTriggers.ON_DOUBLE_CLICK: empty_event, + EventTriggers.ON_MOUSE_DOWN: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, + EventTriggers.ON_MOUSE_MOVE: empty_event, + EventTriggers.ON_MOUSE_OUT: empty_event, + EventTriggers.ON_MOUSE_OVER: empty_event, + EventTriggers.ON_MOUSE_UP: empty_event, + EventTriggers.ON_SCROLL: empty_event, + EventTriggers.ON_MOUNT: empty_event, + EventTriggers.ON_UNMOUNT: empty_event, } # Look for component specific triggers, @@ -648,7 +649,7 @@ class Component(BaseComponent, ABC): annotation = field.annotation if (metadata := getattr(annotation, "__metadata__", None)) is not None: args_spec = metadata[0] - default_triggers[field.name] = args_spec or (lambda: []) + default_triggers[field.name] = args_spec or (empty_event) # type: ignore return default_triggers def __repr__(self) -> str: @@ -1705,7 +1706,7 @@ class CustomComponent(Component): value = self._create_event_chain( value=value, args_spec=event_triggers_in_component_declaration.get( - key, lambda: [] + key, empty_event ), ) self.props[format.to_camel_case(key)] = value diff --git a/reflex/components/datadisplay/dataeditor.pyi b/reflex/components/datadisplay/dataeditor.pyi index b3a9ce2b3..1b8fed287 100644 --- a/reflex/components/datadisplay/dataeditor.pyi +++ b/reflex/components/datadisplay/dataeditor.pyi @@ -139,20 +139,20 @@ class DataEditor(NoSSRComponent): on_cell_activated: Optional[EventType] = None, on_cell_clicked: Optional[EventType] = None, on_cell_context_menu: Optional[EventType] = None, - on_cell_edited: Optional[EventType[[]]] = None, + on_cell_edited: Optional[EventType] = None, on_click: Optional[EventType[[]]] = None, - on_column_resize: Optional[EventType[[]]] = None, + on_column_resize: Optional[EventType] = None, on_context_menu: Optional[EventType[[]]] = None, - on_delete: Optional[EventType[[]]] = None, + on_delete: Optional[EventType] = None, on_double_click: Optional[EventType[[]]] = None, - on_finished_editing: Optional[EventType[[]]] = None, + on_finished_editing: Optional[EventType] = None, on_focus: Optional[EventType[[]]] = None, - on_group_header_clicked: Optional[EventType[[]]] = None, - on_group_header_context_menu: Optional[EventType[[]]] = None, - on_group_header_renamed: Optional[EventType[[]]] = None, + on_group_header_clicked: Optional[EventType] = None, + on_group_header_context_menu: Optional[EventType] = None, + on_group_header_renamed: Optional[EventType] = None, on_header_clicked: Optional[EventType] = None, on_header_context_menu: Optional[EventType] = None, - on_header_menu_click: Optional[EventType[[]]] = None, + on_header_menu_click: Optional[EventType] = None, on_item_hovered: Optional[EventType] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, diff --git a/reflex/components/react_player/audio.pyi b/reflex/components/react_player/audio.pyi index 4b566d898..d1f29f508 100644 --- a/reflex/components/react_player/audio.pyi +++ b/reflex/components/react_player/audio.pyi @@ -58,7 +58,7 @@ class Audio(ReactPlayer): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType[[]]] = None, + on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType] = None, diff --git a/reflex/components/react_player/react_player.pyi b/reflex/components/react_player/react_player.pyi index 1977eaa00..940b09e51 100644 --- a/reflex/components/react_player/react_player.pyi +++ b/reflex/components/react_player/react_player.pyi @@ -56,7 +56,7 @@ class ReactPlayer(NoSSRComponent): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType[[]]] = None, + on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType] = None, diff --git a/reflex/components/react_player/video.pyi b/reflex/components/react_player/video.pyi index 6e4047d06..a50ccf71f 100644 --- a/reflex/components/react_player/video.pyi +++ b/reflex/components/react_player/video.pyi @@ -58,7 +58,7 @@ class Video(ReactPlayer): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType[[]]] = None, + on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType] = None, diff --git a/reflex/components/recharts/cartesian.py b/reflex/components/recharts/cartesian.py index 153d3fb2a..865b50a32 100644 --- a/reflex/components/recharts/cartesian.py +++ b/reflex/components/recharts/cartesian.py @@ -252,7 +252,7 @@ class Brush(Recharts): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_CHANGE: lambda: [], + EventTriggers.ON_CHANGE: empty_event, } @@ -293,10 +293,10 @@ class Cartesian(Recharts): name: Var[Union[str, int]] # The customized event handler of animation start - on_animation_start: EventHandler[lambda: []] + on_animation_start: EventHandler[empty_event] # The customized event handler of animation end - on_animation_end: EventHandler[lambda: []] + on_animation_end: EventHandler[empty_event] # The customized event handler of click on the component in this group on_click: EventHandler[empty_event] diff --git a/reflex/components/recharts/charts.py b/reflex/components/recharts/charts.py index d4785f6c4..d7e07b2d8 100644 --- a/reflex/components/recharts/charts.py +++ b/reflex/components/recharts/charts.py @@ -330,9 +330,9 @@ class RadarChart(ChartBase): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, } @@ -419,14 +419,14 @@ class ScatterChart(ChartBase): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_MOUSE_DOWN: lambda: [], - EventTriggers.ON_MOUSE_UP: lambda: [], - EventTriggers.ON_MOUSE_MOVE: lambda: [], - EventTriggers.ON_MOUSE_OVER: lambda: [], - EventTriggers.ON_MOUSE_OUT: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_MOUSE_DOWN: empty_event, + EventTriggers.ON_MOUSE_UP: empty_event, + EventTriggers.ON_MOUSE_MOVE: empty_event, + EventTriggers.ON_MOUSE_OVER: empty_event, + EventTriggers.ON_MOUSE_OUT: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, } diff --git a/reflex/components/recharts/polar.py b/reflex/components/recharts/polar.py index bcbd5abd3..ccb96f180 100644 --- a/reflex/components/recharts/polar.py +++ b/reflex/components/recharts/polar.py @@ -103,14 +103,14 @@ class Pie(Recharts): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_ANIMATION_START: lambda: [], - EventTriggers.ON_ANIMATION_END: lambda: [], - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_MOUSE_MOVE: lambda: [], - EventTriggers.ON_MOUSE_OVER: lambda: [], - EventTriggers.ON_MOUSE_OUT: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], + EventTriggers.ON_ANIMATION_START: empty_event, + EventTriggers.ON_ANIMATION_END: empty_event, + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_MOUSE_MOVE: empty_event, + EventTriggers.ON_MOUSE_OVER: empty_event, + EventTriggers.ON_MOUSE_OUT: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, } @@ -167,8 +167,8 @@ class Radar(Recharts): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_ANIMATION_START: lambda: [], - EventTriggers.ON_ANIMATION_END: lambda: [], + EventTriggers.ON_ANIMATION_START: empty_event, + EventTriggers.ON_ANIMATION_END: empty_event, } @@ -219,14 +219,14 @@ class RadialBar(Recharts): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_MOUSE_MOVE: lambda: [], - EventTriggers.ON_MOUSE_OVER: lambda: [], - EventTriggers.ON_MOUSE_OUT: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], - EventTriggers.ON_ANIMATION_START: lambda: [], - EventTriggers.ON_ANIMATION_END: lambda: [], + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_MOUSE_MOVE: empty_event, + EventTriggers.ON_MOUSE_OVER: empty_event, + EventTriggers.ON_MOUSE_OUT: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, + EventTriggers.ON_ANIMATION_START: empty_event, + EventTriggers.ON_ANIMATION_END: empty_event, } @@ -392,12 +392,12 @@ class PolarRadiusAxis(Recharts): A dict mapping the event trigger to the var that is passed to the handler. """ return { - EventTriggers.ON_CLICK: lambda: [], - EventTriggers.ON_MOUSE_MOVE: lambda: [], - EventTriggers.ON_MOUSE_OVER: lambda: [], - EventTriggers.ON_MOUSE_OUT: lambda: [], - EventTriggers.ON_MOUSE_ENTER: lambda: [], - EventTriggers.ON_MOUSE_LEAVE: lambda: [], + EventTriggers.ON_CLICK: empty_event, + EventTriggers.ON_MOUSE_MOVE: empty_event, + EventTriggers.ON_MOUSE_OVER: empty_event, + EventTriggers.ON_MOUSE_OUT: empty_event, + EventTriggers.ON_MOUSE_ENTER: empty_event, + EventTriggers.ON_MOUSE_LEAVE: empty_event, } diff --git a/reflex/components/suneditor/editor.pyi b/reflex/components/suneditor/editor.pyi index bcc0b64ac..f7149a02c 100644 --- a/reflex/components/suneditor/editor.pyi +++ b/reflex/components/suneditor/editor.pyi @@ -122,16 +122,16 @@ class Editor(NoSSRComponent): class_name: Optional[Any] = None, autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType[[]]] = None, + on_blur: Optional[EventType] = None, + on_change: Optional[EventType] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, - on_copy: Optional[EventType[[]]] = None, - on_cut: Optional[EventType[[]]] = None, + on_copy: Optional[EventType] = None, + on_cut: Optional[EventType] = None, on_double_click: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None, - on_input: Optional[EventType[[]]] = None, - on_load: Optional[EventType[[]]] = None, + on_input: Optional[EventType] = None, + on_load: Optional[EventType] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, on_mouse_enter: Optional[EventType[[]]] = None, @@ -140,12 +140,12 @@ class Editor(NoSSRComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_paste: Optional[EventType[[]]] = None, - on_resize_editor: Optional[EventType[[]]] = None, + on_paste: Optional[EventType] = None, + on_resize_editor: Optional[EventType] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, - toggle_code_view: Optional[EventType[[]]] = None, - toggle_full_screen: Optional[EventType[[]]] = None, + toggle_code_view: Optional[EventType] = None, + toggle_full_screen: Optional[EventType] = None, **props, ) -> "Editor": """Create an instance of Editor. No children allowed. diff --git a/reflex/event.py b/reflex/event.py index 58053408d..f93bc63d2 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -399,11 +399,6 @@ class EventChain(EventActionsMixin): invocation: Optional[Var] = dataclasses.field(default=None) -# These chains can be used for their side effects when no other events are desired. -stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation -prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default - - @dataclasses.dataclass( init=True, frozen=True, @@ -467,6 +462,11 @@ def empty_event() -> Tuple[()]: return tuple() # type: ignore +# These chains can be used for their side effects when no other events are desired. +stop_propagation = EventChain(events=[], args_spec=empty_event).stop_propagation +prevent_default = EventChain(events=[], args_spec=empty_event).prevent_default + + T = TypeVar("T") diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index 1df8e6fa0..fd76576b9 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -429,7 +429,7 @@ def _generate_component_create_functiondef( def figure_out_return_type(annotation: Any): if inspect.isclass(annotation) and issubclass(annotation, inspect._empty): - return ast.Name(id="Optional[EventType[[]]]") + return ast.Name(id="Optional[EventType]") if isinstance(annotation, str) and annotation.startswith("Tuple["): inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]") From 35810fe1bd3935e4df1f8246b98163a6517acbd2 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 16 Oct 2024 15:20:51 -0700 Subject: [PATCH 08/27] use larger or equal for node version check (#4189) --- reflex/utils/prerequisites.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 34ed5f53f..44d1f6acc 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -146,14 +146,9 @@ def check_node_version() -> bool: Whether the version of Node.js is valid. """ current_version = get_node_version() - if current_version: - # Compare the version numbers - return ( - current_version >= version.parse(constants.Node.MIN_VERSION) - if constants.IS_WINDOWS or path_ops.use_system_node() - else current_version == version.parse(constants.Node.VERSION) - ) - return False + return current_version is not None and current_version >= version.parse( + constants.Node.MIN_VERSION + ) def get_node_version() -> version.Version | None: From 101fb1b5405e2aa20e91efa21ecb77068e7e3fd4 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 16 Oct 2024 15:21:47 -0700 Subject: [PATCH 09/27] check for none before returning which (#4187) --- reflex/utils/path_ops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index f597e0075..f795e1aa4 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -185,7 +185,8 @@ def get_node_path() -> str | None: """ node_path = Path(constants.Node.PATH) if use_system_node() or not node_path.exists(): - return str(which("node")) + system_node_path = which("node") + return str(system_node_path) if system_node_path else None return str(node_path) @@ -197,7 +198,8 @@ def get_npm_path() -> str | None: """ npm_path = Path(constants.Node.NPM_PATH) if use_system_node() or not npm_path.exists(): - return str(which("npm")) + system_npm_path = which("npm") + return str(system_npm_path) if system_npm_path else None return str(npm_path) From e14c79d57d8f96b62a1da122f706d6a601a3fd62 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 17 Oct 2024 16:16:24 -0700 Subject: [PATCH 10/27] [ENG-3954] Treat ArrayVar.foreach index as int (#4193) * [ENG-3954] Treat ArrayVar.foreach index as int * foreach: convert return value to a Var When the value returned from the foreach is not hashable (mutable type), then it will raise an exception if it is not first converted to a LiteralVar. --- reflex/vars/sequence.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 9b65507b7..e3b422f35 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -1155,7 +1155,7 @@ class ArrayVar(Var[ARRAY_VAR_TYPE]): function_var = ArgsFunctionOperation.create(tuple(), return_value) else: # generic number var - number_var = Var("").to(NumberVar) + number_var = Var("").to(NumberVar, int) first_arg_type = self[number_var]._var_type @@ -1167,7 +1167,10 @@ class ArrayVar(Var[ARRAY_VAR_TYPE]): _var_type=first_arg_type, ).guess_type() - function_var = ArgsFunctionOperation.create((arg_name,), fn(first_arg)) + function_var = ArgsFunctionOperation.create( + (arg_name,), + Var.create(fn(first_arg)), + ) return map_array_operation(self, function_var) From 330c280c7857a2cf5ac09fa8520f5510514dfa13 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 17 Oct 2024 16:54:36 -0700 Subject: [PATCH 11/27] When REDIS_URL is set, use redis, regardless of config preference. (#4196) We might change this down the road, but we don't want to introduce a breaking change at this time. --- reflex/state.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reflex/state.py b/reflex/state.py index 0d6eed0ed..9740bddaa 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2566,6 +2566,8 @@ class StateManager(Base, ABC): The state manager (either disk, memory or redis). """ config = get_config() + if prerequisites.parse_redis_url() is not None: + config.state_manager_mode = constants.StateManagerMode.REDIS if config.state_manager_mode == constants.StateManagerMode.MEMORY: return StateManagerMemory(state=state) if config.state_manager_mode == constants.StateManagerMode.DISK: From 6cb87a812f5dbac7745ec2f1dae4373980206743 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 17 Oct 2024 19:21:43 -0700 Subject: [PATCH 12/27] Fix runtime error on python 3.11.0 (#4197) All generic types present in a Union must be parametrized on 3.11.0 if any other generic types in the union are parametrized. This appears to be a bug in 3.11.0, as the behavior is not observed in 3.11.1 or 3.10; fixing here as this is technically more correct anyway and avoids a crash. --- reflex/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/event.py b/reflex/event.py index f93bc63d2..8291e3465 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -1412,7 +1412,7 @@ class ToEventChainVarOperation(ToOperation, EventChainVar): G = ParamSpec("G") -IndividualEventType = Union[EventSpec, EventHandler, Callable[G, Any], Var] +IndividualEventType = Union[EventSpec, EventHandler, Callable[G, Any], Var[Any]] EventType = Union[IndividualEventType[G], List[IndividualEventType[G]]] From fcc97b04029fdf4063fc648a00f5673d22c4b326 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 12:11:00 -0700 Subject: [PATCH 13/27] upgrade node to latest lts (#4191) --- reflex/constants/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index b12d56c78..9acc109ac 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -80,7 +80,7 @@ class Node(SimpleNamespace): """Node/ NPM constants.""" # The Node version. - VERSION = "20.18.0" + VERSION = "22.10.0" # The minimum required node version. MIN_VERSION = "18.17.0" From 7560bf6429487ef4b0f64096b341ae8b1a27ab76 Mon Sep 17 00:00:00 2001 From: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:26:09 +0200 Subject: [PATCH 14/27] allow setting run mode via env, add helpers to determine it (#4168) --- reflex/constants/__init__.py | 2 ++ reflex/constants/base.py | 3 +++ reflex/reflex.py | 18 ++++++++++++++++-- reflex/utils/exec.py | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/reflex/constants/__init__.py b/reflex/constants/__init__.py index 8e61a3717..a540805b5 100644 --- a/reflex/constants/__init__.py +++ b/reflex/constants/__init__.py @@ -2,6 +2,8 @@ from .base import ( COOKIES, + ENV_BACKEND_ONLY_ENV_VAR, + ENV_FRONTEND_ONLY_ENV_VAR, ENV_MODE_ENV_VAR, IS_WINDOWS, LOCAL_STORAGE, diff --git a/reflex/constants/base.py b/reflex/constants/base.py index b86f083cc..070ff7724 100644 --- a/reflex/constants/base.py +++ b/reflex/constants/base.py @@ -226,6 +226,9 @@ SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE" # This env var stores the execution mode of the app ENV_MODE_ENV_VAR = "REFLEX_ENV_MODE" +ENV_BACKEND_ONLY_ENV_VAR = "REFLEX_BACKEND_ONLY" +ENV_FRONTEND_ONLY_ENV_VAR = "REFLEX_FRONTEND_ONLY" + # Testing variables. # Testing os env set by pytest when running a test case. PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST" diff --git a/reflex/reflex.py b/reflex/reflex.py index bd6904d06..99850a9d2 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -274,9 +274,17 @@ def run( constants.Env.DEV, help="The environment to run the app in." ), frontend: bool = typer.Option( - False, "--frontend-only", help="Execute only frontend." + False, + "--frontend-only", + help="Execute only frontend.", + envvar=constants.ENV_FRONTEND_ONLY_ENV_VAR, + ), + backend: bool = typer.Option( + False, + "--backend-only", + help="Execute only backend.", + envvar=constants.ENV_BACKEND_ONLY_ENV_VAR, ), - backend: bool = typer.Option(False, "--backend-only", help="Execute only backend."), frontend_port: str = typer.Option( config.frontend_port, help="Specify a different frontend port." ), @@ -291,6 +299,12 @@ def run( ), ): """Run the app in the current directory.""" + if frontend and backend: + console.error("Cannot use both --frontend-only and --backend-only options.") + raise typer.Exit(1) + os.environ[constants.ENV_BACKEND_ONLY_ENV_VAR] = str(backend).lower() + os.environ[constants.ENV_FRONTEND_ONLY_ENV_VAR] = str(frontend).lower() + _run(env, frontend, backend, frontend_port, backend_port, backend_host, loglevel) diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index acb69ee19..6fa67a96f 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -496,6 +496,24 @@ def is_prod_mode() -> bool: return current_mode == constants.Env.PROD.value +def is_frontend_only() -> bool: + """Check if the app is running in frontend-only mode. + + Returns: + True if the app is running in frontend-only mode. + """ + return os.environ.get(constants.ENV_FRONTEND_ONLY_ENV_VAR, "").lower() == "true" + + +def is_backend_only() -> bool: + """Check if the app is running in backend-only mode. + + Returns: + True if the app is running in backend-only mode. + """ + return os.environ.get(constants.ENV_BACKEND_ONLY_ENV_VAR, "").lower() == "true" + + def should_skip_compile() -> bool: """Whether the app should skip compile. From 7168b42babc2954d9a6d22bc53e26542f3e70134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Brand=C3=A9ho?= Date: Mon, 21 Oct 2024 12:56:36 -0700 Subject: [PATCH 15/27] versions bump before 0.6.4 (#4208) --- .../workflows/check_outdated_dependencies.yml | 2 +- poetry.lock | 1062 +++++++++-------- pyproject.toml | 2 +- reflex/components/core/upload.py | 2 +- reflex/components/datadisplay/code.py | 2 +- reflex/components/recharts/recharts.py | 4 +- reflex/constants/installer.py | 6 +- reflex/constants/style.py | 2 +- 8 files changed, 554 insertions(+), 528 deletions(-) diff --git a/.github/workflows/check_outdated_dependencies.yml b/.github/workflows/check_outdated_dependencies.yml index fb28bb318..fe8c42608 100644 --- a/.github/workflows/check_outdated_dependencies.yml +++ b/.github/workflows/check_outdated_dependencies.yml @@ -74,7 +74,7 @@ jobs: echo "$outdated" # Ignore 3rd party dependencies that are not updated. - filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw' || true) + filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw|remark-unwrap-images' || true) no_extra=$(echo "$filtered_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-' || true) diff --git a/poetry.lock b/poetry.lock index 144547342..2f77234d4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -32,13 +32,13 @@ files = [ [[package]] name = "anyio" -version = "4.6.0" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, - {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -49,7 +49,7 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -247,101 +247,116 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -371,83 +386,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.1" +version = "7.6.4" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, - {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, - {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, - {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, - {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, - {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, - {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, - {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, - {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, + {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, + {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, + {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, + {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, + {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, + {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, + {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, + {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, + {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, + {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, + {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, + {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, + {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, + {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, ] [package.dependencies] @@ -458,38 +463,38 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "43.0.1" +version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, - {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, - {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, - {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, - {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, - {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, - {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, ] [package.dependencies] @@ -502,7 +507,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -518,13 +523,13 @@ files = [ [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] @@ -565,18 +570,18 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.0" +version = "0.115.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.0-py3-none-any.whl", hash = "sha256:17ea427674467486e997206a5ab25760f6b09e069f099b96f5b55a32fb6f1631"}, - {file = "fastapi-0.115.0.tar.gz", hash = "sha256:f93b4ca3529a8ebc6fc3fcf710e5efa8de3df9b41570958abf1d97d843138004"}, + {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"}, + {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.37.2,<0.41.0" typing-extensions = ">=4.8.0" [package.extras] @@ -601,69 +606,84 @@ typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] @@ -993,71 +1013,72 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -1347,95 +1368,90 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "pillow" -version = "10.4.0" +version = "11.0.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, - {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, - {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -1503,22 +1519,22 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.47.0" +version = "1.48.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" files = [ - {file = "playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341"}, - {file = "playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955"}, - {file = "playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249"}, - {file = "playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974"}, - {file = "playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280"}, + {file = "playwright-1.48.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:082bce2739f1078acc7d0734da8cc0e23eb91b7fae553f3316d733276f09a6b1"}, + {file = "playwright-1.48.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7da2eb51a19c7f3b523e9faa9d98e7af92e52eb983a099979ea79c9668e3cbf7"}, + {file = "playwright-1.48.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:115b988d1da322358b77bc3bf2d3cc90f8c881e691461538e7df91614c4833c9"}, + {file = "playwright-1.48.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8dabb80e62f667fe2640a8b694e26a7b884c0b4803f7514a3954fc849126227b"}, + {file = "playwright-1.48.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ff8303409ebed76bed4c3d655340320b768817d900ba208b394fdd7d7939a5c"}, + {file = "playwright-1.48.0-py3-none-win32.whl", hash = "sha256:85598c360c590076d4f435525be991246d74a905b654ac19d26eab7ed9b98b2d"}, + {file = "playwright-1.48.0-py3-none-win_amd64.whl", hash = "sha256:e0e87b0c4dc8fce83c725dd851aec37bc4e882bb225ec8a96bd83cf32d4f1623"}, ] [package.dependencies] -greenlet = "3.0.3" +greenlet = "3.1.1" pyee = "12.0.0" [[package]] @@ -1553,13 +1569,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "4.0.0" +version = "4.0.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-4.0.0-py2.py3-none-any.whl", hash = "sha256:0ca2341cf94ac1865350970951e54b1a50521e57b7b500403307aed4315a1234"}, - {file = "pre_commit-4.0.0.tar.gz", hash = "sha256:5d9807162cc5537940f94f266cbe2d716a75cfad0d78a317a92cac16287cfed6"}, + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, ] [package.dependencies] @@ -1571,32 +1587,33 @@ virtualenv = ">=20.10.0" [[package]] name = "psutil" -version = "6.0.0" +version = "6.1.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, + {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, + {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, + {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, + {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, + {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, + {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, + {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "py-cpuinfo" @@ -1962,13 +1979,13 @@ six = ">=1.5" [[package]] name = "python-engineio" -version = "4.9.1" +version = "4.10.1" description = "Engine.IO server and client for Python" optional = false python-versions = ">=3.6" files = [ - {file = "python_engineio-4.9.1-py3-none-any.whl", hash = "sha256:f995e702b21f6b9ebde4e2000cd2ad0112ba0e5116ec8d22fe3515e76ba9dddd"}, - {file = "python_engineio-4.9.1.tar.gz", hash = "sha256:7631cf5563086076611e494c643b3fa93dd3a854634b5488be0bba0ef9b99709"}, + {file = "python_engineio-4.10.1-py3-none-any.whl", hash = "sha256:445a94004ec8034960ab99e7ce4209ec619c6e6b6a12aedcb05abeab924025c0"}, + {file = "python_engineio-4.10.1.tar.gz", hash = "sha256:166cea8dd7429638c5c4e3a4895beae95196e860bc6f29ed0b9fe753d1ef2072"}, ] [package.dependencies] @@ -2150,13 +2167,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)" [[package]] name = "reflex-chakra" -version = "0.6.1" +version = "0.6.2" description = "reflex using chakra components" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "reflex_chakra-0.6.1-py3-none-any.whl", hash = "sha256:824d461264b6d2c836ba4a2a430e677a890b82e83da149672accfc58786442fa"}, - {file = "reflex_chakra-0.6.1.tar.gz", hash = "sha256:4b9b3c8bada19cbb4d1b8d8bc4ab0460ec008a91f380010c34d416d5b613dc07"}, + {file = "reflex_chakra-0.6.2-py3-none-any.whl", hash = "sha256:b8aa19f39a02601c560b97f4b17f171c0b5980e13a58069e3a5dd0999e362e4f"}, + {file = "reflex_chakra-0.6.2.tar.gz", hash = "sha256:81ddb7f182cc454922cc817312755b799d4e1a49a46ef2e81305052dc76ef86d"}, ] [package.dependencies] @@ -2316,13 +2333,13 @@ websocket-client = ">=1.8,<2.0" [[package]] name = "setuptools" -version = "75.1.0" +version = "75.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, - {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [package.extras] @@ -2347,19 +2364,20 @@ files = [ [[package]] name = "simple-websocket" -version = "1.0.0" +version = "1.1.0" description = "Simple WebSocket server and client for Python" optional = false python-versions = ">=3.6" files = [ - {file = "simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8"}, - {file = "simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc"}, + {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"}, + {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"}, ] [package.dependencies] wsproto = "*" [package.extras] +dev = ["flake8", "pytest", "pytest-cov", "tox"] docs = ["sphinx"] [[package]] @@ -2397,60 +2415,68 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.35" +version = "2.0.36" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-win32.whl", hash = "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b"}, - {file = "SQLAlchemy-2.0.35-cp310-cp310-win_amd64.whl", hash = "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-win32.whl", hash = "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e"}, - {file = "SQLAlchemy-2.0.35-cp311-cp311-win_amd64.whl", hash = "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf"}, - {file = "SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f021d334f2ca692523aaf7bbf7592ceff70c8594fad853416a81d66b35e3abf9"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c3f58cf91683102f2f0265c0db3bd3892e9eedabe059720492dbaa4f922da1"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:032d979ce77a6c2432653322ba4cbeabf5a6837f704d16fa38b5a05d8e21fa00"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:2e795c2f7d7249b75bb5f479b432a51b59041580d20599d4e112b5f2046437a3"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:cc32b2990fc34380ec2f6195f33a76b6cdaa9eecf09f0c9404b74fc120aef36f"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-win32.whl", hash = "sha256:9509c4123491d0e63fb5e16199e09f8e262066e58903e84615c301dde8fa2e87"}, - {file = "SQLAlchemy-2.0.35-cp37-cp37m-win_amd64.whl", hash = "sha256:3655af10ebcc0f1e4e06c5900bb33e080d6a1fa4228f502121f28a3b1753cde5"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-win32.whl", hash = "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0"}, - {file = "SQLAlchemy-2.0.35-cp38-cp38-win_amd64.whl", hash = "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3"}, - {file = "SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f"}, - {file = "SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1"}, - {file = "sqlalchemy-2.0.35.tar.gz", hash = "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e"}, + {file = "SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436"}, + {file = "SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-win32.whl", hash = "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c"}, + {file = "SQLAlchemy-2.0.36-cp37-cp37m-win_amd64.whl", hash = "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-win32.whl", hash = "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e"}, + {file = "SQLAlchemy-2.0.36-cp38-cp38-win_amd64.whl", hash = "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-win32.whl", hash = "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28"}, + {file = "SQLAlchemy-2.0.36-cp39-cp39-win_amd64.whl", hash = "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a"}, + {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"}, + {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"}, ] [package.dependencies] @@ -2463,7 +2489,7 @@ aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] mssql-pyodbc = ["pyodbc"] @@ -2499,13 +2525,13 @@ SQLAlchemy = ">=2.0.14,<2.1.0" [[package]] name = "starlette" -version = "0.38.6" +version = "0.40.0" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"}, - {file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"}, + {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, + {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, ] [package.dependencies] @@ -2613,13 +2639,13 @@ files = [ [[package]] name = "trio" -version = "0.26.2" +version = "0.27.0" description = "A friendly Python library for async concurrency and I/O" optional = false python-versions = ">=3.8" files = [ - {file = "trio-0.26.2-py3-none-any.whl", hash = "sha256:c5237e8133eb0a1d72f09a971a55c28ebe69e351c783fc64bc37db8db8bbe1d0"}, - {file = "trio-0.26.2.tar.gz", hash = "sha256:0346c3852c15e5c7d40ea15972c4805689ef2cb8b5206f794c9c19450119f3a4"}, + {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"}, + {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"}, ] [package.dependencies] @@ -2730,13 +2756,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.31.0" +version = "0.32.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.31.0-py3-none-any.whl", hash = "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced"}, - {file = "uvicorn-0.31.0.tar.gz", hash = "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906"}, + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, ] [package.dependencies] @@ -2749,13 +2775,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.26.6" +version = "20.27.0" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, - {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, + {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, + {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] [package.dependencies] @@ -3007,4 +3033,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "796ddba36f031ad2b47cae43ce6c49102e2cb98f92823b265d9779e6684333f6" +content-hash = "edb7145394dd61f5a665b5519cc0c091c8c1628200ea1170857cff1a6bdb829e" diff --git a/pyproject.toml b/pyproject.toml index ae636a1c4..d4f583189 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,7 @@ pytest-asyncio = ">=0.24.0" pytest-cov = ">=4.0.0,<6.0" ruff = "^0.6.9" pandas = ">=2.1.1,<3.0" -pillow = ">=10.0.0,<11.0" +pillow = ">=10.0.0,<12.0" plotly = ">=5.13.0,<6.0" asynctest = ">=0.13.0,<1.0" pre-commit = ">=3.2.1" diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index be97b170d..bc8826fe9 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -179,7 +179,7 @@ class UploadFilesProvider(Component): class Upload(MemoizationLeaf): """A file upload component.""" - library = "react-dropzone@14.2.9" + library = "react-dropzone@14.2.10" tag = "ReactDropzone" diff --git a/reflex/components/datadisplay/code.py b/reflex/components/datadisplay/code.py index c18e44885..3b4fa39d1 100644 --- a/reflex/components/datadisplay/code.py +++ b/reflex/components/datadisplay/code.py @@ -381,7 +381,7 @@ for theme_name in dir(Theme): class CodeBlock(Component): """A code block.""" - library = "react-syntax-highlighter@15.5.0" + library = "react-syntax-highlighter@15.6.1" tag = "PrismAsyncLight" diff --git a/reflex/components/recharts/recharts.py b/reflex/components/recharts/recharts.py index 9068cb396..a0d683f72 100644 --- a/reflex/components/recharts/recharts.py +++ b/reflex/components/recharts/recharts.py @@ -9,7 +9,7 @@ from reflex.utils import console class Recharts(Component): """A component that wraps a recharts lib.""" - library = "recharts@2.12.7" + library = "recharts@2.13.0" def render(self) -> Dict: """Render the tag. @@ -29,7 +29,7 @@ class Recharts(Component): class RechartsCharts(NoSSRComponent, MemoizationLeaf): """A component that wraps a recharts lib.""" - library = "recharts@2.12.7" + library = "recharts@2.13.0" LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"] diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index 9acc109ac..02e184032 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -117,18 +117,18 @@ class PackageJson(SimpleNamespace): PATH = "package.json" DEPENDENCIES = { - "@babel/standalone": "7.25.7", + "@babel/standalone": "7.25.8", "@emotion/react": "11.13.3", "axios": "1.7.7", "json5": "2.2.3", - "next": "14.2.14", + "next": "14.2.15", "next-sitemap": "4.2.3", "next-themes": "0.3.0", "react": "18.3.1", "react-dom": "18.3.1", "react-focus-lock": "2.13.2", "socket.io-client": "4.8.0", - "universal-cookie": "7.2.0", + "universal-cookie": "7.2.1", } DEV_DEPENDENCIES = { "autoprefixer": "10.4.20", diff --git a/reflex/constants/style.py b/reflex/constants/style.py index 9cd51da79..403acd4ba 100644 --- a/reflex/constants/style.py +++ b/reflex/constants/style.py @@ -7,7 +7,7 @@ class Tailwind(SimpleNamespace): """Tailwind constants.""" # The Tailwindcss version - VERSION = "tailwindcss@3.4.13" + VERSION = "tailwindcss@3.4.14" # The Tailwind config. CONFIG = "tailwind.config.js" # Default Tailwind content paths From c05da488f95eb651cc5f100f3d32313bde08374d Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 21 Oct 2024 12:56:56 -0700 Subject: [PATCH 16/27] Raise TypeError when `ComputedVar.__init__` gets bad kwargs (#4199) It's easy to mis-spell `rx.var(cached=True)` instead of `rx.var(cache=True)`, and in 0.6.3, this doesn't actual raise an error... the bad value is silently discarded and the var is NOT marked as being cached. --- reflex/vars/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index 14e7251bb..cf02863c3 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -1557,8 +1557,8 @@ class ComputedVar(Var[RETURN_TYPE]): "return", Any ) - kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__) - kwargs["_var_type"] = kwargs.pop("_var_type", hint) + kwargs.setdefault("_js_expr", fget.__name__) + kwargs.setdefault("_var_type", hint) Var.__init__( self, @@ -1567,6 +1567,9 @@ class ComputedVar(Var[RETURN_TYPE]): _var_data=kwargs.pop("_var_data", None), ) + if kwargs: + raise TypeError(f"Unexpected keyword arguments: {tuple(kwargs)}") + if backend is None: backend = fget.__name__.startswith("_") From f39e8c96674a5e8bad5f42575a321c2af47c34ec Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 13:28:55 -0700 Subject: [PATCH 17/27] move all environment variables to the same place (#4192) * move all environment variables to the same place * reorder things around * move more variables to environment * remove cyclical imports * forgot default value for field * for some reason type hints aren't being interpreted * put the field type *before* not after * make it get * move a bit more * add more fields * move reflex dir * add return * put things somewhere else * add custom error --- reflex/app.py | 9 +- reflex/compiler/compiler.py | 5 +- reflex/components/core/upload.py | 6 +- reflex/config.py | 198 +++++++++++++++++- reflex/constants/base.py | 79 ++++--- reflex/constants/config.py | 3 +- reflex/constants/installer.py | 106 +++++++--- reflex/constants/utils.py | 32 +++ reflex/custom_components/custom_components.py | 6 +- reflex/model.py | 19 +- reflex/reflex.py | 4 +- reflex/state.py | 8 +- reflex/utils/build.py | 12 -- reflex/utils/exceptions.py | 4 + reflex/utils/exec.py | 4 +- reflex/utils/net.py | 6 +- reflex/utils/path_ops.py | 22 +- reflex/utils/prerequisites.py | 17 +- reflex/utils/registry.py | 8 +- reflex/utils/types.py | 14 ++ tests/units/test_config.py | 3 +- 21 files changed, 421 insertions(+), 144 deletions(-) create mode 100644 reflex/constants/utils.py diff --git a/reflex/app.py b/reflex/app.py index 584b8a321..abf0b5d41 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -64,7 +64,7 @@ from reflex.components.core.client_side_routing import ( ) from reflex.components.core.upload import Upload, get_upload_dir from reflex.components.radix import themes -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.event import Event, EventHandler, EventSpec, window_alert from reflex.model import Model, get_db_status from reflex.page import ( @@ -957,15 +957,16 @@ class App(MiddlewareMixin, LifespanMixin, Base): executor = None if ( platform.system() in ("Linux", "Darwin") - and os.environ.get("REFLEX_COMPILE_PROCESSES") is not None + and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES) + is not None ): executor = concurrent.futures.ProcessPoolExecutor( - max_workers=int(os.environ.get("REFLEX_COMPILE_PROCESSES", 0)) or None, + max_workers=number_of_processes, mp_context=multiprocessing.get_context("fork"), ) else: executor = concurrent.futures.ThreadPoolExecutor( - max_workers=int(os.environ.get("REFLEX_COMPILE_THREADS", 0)) or None, + max_workers=environment.REFLEX_COMPILE_THREADS ) with executor: diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 0c29f941d..909299635 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -2,7 +2,6 @@ from __future__ import annotations -import os from datetime import datetime from pathlib import Path from typing import Dict, Iterable, Optional, Type, Union @@ -16,7 +15,7 @@ from reflex.components.component import ( CustomComponent, StatefulComponent, ) -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.state import BaseState from reflex.style import SYSTEM_COLOR_MODE from reflex.utils.exec import is_prod_mode @@ -527,7 +526,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]: def purge_web_pages_dir(): """Empty out .web/pages directory.""" - if not is_prod_mode() and os.environ.get("REFLEX_PERSIST_WEB_DIR"): + if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR: # Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set. return diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index bc8826fe9..89665bb31 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -2,13 +2,13 @@ from __future__ import annotations -import os from pathlib import Path from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf from reflex.components.el.elements.forms import Input from reflex.components.radix.themes.layout.box import Box +from reflex.config import environment from reflex.constants import Dirs from reflex.event import ( CallableEventSpec, @@ -125,9 +125,7 @@ def get_upload_dir() -> Path: """ Upload.is_used = True - uploaded_files_dir = Path( - os.environ.get("REFLEX_UPLOADED_FILES_DIR", "./uploaded_files") - ) + uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR uploaded_files_dir.mkdir(parents=True, exist_ok=True) return uploaded_files_dir diff --git a/reflex/config.py b/reflex/config.py index 719d5c21f..2e16e2eb0 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -2,6 +2,7 @@ from __future__ import annotations +import dataclasses import importlib import os import sys @@ -9,7 +10,10 @@ import urllib.parse from pathlib import Path from typing import Any, Dict, List, Optional, Set, Union -from reflex.utils.exceptions import ConfigError +from typing_extensions import get_type_hints + +from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError +from reflex.utils.types import value_inside_optional try: import pydantic.v1 as pydantic @@ -131,6 +135,198 @@ class DBConfig(Base): return f"{self.engine}://{path}/{self.database}" +def get_default_value_for_field(field: dataclasses.Field) -> Any: + """Get the default value for a field. + + Args: + field: The field. + + Returns: + The default value. + + Raises: + ValueError: If no default value is found. + """ + if field.default != dataclasses.MISSING: + return field.default + elif field.default_factory != dataclasses.MISSING: + return field.default_factory() + else: + raise ValueError( + f"Missing value for environment variable {field.name} and no default value found" + ) + + +def interpret_boolean_env(value: str) -> bool: + """Interpret a boolean environment variable value. + + Args: + value: The environment variable value. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the value is invalid. + """ + true_values = ["true", "1", "yes", "y"] + false_values = ["false", "0", "no", "n"] + + if value.lower() in true_values: + return True + elif value.lower() in false_values: + return False + raise EnvironmentVarValueError(f"Invalid boolean value: {value}") + + +def interpret_int_env(value: str) -> int: + """Interpret an integer environment variable value. + + Args: + value: The environment variable value. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the value is invalid. + """ + try: + return int(value) + except ValueError as ve: + raise EnvironmentVarValueError(f"Invalid integer value: {value}") from ve + + +def interpret_path_env(value: str) -> Path: + """Interpret a path environment variable value. + + Args: + value: The environment variable value. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the path does not exist. + """ + path = Path(value) + if not path.exists(): + raise EnvironmentVarValueError(f"Path does not exist: {path}") + return path + + +def interpret_env_var_value(value: str, field: dataclasses.Field) -> Any: + """Interpret an environment variable value based on the field type. + + Args: + value: The environment variable value. + field: The field. + + Returns: + The interpreted value. + + Raises: + ValueError: If the value is invalid. + """ + field_type = value_inside_optional(field.type) + + if field_type is bool: + return interpret_boolean_env(value) + elif field_type is str: + return value + elif field_type is int: + return interpret_int_env(value) + elif field_type is Path: + return interpret_path_env(value) + + else: + raise ValueError( + f"Invalid type for environment variable {field.name}: {field_type}. This is probably an issue in Reflex." + ) + + +@dataclasses.dataclass(init=False) +class EnvironmentVariables: + """Environment variables class to instantiate environment variables.""" + + # Whether to use npm over bun to install frontend packages. + REFLEX_USE_NPM: bool = False + + # The npm registry to use. + NPM_CONFIG_REGISTRY: Optional[str] = None + + # Whether to use Granian for the backend. Otherwise, use Uvicorn. + REFLEX_USE_GRANIAN: bool = False + + # The username to use for authentication on python package repository. Username and password must both be provided. + TWINE_USERNAME: Optional[str] = None + + # The password to use for authentication on python package repository. Username and password must both be provided. + TWINE_PASSWORD: Optional[str] = None + + # Whether to use the system installed bun. If set to false, bun will be bundled with the app. + REFLEX_USE_SYSTEM_BUN: bool = False + + # Whether to use the system installed node and npm. If set to false, node and npm will be bundled with the app. + REFLEX_USE_SYSTEM_NODE: bool = False + + # The working directory for the next.js commands. + REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB) + + # Path to the alembic config file + ALEMBIC_CONFIG: Path = Path(constants.ALEMBIC_CONFIG) + + # Disable SSL verification for HTTPX requests. + SSL_NO_VERIFY: bool = False + + # The directory to store uploaded files. + REFLEX_UPLOADED_FILES_DIR: Path = Path(constants.Dirs.UPLOADED_FILES) + + # Whether to use seperate processes to compile the frontend and how many. If not set, defaults to thread executor. + REFLEX_COMPILE_PROCESSES: Optional[int] = None + + # Whether to use seperate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`. + REFLEX_COMPILE_THREADS: Optional[int] = None + + # The directory to store reflex dependencies. + REFLEX_DIR: Path = Path(constants.Reflex.DIR) + + # Whether to print the SQL queries if the log level is INFO or lower. + SQLALCHEMY_ECHO: bool = False + + # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration. + REFLEX_IGNORE_REDIS_CONFIG_ERROR: bool = False + + # Whether to skip purging the web directory in dev mode. + REFLEX_PERSIST_WEB_DIR: bool = False + + # The reflex.build frontend host. + REFLEX_BUILD_FRONTEND: str = constants.Templates.REFLEX_BUILD_FRONTEND + + # The reflex.build backend host. + REFLEX_BUILD_BACKEND: str = constants.Templates.REFLEX_BUILD_BACKEND + + def __init__(self): + """Initialize the environment variables.""" + type_hints = get_type_hints(type(self)) + + for field in dataclasses.fields(self): + raw_value = os.getenv(field.name, None) + + field.type = type_hints.get(field.name) or field.type + + value = ( + interpret_env_var_value(raw_value, field) + if raw_value is not None + else get_default_value_for_field(field) + ) + + setattr(self, field.name, value) + + +environment = EnvironmentVariables() + + class Config(Base): """The config defines runtime settings for the app. diff --git a/reflex/constants/base.py b/reflex/constants/base.py index 070ff7724..bba53d625 100644 --- a/reflex/constants/base.py +++ b/reflex/constants/base.py @@ -2,7 +2,6 @@ from __future__ import annotations -import os import platform from enum import Enum from importlib import metadata @@ -11,6 +10,8 @@ from types import SimpleNamespace from platformdirs import PlatformDirs +from .utils import classproperty + IS_WINDOWS = platform.system() == "Windows" @@ -20,6 +21,8 @@ class Dirs(SimpleNamespace): # The frontend directories in a project. # The web folder where the NextJS app is compiled to. WEB = ".web" + # The directory where uploaded files are stored. + UPLOADED_FILES = "uploaded_files" # The name of the assets directory. APP_ASSETS = "assets" # The name of the assets directory for external ressource (a subfolder of APP_ASSETS). @@ -64,21 +67,13 @@ class Reflex(SimpleNamespace): # Files and directories used to init a new project. # The directory to store reflex dependencies. - # Get directory value from enviroment variables if it exists. - _dir = os.environ.get("REFLEX_DIR", "") + # on windows, we use C:/Users//AppData/Local/reflex. + # on macOS, we use ~/Library/Application Support/reflex. + # on linux, we use ~/.local/share/reflex. + # If user sets REFLEX_DIR envroment variable use that instead. + DIR = PlatformDirs(MODULE_NAME, False).user_data_path - DIR = Path( - _dir - or ( - # on windows, we use C:/Users//AppData/Local/reflex. - # on macOS, we use ~/Library/Application Support/reflex. - # on linux, we use ~/.local/share/reflex. - # If user sets REFLEX_DIR envroment variable use that instead. - PlatformDirs(MODULE_NAME, False).user_data_dir - ) - ) # The root directory of the reflex library. - ROOT_DIR = Path(__file__).parents[2] RELEASES_URL = f"https://api.github.com/repos/reflex-dev/templates/releases" @@ -101,27 +96,51 @@ class Templates(SimpleNamespace): DEFAULT = "blank" # The reflex.build frontend host - REFLEX_BUILD_FRONTEND = os.environ.get( - "REFLEX_BUILD_FRONTEND", "https://flexgen.reflex.run" - ) + REFLEX_BUILD_FRONTEND = "https://flexgen.reflex.run" # The reflex.build backend host - REFLEX_BUILD_BACKEND = os.environ.get( - "REFLEX_BUILD_BACKEND", "https://flexgen-prod-flexgen.fly.dev" - ) + REFLEX_BUILD_BACKEND = "https://flexgen-prod-flexgen.fly.dev" - # The URL to redirect to reflex.build - REFLEX_BUILD_URL = ( - REFLEX_BUILD_FRONTEND + "/gen?reflex_init_token={reflex_init_token}" - ) + @classproperty + @classmethod + def REFLEX_BUILD_URL(cls): + """The URL to redirect to reflex.build. - # The URL to poll waiting for the user to select a generation. - REFLEX_BUILD_POLL_URL = REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}" + Returns: + The URL to redirect to reflex.build. + """ + from reflex.config import environment - # The URL to fetch the generation's reflex code - REFLEX_BUILD_CODE_URL = ( - REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}/refactored" - ) + return ( + environment.REFLEX_BUILD_FRONTEND + + "/gen?reflex_init_token={reflex_init_token}" + ) + + @classproperty + @classmethod + def REFLEX_BUILD_POLL_URL(cls): + """The URL to poll waiting for the user to select a generation. + + Returns: + The URL to poll waiting for the user to select a generation. + """ + from reflex.config import environment + + return environment.REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}" + + @classproperty + @classmethod + def REFLEX_BUILD_CODE_URL(cls): + """The URL to fetch the generation's reflex code. + + Returns: + The URL to fetch the generation's reflex code. + """ + from reflex.config import environment + + return ( + environment.REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}/refactored" + ) class Dirs(SimpleNamespace): """Folders used by the template system of Reflex.""" diff --git a/reflex/constants/config.py b/reflex/constants/config.py index 3ff7aade5..970e67844 100644 --- a/reflex/constants/config.py +++ b/reflex/constants/config.py @@ -1,6 +1,5 @@ """Config constants.""" -import os from pathlib import Path from types import SimpleNamespace @@ -9,7 +8,7 @@ from reflex.constants.base import Dirs, Reflex from .compiler import Ext # Alembic migrations -ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini") +ALEMBIC_CONFIG = "alembic.ini" class Config(SimpleNamespace): diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index 02e184032..766dcf5be 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -3,9 +3,11 @@ from __future__ import annotations import platform +from pathlib import Path from types import SimpleNamespace -from .base import IS_WINDOWS, Reflex +from .base import IS_WINDOWS +from .utils import classproperty def get_fnm_name() -> str | None: @@ -36,12 +38,9 @@ class Bun(SimpleNamespace): # The Bun version. VERSION = "1.1.29" + # Min Bun Version MIN_VERSION = "0.7.0" - # The directory to store the bun. - ROOT_PATH = Reflex.DIR / "bun" - # Default bun path. - DEFAULT_PATH = ROOT_PATH / "bin" / ("bun" if not IS_WINDOWS else "bun.exe") # URL to bun install script. INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh" @@ -50,11 +49,31 @@ class Bun(SimpleNamespace): WINDOWS_INSTALL_URL = ( "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1" ) + # Path of the bunfig file CONFIG_PATH = "bunfig.toml" - # The environment variable to use the system installed bun. - USE_SYSTEM_VAR = "REFLEX_USE_SYSTEM_BUN" + @classproperty + @classmethod + def ROOT_PATH(cls): + """The directory to store the bun. + + Returns: + The directory to store the bun. + """ + from reflex.config import environment + + return environment.REFLEX_DIR / "bun" + + @classproperty + @classmethod + def DEFAULT_PATH(cls): + """Default bun path. + + Returns: + The default bun path. + """ + return cls.ROOT_PATH / "bin" / ("bun" if not IS_WINDOWS else "bun.exe") # FNM config. @@ -63,17 +82,36 @@ class Fnm(SimpleNamespace): # The FNM version. VERSION = "1.35.1" - # The directory to store fnm. - DIR = Reflex.DIR / "fnm" + FILENAME = get_fnm_name() - # The fnm executable binary. - EXE = DIR / ("fnm.exe" if IS_WINDOWS else "fnm") # The URL to the fnm release binary INSTALL_URL = ( f"https://github.com/Schniz/fnm/releases/download/v{VERSION}/{FILENAME}.zip" ) + @classproperty + @classmethod + def DIR(cls) -> Path: + """The directory to store fnm. + + Returns: + The directory to store fnm. + """ + from reflex.config import environment + + return environment.REFLEX_DIR / "fnm" + + @classproperty + @classmethod + def EXE(cls): + """The fnm executable binary. + + Returns: + The fnm executable binary. + """ + return cls.DIR / ("fnm.exe" if IS_WINDOWS else "fnm") + # Node / NPM config class Node(SimpleNamespace): @@ -84,23 +122,41 @@ class Node(SimpleNamespace): # The minimum required node version. MIN_VERSION = "18.17.0" - # The node bin path. - BIN_PATH = ( - Fnm.DIR - / "node-versions" - / f"v{VERSION}" - / "installation" - / ("bin" if not IS_WINDOWS else "") - ) + @classproperty + @classmethod + def BIN_PATH(cls): + """The node bin path. - # The default path where node is installed. - PATH = BIN_PATH / ("node.exe" if IS_WINDOWS else "node") + Returns: + The node bin path. + """ + return ( + Fnm.DIR + / "node-versions" + / f"v{cls.VERSION}" + / "installation" + / ("bin" if not IS_WINDOWS else "") + ) - # The default path where npm is installed. - NPM_PATH = BIN_PATH / "npm" + @classproperty + @classmethod + def PATH(cls): + """The default path where node is installed. - # The environment variable to use the system installed node. - USE_SYSTEM_VAR = "REFLEX_USE_SYSTEM_NODE" + Returns: + The default path where node is installed. + """ + return cls.BIN_PATH / ("node.exe" if IS_WINDOWS else "node") + + @classproperty + @classmethod + def NPM_PATH(cls): + """The default path where npm is installed. + + Returns: + The default path where npm is installed. + """ + return cls.BIN_PATH / "npm" class PackageJson(SimpleNamespace): diff --git a/reflex/constants/utils.py b/reflex/constants/utils.py new file mode 100644 index 000000000..48797afbf --- /dev/null +++ b/reflex/constants/utils.py @@ -0,0 +1,32 @@ +"""Utility functions for constants.""" + +from typing import Any, Callable, Generic, Type + +from typing_extensions import TypeVar + +T = TypeVar("T") +V = TypeVar("V") + + +class classproperty(Generic[T, V]): + """A class property decorator.""" + + def __init__(self, getter: Callable[[Type[T]], V]) -> None: + """Initialize the class property. + + Args: + getter: The getter function. + """ + self.getter = getattr(getter, "__func__", getter) + + def __get__(self, instance: Any, owner: Type[T]) -> V: + """Get the value of the class property. + + Args: + instance: The instance of the class. + owner: The class itself. + + Returns: + The value of the class property. + """ + return self.getter(owner) diff --git a/reflex/custom_components/custom_components.py b/reflex/custom_components/custom_components.py index ee24a7cd0..ddda3de56 100644 --- a/reflex/custom_components/custom_components.py +++ b/reflex/custom_components/custom_components.py @@ -17,7 +17,7 @@ import typer from tomlkit.exceptions import TOMLKitError from reflex import constants -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.constants import CustomComponents from reflex.utils import console @@ -609,14 +609,14 @@ def publish( help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time", ), username: Optional[str] = typer.Option( - os.getenv("TWINE_USERNAME"), + environment.TWINE_USERNAME, "-u", "--username", show_default="TWINE_USERNAME environment variable value if set", help="The username to use for authentication on python package repository. Username and password must both be provided.", ), password: Optional[str] = typer.Option( - os.getenv("TWINE_PASSWORD"), + environment.TWINE_PASSWORD, "-p", "--password", show_default="TWINE_PASSWORD environment variable value if set", diff --git a/reflex/model.py b/reflex/model.py index 0e8d62e90..b269044c5 100644 --- a/reflex/model.py +++ b/reflex/model.py @@ -2,9 +2,7 @@ from __future__ import annotations -import os from collections import defaultdict -from pathlib import Path from typing import Any, ClassVar, Optional, Type, Union import alembic.autogenerate @@ -18,9 +16,8 @@ import sqlalchemy import sqlalchemy.exc import sqlalchemy.orm -from reflex import constants from reflex.base import Base -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.utils import console from reflex.utils.compat import sqlmodel, sqlmodel_field_has_primary_key @@ -41,12 +38,12 @@ def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine: url = url or conf.db_url if url is None: raise ValueError("No database url configured") - if not Path(constants.ALEMBIC_CONFIG).exists(): + if environment.ALEMBIC_CONFIG.exists(): console.warn( "Database is not initialized, run [bold]reflex db init[/bold] first." ) # Print the SQL queries if the log level is INFO or lower. - echo_db_query = os.environ.get("SQLALCHEMY_ECHO") == "True" + echo_db_query = environment.SQLALCHEMY_ECHO # Needed for the admin dash on sqlite. connect_args = {"check_same_thread": False} if url.startswith("sqlite") else {} return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args) @@ -234,7 +231,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue Returns: tuple of (config, script_directory) """ - config = alembic.config.Config(constants.ALEMBIC_CONFIG) + config = alembic.config.Config(environment.ALEMBIC_CONFIG) return config, alembic.script.ScriptDirectory( config.get_main_option("script_location", default="version"), ) @@ -269,8 +266,8 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue def alembic_init(cls): """Initialize alembic for the project.""" alembic.command.init( - config=alembic.config.Config(constants.ALEMBIC_CONFIG), - directory=str(Path(constants.ALEMBIC_CONFIG).parent / "alembic"), + config=alembic.config.Config(environment.ALEMBIC_CONFIG), + directory=str(environment.ALEMBIC_CONFIG.parent / "alembic"), ) @classmethod @@ -290,7 +287,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue Returns: True when changes have been detected. """ - if not Path(constants.ALEMBIC_CONFIG).exists(): + if not environment.ALEMBIC_CONFIG.exists(): return False config, script_directory = cls._alembic_config() @@ -391,7 +388,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue True - indicating the process was successful. None - indicating the process was skipped. """ - if not Path(constants.ALEMBIC_CONFIG).exists(): + if not environment.ALEMBIC_CONFIG.exists(): return with cls.get_db_engine().connect() as connection: diff --git a/reflex/reflex.py b/reflex/reflex.py index 99850a9d2..7bef8b7e5 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -13,7 +13,7 @@ from reflex_cli.deployments import deployments_cli from reflex_cli.utils import dependency from reflex import constants -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.custom_components.custom_components import custom_components_cli from reflex.state import reset_disk_state_manager from reflex.utils import console, redir, telemetry @@ -420,7 +420,7 @@ def db_init(): return # Check the alembic config. - if Path(constants.ALEMBIC_CONFIG).exists(): + if environment.ALEMBIC_CONFIG.exists(): console.error( "Database is already initialized. Use " "[bold]reflex db makemigrations[/bold] to create schema change " diff --git a/reflex/state.py b/reflex/state.py index 9740bddaa..0634ad5b7 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -8,7 +8,6 @@ import copy import dataclasses import functools import inspect -import os import pickle import sys import uuid @@ -64,6 +63,7 @@ from redis.exceptions import ResponseError import reflex.istate.dynamic from reflex import constants from reflex.base import Base +from reflex.config import environment from reflex.event import ( BACKGROUND_TASK_MARKER, Event, @@ -3274,11 +3274,7 @@ class StateManagerRedis(StateManager): ) except ResponseError: # Some redis servers only allow out-of-band configuration, so ignore errors here. - ignore_config_error = os.environ.get( - "REFLEX_IGNORE_REDIS_CONFIG_ERROR", - None, - ) - if not ignore_config_error: + if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR: raise async with self.redis.pubsub() as pubsub: await pubsub.psubscribe(lock_key_channel) diff --git a/reflex/utils/build.py b/reflex/utils/build.py index 770809015..14709d99c 100644 --- a/reflex/utils/build.py +++ b/reflex/utils/build.py @@ -23,18 +23,6 @@ def set_env_json(): ) -def set_os_env(**kwargs): - """Set os environment variables. - - Args: - kwargs: env key word args. - """ - for key, value in kwargs.items(): - if not value: - continue - os.environ[key.upper()] = value - - def generate_sitemap_config(deploy_url: str, export=False): """Generate the sitemap config file. diff --git a/reflex/utils/exceptions.py b/reflex/utils/exceptions.py index 35f59a0e1..cd3d108b4 100644 --- a/reflex/utils/exceptions.py +++ b/reflex/utils/exceptions.py @@ -135,3 +135,7 @@ class SetUndefinedStateVarError(ReflexError, AttributeError): class StateSchemaMismatchError(ReflexError, TypeError): """Raised when the serialized schema of a state class does not match the current schema.""" + + +class EnvironmentVarValueError(ReflexError, ValueError): + """Raised when an environment variable is set to an invalid value.""" diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index 6fa67a96f..f5521c91f 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -15,7 +15,7 @@ from urllib.parse import urljoin import psutil from reflex import constants -from reflex.config import get_config +from reflex.config import environment, get_config from reflex.constants.base import LogLevel from reflex.utils import console, path_ops from reflex.utils.prerequisites import get_web_dir @@ -184,7 +184,7 @@ def should_use_granian(): Returns: True if Granian should be used. """ - return os.getenv("REFLEX_USE_GRANIAN", "0") == "1" + return environment.REFLEX_USE_GRANIAN def get_app_module(): diff --git a/reflex/utils/net.py b/reflex/utils/net.py index 83e25559c..2c6f22764 100644 --- a/reflex/utils/net.py +++ b/reflex/utils/net.py @@ -1,9 +1,8 @@ """Helpers for downloading files from the network.""" -import os - import httpx +from ..config import environment from . import console @@ -13,8 +12,7 @@ def _httpx_verify_kwarg() -> bool: Returns: True if SSL verification is enabled, False otherwise """ - ssl_no_verify = os.environ.get("SSL_NO_VERIFY", "").lower() in ["true", "1", "yes"] - return not ssl_no_verify + return not environment.SSL_NO_VERIFY def get(url: str, **kwargs) -> httpx.Response: diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index f795e1aa4..ee93b24cf 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -9,6 +9,7 @@ import shutil from pathlib import Path from reflex import constants +from reflex.config import environment # Shorthand for join. join = os.linesep.join @@ -129,30 +130,13 @@ def which(program: str | Path) -> str | Path | None: return shutil.which(str(program)) -def use_system_install(var_name: str) -> bool: - """Check if the system install should be used. - - Args: - var_name: The name of the environment variable. - - Raises: - ValueError: If the variable name is invalid. - - Returns: - Whether the associated env var should use the system install. - """ - if not var_name.startswith("REFLEX_USE_SYSTEM_"): - raise ValueError("Invalid system install variable name.") - return os.getenv(var_name, "").lower() in ["true", "1", "yes"] - - def use_system_node() -> bool: """Check if the system node should be used. Returns: Whether the system node should be used. """ - return use_system_install(constants.Node.USE_SYSTEM_VAR) + return environment.REFLEX_USE_SYSTEM_NODE def use_system_bun() -> bool: @@ -161,7 +145,7 @@ def use_system_bun() -> bool: Returns: Whether the system bun should be used. """ - return use_system_install(constants.Bun.USE_SYSTEM_VAR) + return environment.REFLEX_USE_SYSTEM_BUN def get_node_bin_path() -> Path | None: diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 44d1f6acc..33165af0e 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -33,7 +33,7 @@ from redis.asyncio import Redis from reflex import constants, model from reflex.compiler import templates -from reflex.config import Config, get_config +from reflex.config import Config, environment, get_config from reflex.utils import console, net, path_ops, processes from reflex.utils.exceptions import GeneratedCodeHasNoFunctionDefs from reflex.utils.format import format_library_name @@ -69,8 +69,7 @@ def get_web_dir() -> Path: Returns: The working directory. """ - workdir = Path(os.getenv("REFLEX_WEB_WORKDIR", constants.Dirs.WEB)) - return workdir + return environment.REFLEX_WEB_WORKDIR def _python_version_check(): @@ -250,7 +249,7 @@ def windows_npm_escape_hatch() -> bool: Returns: If the user has set REFLEX_USE_NPM. """ - return os.environ.get("REFLEX_USE_NPM", "").lower() in ["true", "1", "yes"] + return environment.REFLEX_USE_NPM def get_app(reload: bool = False) -> ModuleType: @@ -992,7 +991,7 @@ def needs_reinit(frontend: bool = True) -> bool: return False # Make sure the .reflex directory exists. - if not constants.Reflex.DIR.exists(): + if not environment.REFLEX_DIR.exists(): return True # Make sure the .web directory exists in frontend mode. @@ -1097,7 +1096,7 @@ def ensure_reflex_installation_id() -> Optional[int]: """ try: initialize_reflex_user_directory() - installation_id_file = constants.Reflex.DIR / "installation_id" + installation_id_file = environment.REFLEX_DIR / "installation_id" installation_id = None if installation_id_file.exists(): @@ -1122,7 +1121,7 @@ def ensure_reflex_installation_id() -> Optional[int]: def initialize_reflex_user_directory(): """Initialize the reflex user directory.""" # Create the reflex directory. - path_ops.mkdir(constants.Reflex.DIR) + path_ops.mkdir(environment.REFLEX_DIR) def initialize_frontend_dependencies(): @@ -1145,7 +1144,7 @@ def check_db_initialized() -> bool: Returns: True if alembic is initialized (or if database is not used). """ - if get_config().db_url is not None and not Path(constants.ALEMBIC_CONFIG).exists(): + if get_config().db_url is not None and not environment.ALEMBIC_CONFIG.exists(): console.error( "Database is not initialized. Run [bold]reflex db init[/bold] first." ) @@ -1155,7 +1154,7 @@ def check_db_initialized() -> bool: def check_schema_up_to_date(): """Check if the sqlmodel metadata matches the current database schema.""" - if get_config().db_url is None or not Path(constants.ALEMBIC_CONFIG).exists(): + if get_config().db_url is None or not environment.ALEMBIC_CONFIG.exists(): return with model.Model.get_db_engine().connect() as connection: try: diff --git a/reflex/utils/registry.py b/reflex/utils/registry.py index 6b87c163d..77c3d31cd 100644 --- a/reflex/utils/registry.py +++ b/reflex/utils/registry.py @@ -1,9 +1,8 @@ """Utilities for working with registries.""" -import os - import httpx +from reflex.config import environment from reflex.utils import console, net @@ -56,7 +55,4 @@ def _get_npm_registry() -> str: Returns: str: """ - if npm_registry := os.environ.get("NPM_CONFIG_REGISTRY", ""): - return npm_registry - else: - return get_best_registry() + return environment.NPM_CONFIG_REGISTRY or get_best_registry() diff --git a/reflex/utils/types.py b/reflex/utils/types.py index 0a3aed110..3d7992011 100644 --- a/reflex/utils/types.py +++ b/reflex/utils/types.py @@ -274,6 +274,20 @@ def is_optional(cls: GenericType) -> bool: return is_union(cls) and type(None) in get_args(cls) +def value_inside_optional(cls: GenericType) -> GenericType: + """Get the value inside an Optional type or the original type. + + Args: + cls: The class to check. + + Returns: + The value inside the Optional type or the original type. + """ + if is_union(cls) and len(args := get_args(cls)) >= 2 and type(None) in args: + return unionize(*[arg for arg in args if arg is not type(None)]) + return cls + + def get_property_hint(attr: Any | None) -> GenericType | None: """Check if an attribute is a property and return its type hint. diff --git a/tests/units/test_config.py b/tests/units/test_config.py index a6c6fe697..c4a143bc5 100644 --- a/tests/units/test_config.py +++ b/tests/units/test_config.py @@ -5,6 +5,7 @@ import pytest import reflex as rx import reflex.config +from reflex.config import environment from reflex.constants import Endpoint @@ -178,7 +179,7 @@ def test_replace_defaults( def reflex_dir_constant(): - return rx.constants.Reflex.DIR + return environment.REFLEX_DIR def test_reflex_dir_env_var(monkeypatch, tmp_path): From 54ad9f0f4b4e7832c4720ecc5245f9e214bd5ec9 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 17:05:13 -0700 Subject: [PATCH 18/27] make var system expandable (#4175) * make var system expandable * use old syntax * remove newer features * that's a weird error * remove unnecessary error message * remove hacky getattr as it's no longer necessary * improve color handling * get it right pyright * dang it darglint * fix prototype to string * don't try twice * adjust test case * add test for var alpha * change place of type ignore * fix json * add name to custom var operation * don't delete that you silly * change logic * remove extra word --- reflex/event.py | 35 +- reflex/vars/base.py | 866 +++++++++++---------- reflex/vars/function.py | 21 +- reflex/vars/number.py | 139 ++-- reflex/vars/object.py | 32 +- reflex/vars/sequence.py | 204 +++-- tests/units/components/core/test_colors.py | 12 +- tests/units/test_var.py | 4 +- 8 files changed, 682 insertions(+), 631 deletions(-) diff --git a/reflex/event.py b/reflex/event.py index 8291e3465..76a465739 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -12,7 +12,6 @@ from functools import partial from typing import ( Any, Callable, - ClassVar, Dict, Generic, List, @@ -33,9 +32,7 @@ from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch from reflex.utils.types import ArgsSpec, GenericType from reflex.vars import VarData from reflex.vars.base import ( - LiteralNoneVar, LiteralVar, - ToOperation, Var, ) from reflex.vars.function import ( @@ -1254,7 +1251,7 @@ def get_fn_signature(fn: Callable) -> inspect.Signature: return signature.replace(parameters=(new_param, *signature.parameters.values())) -class EventVar(ObjectVar): +class EventVar(ObjectVar, python_types=EventSpec): """Base class for event vars.""" @@ -1315,7 +1312,7 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar): ) -class EventChainVar(FunctionVar): +class EventChainVar(FunctionVar, python_types=EventChain): """Base class for event chain vars.""" @@ -1384,32 +1381,6 @@ class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar): ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToEventVarOperation(ToOperation, EventVar): - """Result of a cast to an event var.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = EventSpec - - -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToEventChainVarOperation(ToOperation, EventChainVar): - """Result of a cast to an event chain var.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = EventChain - - G = ParamSpec("G") IndividualEventType = Union[EventSpec, EventHandler, Callable[G, Any], Var[Any]] @@ -1537,8 +1508,6 @@ class EventNamespace(types.SimpleNamespace): LiteralEventVar = LiteralEventVar EventChainVar = EventChainVar LiteralEventChainVar = LiteralEventChainVar - ToEventVarOperation = ToEventVarOperation - ToEventChainVarOperation = ToEventChainVarOperation EventType = EventType __call__ = staticmethod(event_handler) diff --git a/reflex/vars/base.py b/reflex/vars/base.py index cf02863c3..6df8eec6f 100644 --- a/reflex/vars/base.py +++ b/reflex/vars/base.py @@ -19,6 +19,7 @@ from typing import ( TYPE_CHECKING, Any, Callable, + ClassVar, Dict, FrozenSet, Generic, @@ -37,7 +38,13 @@ from typing import ( overload, ) -from typing_extensions import ParamSpec, TypeGuard, deprecated, get_type_hints, override +from typing_extensions import ( + ParamSpec, + TypeGuard, + deprecated, + get_type_hints, + override, +) from reflex import constants from reflex.base import Base @@ -61,15 +68,13 @@ from reflex.utils.types import GenericType, Self, get_origin, has_args, unionize if TYPE_CHECKING: from reflex.state import BaseState - from .function import FunctionVar, ToFunctionOperation + from .function import FunctionVar from .number import ( BooleanVar, NumberVar, - ToBooleanVarOperation, - ToNumberVarOperation, ) - from .object import ObjectVar, ToObjectOperation - from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation + from .object import ObjectVar + from .sequence import ArrayVar, StringVar VAR_TYPE = TypeVar("VAR_TYPE", covariant=True) @@ -78,6 +83,184 @@ OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE") warnings.filterwarnings("ignore", message="fields may not start with an underscore") +@dataclasses.dataclass( + eq=False, + frozen=True, +) +class VarSubclassEntry: + """Entry for a Var subclass.""" + + var_subclass: Type[Var] + to_var_subclass: Type[ToOperation] + python_types: Tuple[GenericType, ...] + + +_var_subclasses: List[VarSubclassEntry] = [] +_var_literal_subclasses: List[Tuple[Type[LiteralVar], VarSubclassEntry]] = [] + + +@dataclasses.dataclass( + eq=True, + frozen=True, +) +class VarData: + """Metadata associated with a x.""" + + # The name of the enclosing state. + state: str = dataclasses.field(default="") + + # The name of the field in the state. + field_name: str = dataclasses.field(default="") + + # Imports needed to render this var + imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple) + + # Hooks that need to be present in the component to render this var + hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple) + + def __init__( + self, + state: str = "", + field_name: str = "", + imports: ImportDict | ParsedImportDict | None = None, + hooks: dict[str, None] | None = None, + ): + """Initialize the var data. + + Args: + state: The name of the enclosing state. + field_name: The name of the field in the state. + imports: Imports needed to render this var. + hooks: Hooks that need to be present in the component to render this var. + """ + immutable_imports: ImmutableParsedImportDict = tuple( + sorted( + ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items()) + ) + ) + object.__setattr__(self, "state", state) + object.__setattr__(self, "field_name", field_name) + object.__setattr__(self, "imports", immutable_imports) + object.__setattr__(self, "hooks", tuple(hooks or {})) + + def old_school_imports(self) -> ImportDict: + """Return the imports as a mutable dict. + + Returns: + The imports as a mutable dict. + """ + return dict((k, list(v)) for k, v in self.imports) + + @classmethod + def merge(cls, *others: VarData | None) -> VarData | None: + """Merge multiple var data objects. + + Args: + *others: The var data objects to merge. + + Returns: + The merged var data object. + """ + state = "" + field_name = "" + _imports = {} + hooks = {} + for var_data in others: + if var_data is None: + continue + state = state or var_data.state + field_name = field_name or var_data.field_name + _imports = imports.merge_imports(_imports, var_data.imports) + hooks.update( + var_data.hooks + if isinstance(var_data.hooks, dict) + else {k: None for k in var_data.hooks} + ) + + if state or _imports or hooks or field_name: + return VarData( + state=state, + field_name=field_name, + imports=_imports, + hooks=hooks, + ) + return None + + def __bool__(self) -> bool: + """Check if the var data is non-empty. + + Returns: + True if any field is set to a non-default value. + """ + return bool(self.state or self.imports or self.hooks or self.field_name) + + @classmethod + def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData: + """Set the state of the var. + + Args: + state: The state to set or the full name of the state. + field_name: The name of the field in the state. Optional. + + Returns: + The var with the set state. + """ + from reflex.utils import format + + state_name = state if isinstance(state, str) else state.get_full_name() + return VarData( + state=state_name, + field_name=field_name, + hooks={ + "const {0} = useContext(StateContexts.{0})".format( + format.format_state_name(state_name) + ): None + }, + imports={ + f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")], + "react": [ImportVar(tag="useContext")], + }, + ) + + +def _decode_var_immutable(value: str) -> tuple[VarData | None, str]: + """Decode the state name from a formatted var. + + Args: + value: The value to extract the state name from. + + Returns: + The extracted state name and the value without the state name. + """ + var_datas = [] + if isinstance(value, str): + # fast path if there is no encoded VarData + if constants.REFLEX_VAR_OPENING_TAG not in value: + return None, value + + offset = 0 + + # Find all tags. + while m := _decode_var_pattern.search(value): + start, end = m.span() + value = value[:start] + value[end:] + + serialized_data = m.group(1) + + if serialized_data.isnumeric() or ( + serialized_data[0] == "-" and serialized_data[1:].isnumeric() + ): + # This is a global immutable var. + var = _global_vars[int(serialized_data)] + var_data = var._get_all_var_data() + + if var_data is not None: + var_datas.append(var_data) + offset += end - start + + return VarData.merge(*var_datas) if var_datas else None, value + + @dataclasses.dataclass( eq=False, frozen=True, @@ -151,6 +334,40 @@ class Var(Generic[VAR_TYPE]): """ return False + def __init_subclass__( + cls, python_types: Tuple[GenericType, ...] | GenericType = types.Unset, **kwargs + ): + """Initialize the subclass. + + Args: + python_types: The python types that the var represents. + **kwargs: Additional keyword arguments. + """ + super().__init_subclass__(**kwargs) + + if python_types is not types.Unset: + python_types = ( + python_types if isinstance(python_types, tuple) else (python_types,) + ) + + @dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, + ) + class ToVarOperation(ToOperation, cls): + """Base class of converting a var to another var type.""" + + _original: Var = dataclasses.field( + default=Var(_js_expr="null", _var_type=None), + ) + + _default_var_type: ClassVar[GenericType] = python_types[0] + + ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation' + + _var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types)) + def __post_init__(self): """Post-initialize the var.""" # Decode any inline Var markup and apply it to the instance @@ -331,35 +548,35 @@ class Var(Generic[VAR_TYPE]): return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}" @overload - def to(self, output: Type[StringVar]) -> ToStringOperation: ... + def to(self, output: Type[StringVar]) -> StringVar: ... @overload - def to(self, output: Type[str]) -> ToStringOperation: ... + def to(self, output: Type[str]) -> StringVar: ... @overload - def to(self, output: Type[BooleanVar]) -> ToBooleanVarOperation: ... + def to(self, output: Type[BooleanVar]) -> BooleanVar: ... @overload def to( self, output: Type[NumberVar], var_type: type[int] | type[float] = float - ) -> ToNumberVarOperation: ... + ) -> NumberVar: ... @overload def to( self, output: Type[ArrayVar], var_type: type[list] | type[tuple] | type[set] = list, - ) -> ToArrayOperation: ... + ) -> ArrayVar: ... @overload def to( self, output: Type[ObjectVar], var_type: types.GenericType = dict - ) -> ToObjectOperation: ... + ) -> ObjectVar: ... @overload def to( self, output: Type[FunctionVar], var_type: Type[Callable] = Callable - ) -> ToFunctionOperation: ... + ) -> FunctionVar: ... @overload def to( @@ -379,56 +596,26 @@ class Var(Generic[VAR_TYPE]): output: The output type. var_type: The type of the var. - Raises: - TypeError: If the var_type is not a supported type for the output. - Returns: The converted var. """ - from reflex.event import ( - EventChain, - EventChainVar, - EventSpec, - EventVar, - ToEventChainVarOperation, - ToEventVarOperation, - ) - - from .function import FunctionVar, ToFunctionOperation - from .number import ( - BooleanVar, - NumberVar, - ToBooleanVarOperation, - ToNumberVarOperation, - ) - from .object import ObjectVar, ToObjectOperation - from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation + from .object import ObjectVar base_type = var_type if types.is_optional(base_type): base_type = types.get_args(base_type)[0] - fixed_type = get_origin(base_type) or base_type - fixed_output_type = get_origin(output) or output # If the first argument is a python type, we map it to the corresponding Var type. - if fixed_output_type is dict: - return self.to(ObjectVar, output) - if fixed_output_type in (list, tuple, set): - return self.to(ArrayVar, output) - if fixed_output_type in (int, float): - return self.to(NumberVar, output) - if fixed_output_type is str: - return self.to(StringVar, output) - if fixed_output_type is bool: - return self.to(BooleanVar, output) + for var_subclass in _var_subclasses[::-1]: + if fixed_output_type in var_subclass.python_types: + return self.to(var_subclass.var_subclass, output) + if fixed_output_type is None: - return ToNoneOperation.create(self) - if fixed_output_type is EventSpec: - return self.to(EventVar, output) - if fixed_output_type is EventChain: - return self.to(EventChainVar, output) + return get_to_operation(NoneVar).create(self) # type: ignore + + # Handle fixed_output_type being Base or a dataclass. try: if issubclass(fixed_output_type, Base): return self.to(ObjectVar, output) @@ -440,57 +627,12 @@ class Var(Generic[VAR_TYPE]): return self.to(ObjectVar, output) if inspect.isclass(output): - if issubclass(output, BooleanVar): - return ToBooleanVarOperation.create(self) - - if issubclass(output, NumberVar): - if fixed_type is not None: - if fixed_type in types.UnionTypes: - inner_types = get_args(base_type) - if not all(issubclass(t, (int, float)) for t in inner_types): - raise TypeError( - f"Unsupported type {var_type} for NumberVar. Must be int or float." - ) - - elif not issubclass(fixed_type, (int, float)): - raise TypeError( - f"Unsupported type {var_type} for NumberVar. Must be int or float." - ) - return ToNumberVarOperation.create(self, var_type or float) - - if issubclass(output, ArrayVar): - if fixed_type is not None and not issubclass( - fixed_type, (list, tuple, set) - ): - raise TypeError( - f"Unsupported type {var_type} for ArrayVar. Must be list, tuple, or set." + for var_subclass in _var_subclasses[::-1]: + if issubclass(output, var_subclass.var_subclass): + to_operation_return = var_subclass.to_var_subclass.create( + value=self, _var_type=var_type ) - return ToArrayOperation.create(self, var_type or list) - - if issubclass(output, StringVar): - return ToStringOperation.create(self, var_type or str) - - if issubclass(output, EventVar): - return ToEventVarOperation.create(self, var_type or EventSpec) - - if issubclass(output, EventChainVar): - return ToEventChainVarOperation.create(self, var_type or EventChain) - - if issubclass(output, (ObjectVar, Base)): - return ToObjectOperation.create(self, var_type or dict) - - if issubclass(output, FunctionVar): - # if fixed_type is not None and not issubclass(fixed_type, Callable): - # raise TypeError( - # f"Unsupported type {var_type} for FunctionVar. Must be Callable." - # ) - return ToFunctionOperation.create(self, var_type or Callable) - - if issubclass(output, NoneVar): - return ToNoneOperation.create(self) - - if dataclasses.is_dataclass(output): - return ToObjectOperation.create(self, var_type or dict) + return to_operation_return # type: ignore # If we can't determine the first argument, we just replace the _var_type. if not issubclass(output, Var) or var_type is None: @@ -508,6 +650,18 @@ class Var(Generic[VAR_TYPE]): return self + @overload + def guess_type(self: Var[str]) -> StringVar: ... + + @overload + def guess_type(self: Var[bool]) -> BooleanVar: ... + + @overload + def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ... + + @overload + def guess_type(self) -> Self: ... + def guess_type(self) -> Var: """Guesses the type of the variable based on its `_var_type` attribute. @@ -517,11 +671,8 @@ class Var(Generic[VAR_TYPE]): Raises: TypeError: If the type is not supported for guessing. """ - from reflex.event import EventChain, EventChainVar, EventSpec, EventVar - - from .number import BooleanVar, NumberVar + from .number import NumberVar from .object import ObjectVar - from .sequence import ArrayVar, StringVar var_type = self._var_type if var_type is None: @@ -558,20 +709,13 @@ class Var(Generic[VAR_TYPE]): if not inspect.isclass(fixed_type): raise TypeError(f"Unsupported type {var_type} for guess_type.") - if issubclass(fixed_type, bool): - return self.to(BooleanVar, self._var_type) - if issubclass(fixed_type, (int, float)): - return self.to(NumberVar, self._var_type) - if issubclass(fixed_type, dict): - return self.to(ObjectVar, self._var_type) - if issubclass(fixed_type, (list, tuple, set)): - return self.to(ArrayVar, self._var_type) - if issubclass(fixed_type, str): - return self.to(StringVar, self._var_type) - if issubclass(fixed_type, EventSpec): - return self.to(EventVar, self._var_type) - if issubclass(fixed_type, EventChain): - return self.to(EventChainVar, self._var_type) + if fixed_type is None: + return self.to(None) + + for var_subclass in _var_subclasses[::-1]: + if issubclass(fixed_type, var_subclass.python_types): + return self.to(var_subclass.var_subclass, self._var_type) + try: if issubclass(fixed_type, Base): return self.to(ObjectVar, self._var_type) @@ -782,16 +926,23 @@ class Var(Generic[VAR_TYPE]): """ return ~self.bool() - def to_string(self): + def to_string(self, use_json: bool = True) -> StringVar: """Convert the var to a string. + Args: + use_json: Whether to use JSON stringify. If False, uses Object.prototype.toString. + Returns: The string var. """ - from .function import JSON_STRINGIFY + from .function import JSON_STRINGIFY, PROTOTYPE_TO_STRING from .sequence import StringVar - return JSON_STRINGIFY.call(self).to(StringVar) + return ( + JSON_STRINGIFY.call(self).to(StringVar) + if use_json + else PROTOTYPE_TO_STRING.call(self).to(StringVar) + ) def as_ref(self) -> Var: """Get a reference to the var. @@ -1017,9 +1168,129 @@ class Var(Generic[VAR_TYPE]): OUTPUT = TypeVar("OUTPUT", bound=Var) +class ToOperation: + """A var operation that converts a var to another type.""" + + def __getattr__(self, name: str) -> Any: + """Get an attribute of the var. + + Args: + name: The name of the attribute. + + Returns: + The attribute of the var. + """ + from .object import ObjectVar + + if isinstance(self, ObjectVar) and name != "_js_expr": + return ObjectVar.__getattr__(self, name) + return getattr(self._original, name) + + def __post_init__(self): + """Post initialization.""" + object.__delattr__(self, "_js_expr") + + def __hash__(self) -> int: + """Calculate the hash value of the object. + + Returns: + int: The hash value of the object. + """ + return hash(self._original) + + def _get_all_var_data(self) -> VarData | None: + """Get all the var data. + + Returns: + The var data. + """ + return VarData.merge( + self._original._get_all_var_data(), + self._var_data, # type: ignore + ) + + @classmethod + def create( + cls, + value: Var, + _var_type: GenericType | None = None, + _var_data: VarData | None = None, + ): + """Create a ToOperation. + + Args: + value: The value of the var. + _var_type: The type of the Var. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The ToOperation. + """ + return cls( + _js_expr="", # type: ignore + _var_data=_var_data, # type: ignore + _var_type=_var_type or cls._default_var_type, # type: ignore + _original=value, # type: ignore + ) + + class LiteralVar(Var): """Base class for immutable literal vars.""" + def __init_subclass__(cls, **kwargs): + """Initialize the subclass. + + Args: + **kwargs: Additional keyword arguments. + + Raises: + TypeError: If the LiteralVar subclass does not have a corresponding Var subclass. + """ + super().__init_subclass__(**kwargs) + + bases = cls.__bases__ + + bases_normalized = [ + base if inspect.isclass(base) else get_origin(base) for base in bases + ] + + possible_bases = [ + base + for base in bases_normalized + if issubclass(base, Var) and base != LiteralVar + ] + + if not possible_bases: + raise TypeError( + f"LiteralVar subclass {cls} must have a base class that is a subclass of Var and not LiteralVar." + ) + + var_subclasses = [ + var_subclass + for var_subclass in _var_subclasses + if var_subclass.var_subclass in possible_bases + ] + + if not var_subclasses: + raise TypeError( + f"LiteralVar {cls} must have a base class annotated with `python_types`." + ) + + if len(var_subclasses) != 1: + raise TypeError( + f"LiteralVar {cls} must have exactly one base class annotated with `python_types`." + ) + + var_subclass = var_subclasses[0] + + # Remove the old subclass, happens because __init_subclass__ is called twice + # for each subclass. This is because of __slots__ in dataclasses. + for var_literal_subclass in list(_var_literal_subclasses): + if var_literal_subclass[1] is var_subclass: + _var_literal_subclasses.remove(var_literal_subclass) + + _var_literal_subclasses.append((cls, var_subclass)) + @classmethod def create( cls, @@ -1038,50 +1309,21 @@ class LiteralVar(Var): Raises: TypeError: If the value is not a supported type for LiteralVar. """ - from .number import LiteralBooleanVar, LiteralNumberVar from .object import LiteralObjectVar - from .sequence import LiteralArrayVar, LiteralStringVar + from .sequence import LiteralStringVar if isinstance(value, Var): if _var_data is None: return value return value._replace(merge_var_data=_var_data) - if isinstance(value, str): - return LiteralStringVar.create(value, _var_data=_var_data) + for literal_subclass, var_subclass in _var_literal_subclasses[::-1]: + if isinstance(value, var_subclass.python_types): + return literal_subclass.create(value, _var_data=_var_data) - if isinstance(value, bool): - return LiteralBooleanVar.create(value, _var_data=_var_data) - - if isinstance(value, (int, float)): - return LiteralNumberVar.create(value, _var_data=_var_data) - - if isinstance(value, dict): - return LiteralObjectVar.create(value, _var_data=_var_data) - - if isinstance(value, (list, tuple, set)): - return LiteralArrayVar.create(value, _var_data=_var_data) - - if value is None: - return LiteralNoneVar.create(_var_data=_var_data) - - from reflex.event import ( - EventChain, - EventHandler, - EventSpec, - LiteralEventChainVar, - LiteralEventVar, - ) + from reflex.event import EventHandler from reflex.utils.format import get_event_handler_parts - from .object import LiteralObjectVar - - if isinstance(value, EventSpec): - return LiteralEventVar.create(value, _var_data=_var_data) - - if isinstance(value, EventChain): - return LiteralEventChainVar.create(value, _var_data=_var_data) - if isinstance(value, EventHandler): return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value)))) @@ -1155,6 +1397,22 @@ def serialize_literal(value: LiteralVar): return value._var_value +def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None: + """Get the Python literal value. + + Args: + value: The value to get the Python literal value of. + + Returns: + The Python literal value. + """ + if isinstance(value, LiteralVar): + return value._var_value + if isinstance(value, Var): + return None + return value + + P = ParamSpec("P") T = TypeVar("T") @@ -1205,6 +1463,12 @@ def var_operation( ) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ... +@overload +def var_operation( + func: Callable[P, CustomVarOperationReturn[T]], +) -> Callable[P, Var[T]]: ... + + def var_operation( func: Callable[P, CustomVarOperationReturn[T]], ) -> Callable[P, Var[T]]: @@ -1237,6 +1501,7 @@ def var_operation( } return CustomVarOperation.create( + name=func.__name__, args=tuple(list(args_vars.items()) + list(kwargs_vars.items())), return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore ).guess_type() @@ -2062,6 +2327,8 @@ def var_operation_return( class CustomVarOperation(CachedVarOperation, Var[T]): """Base class for custom var operations.""" + _name: str = dataclasses.field(default="") + _args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple) _return: CustomVarOperationReturn[T] = dataclasses.field( @@ -2096,6 +2363,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]): @classmethod def create( cls, + name: str, args: Tuple[Tuple[str, Var], ...], return_var: CustomVarOperationReturn[T], _var_data: VarData | None = None, @@ -2103,6 +2371,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]): """Create a CustomVarOperation. Args: + name: The name of the operation. args: The arguments to the operation. return_var: The return var. _var_data: Additional hooks and imports associated with the Var. @@ -2114,12 +2383,13 @@ class CustomVarOperation(CachedVarOperation, Var[T]): _js_expr="", _var_type=return_var._var_type, _var_data=_var_data, + _name=name, _args=args, _return=return_var, ) -class NoneVar(Var[None]): +class NoneVar(Var[None], python_types=type(None)): """A var representing None.""" @@ -2144,11 +2414,13 @@ class LiteralNoneVar(LiteralVar, NoneVar): @classmethod def create( cls, + value: None = None, _var_data: VarData | None = None, ) -> LiteralNoneVar: """Create a var from a value. Args: + value: The value of the var. Must be None. Existed for compatibility with LiteralVar. _var_data: Additional hooks and imports associated with the Var. Returns: @@ -2161,48 +2433,26 @@ class LiteralNoneVar(LiteralVar, NoneVar): ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToNoneOperation(CachedVarOperation, NoneVar): - """A var operation that converts a var to None.""" +def get_to_operation(var_subclass: Type[Var]) -> Type[ToOperation]: + """Get the ToOperation class for a given Var subclass. - _original_var: Var = dataclasses.field( - default_factory=lambda: LiteralNoneVar.create() - ) + Args: + var_subclass: The Var subclass. - @cached_property_no_lock - def _cached_var_name(self) -> str: - """Get the cached var name. + Returns: + The ToOperation class. - Returns: - The cached var name. - """ - return str(self._original_var) - - @classmethod - def create( - cls, - var: Var, - _var_data: VarData | None = None, - ) -> ToNoneOperation: - """Create a ToNoneOperation. - - Args: - var: The var to convert to None. - _var_data: Additional hooks and imports associated with the Var. - - Returns: - The ToNoneOperation. - """ - return ToNoneOperation( - _js_expr="", - _var_type=None, - _var_data=_var_data, - _original_var=var, - ) + Raises: + ValueError: If the ToOperation class cannot be found. + """ + possible_classes = [ + saved_var_subclass.to_var_subclass + for saved_var_subclass in _var_subclasses + if saved_var_subclass.var_subclass is var_subclass + ] + if not possible_classes: + raise ValueError(f"Could not find ToOperation for {var_subclass}.") + return possible_classes[0] @dataclasses.dataclass( @@ -2265,68 +2515,6 @@ class StateOperation(CachedVarOperation, Var): ) -class ToOperation: - """A var operation that converts a var to another type.""" - - def __getattr__(self, name: str) -> Any: - """Get an attribute of the var. - - Args: - name: The name of the attribute. - - Returns: - The attribute of the var. - """ - return getattr(object.__getattribute__(self, "_original"), name) - - def __post_init__(self): - """Post initialization.""" - object.__delattr__(self, "_js_expr") - - def __hash__(self) -> int: - """Calculate the hash value of the object. - - Returns: - int: The hash value of the object. - """ - return hash(object.__getattribute__(self, "_original")) - - def _get_all_var_data(self) -> VarData | None: - """Get all the var data. - - Returns: - The var data. - """ - return VarData.merge( - object.__getattribute__(self, "_original")._get_all_var_data(), - self._var_data, # type: ignore - ) - - @classmethod - def create( - cls, - value: Var, - _var_type: GenericType | None = None, - _var_data: VarData | None = None, - ): - """Create a ToOperation. - - Args: - value: The value of the var. - _var_type: The type of the Var. - _var_data: Additional hooks and imports associated with the Var. - - Returns: - The ToOperation. - """ - return cls( - _js_expr="", # type: ignore - _var_data=_var_data, # type: ignore - _var_type=_var_type or cls._default_var_type, # type: ignore - _original=value, # type: ignore - ) - - def get_uuid_string_var() -> Var: """Return a Var that generates a single memoized UUID via .web/utils/state.js. @@ -2372,168 +2560,6 @@ def get_unique_variable_name() -> str: return get_unique_variable_name() -@dataclasses.dataclass( - eq=True, - frozen=True, -) -class VarData: - """Metadata associated with a x.""" - - # The name of the enclosing state. - state: str = dataclasses.field(default="") - - # The name of the field in the state. - field_name: str = dataclasses.field(default="") - - # Imports needed to render this var - imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple) - - # Hooks that need to be present in the component to render this var - hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple) - - def __init__( - self, - state: str = "", - field_name: str = "", - imports: ImportDict | ParsedImportDict | None = None, - hooks: dict[str, None] | None = None, - ): - """Initialize the var data. - - Args: - state: The name of the enclosing state. - field_name: The name of the field in the state. - imports: Imports needed to render this var. - hooks: Hooks that need to be present in the component to render this var. - """ - immutable_imports: ImmutableParsedImportDict = tuple( - sorted( - ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items()) - ) - ) - object.__setattr__(self, "state", state) - object.__setattr__(self, "field_name", field_name) - object.__setattr__(self, "imports", immutable_imports) - object.__setattr__(self, "hooks", tuple(hooks or {})) - - def old_school_imports(self) -> ImportDict: - """Return the imports as a mutable dict. - - Returns: - The imports as a mutable dict. - """ - return dict((k, list(v)) for k, v in self.imports) - - @classmethod - def merge(cls, *others: VarData | None) -> VarData | None: - """Merge multiple var data objects. - - Args: - *others: The var data objects to merge. - - Returns: - The merged var data object. - """ - state = "" - field_name = "" - _imports = {} - hooks = {} - for var_data in others: - if var_data is None: - continue - state = state or var_data.state - field_name = field_name or var_data.field_name - _imports = imports.merge_imports(_imports, var_data.imports) - hooks.update( - var_data.hooks - if isinstance(var_data.hooks, dict) - else {k: None for k in var_data.hooks} - ) - - if state or _imports or hooks or field_name: - return VarData( - state=state, - field_name=field_name, - imports=_imports, - hooks=hooks, - ) - return None - - def __bool__(self) -> bool: - """Check if the var data is non-empty. - - Returns: - True if any field is set to a non-default value. - """ - return bool(self.state or self.imports or self.hooks or self.field_name) - - @classmethod - def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData: - """Set the state of the var. - - Args: - state: The state to set or the full name of the state. - field_name: The name of the field in the state. Optional. - - Returns: - The var with the set state. - """ - from reflex.utils import format - - state_name = state if isinstance(state, str) else state.get_full_name() - return VarData( - state=state_name, - field_name=field_name, - hooks={ - "const {0} = useContext(StateContexts.{0})".format( - format.format_state_name(state_name) - ): None - }, - imports={ - f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")], - "react": [ImportVar(tag="useContext")], - }, - ) - - -def _decode_var_immutable(value: str) -> tuple[VarData | None, str]: - """Decode the state name from a formatted var. - - Args: - value: The value to extract the state name from. - - Returns: - The extracted state name and the value without the state name. - """ - var_datas = [] - if isinstance(value, str): - # fast path if there is no encoded VarData - if constants.REFLEX_VAR_OPENING_TAG not in value: - return None, value - - offset = 0 - - # Find all tags. - while m := _decode_var_pattern.search(value): - start, end = m.span() - value = value[:start] + value[end:] - - serialized_data = m.group(1) - - if serialized_data.isnumeric() or ( - serialized_data[0] == "-" and serialized_data[1:].isnumeric() - ): - # This is a global immutable var. - var = _global_vars[int(serialized_data)] - var_data = var._get_all_var_data() - - if var_data is not None: - var_datas.append(var_data) - offset += end - start - - return VarData.merge(*var_datas) if var_datas else None, value - - # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" diff --git a/reflex/vars/function.py b/reflex/vars/function.py index a512432b9..a1f7fb7bd 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -4,21 +4,20 @@ from __future__ import annotations import dataclasses import sys -from typing import Any, Callable, ClassVar, Optional, Tuple, Type, Union +from typing import Any, Callable, Optional, Tuple, Type, Union from reflex.utils.types import GenericType from .base import ( CachedVarOperation, LiteralVar, - ToOperation, Var, VarData, cached_property_no_lock, ) -class FunctionVar(Var[Callable]): +class FunctionVar(Var[Callable], python_types=Callable): """Base class for immutable function vars.""" def __call__(self, *args: Var | Any) -> ArgsFunctionOperation: @@ -180,17 +179,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar): ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToFunctionOperation(ToOperation, FunctionVar): - """Base class of converting a var to a function.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None)) - - _default_var_type: ClassVar[GenericType] = Callable - - JSON_STRINGIFY = FunctionStringVar.create("JSON.stringify") +PROTOTYPE_TO_STRING = FunctionStringVar.create( + "((__to_string) => __to_string.toString())" +) diff --git a/reflex/vars/number.py b/reflex/vars/number.py index 0aaa7a068..77c728d13 100644 --- a/reflex/vars/number.py +++ b/reflex/vars/number.py @@ -10,7 +10,6 @@ from typing import ( TYPE_CHECKING, Any, Callable, - ClassVar, NoReturn, Type, TypeVar, @@ -25,9 +24,7 @@ from reflex.utils.types import is_optional from .base import ( CustomVarOperationReturn, - LiteralNoneVar, LiteralVar, - ToOperation, Var, VarData, unionize, @@ -58,7 +55,7 @@ def raise_unsupported_operand_types( ) -class NumberVar(Var[NUMBER_T]): +class NumberVar(Var[NUMBER_T], python_types=(int, float)): """Base class for immutable number vars.""" @overload @@ -760,7 +757,7 @@ def number_trunc_operation(value: NumberVar): return var_operation_return(js_expression=f"Math.trunc({value})", var_type=int) -class BooleanVar(NumberVar[bool]): +class BooleanVar(NumberVar[bool], python_types=bool): """Base class for immutable boolean vars.""" def __invert__(self): @@ -984,51 +981,6 @@ def boolean_not_operation(value: BooleanVar): return var_operation_return(js_expression=f"!({value})", var_type=bool) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class LiteralBooleanVar(LiteralVar, BooleanVar): - """Base class for immutable literal boolean vars.""" - - _var_value: bool = dataclasses.field(default=False) - - def json(self) -> str: - """Get the JSON representation of the var. - - Returns: - The JSON representation of the var. - """ - return "true" if self._var_value else "false" - - def __hash__(self) -> int: - """Calculate the hash value of the object. - - Returns: - int: The hash value of the object. - """ - return hash((self.__class__.__name__, self._var_value)) - - @classmethod - def create(cls, value: bool, _var_data: VarData | None = None): - """Create the boolean var. - - Args: - value: The value of the var. - _var_data: Additional hooks and imports associated with the Var. - - Returns: - The boolean var. - """ - return cls( - _js_expr="true" if value else "false", - _var_type=bool, - _var_data=_var_data, - _var_value=value, - ) - - @dataclasses.dataclass( eq=False, frozen=True, @@ -1088,36 +1040,55 @@ class LiteralNumberVar(LiteralVar, NumberVar): ) +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class LiteralBooleanVar(LiteralVar, BooleanVar): + """Base class for immutable literal boolean vars.""" + + _var_value: bool = dataclasses.field(default=False) + + def json(self) -> str: + """Get the JSON representation of the var. + + Returns: + The JSON representation of the var. + """ + return "true" if self._var_value else "false" + + def __hash__(self) -> int: + """Calculate the hash value of the object. + + Returns: + int: The hash value of the object. + """ + return hash((self.__class__.__name__, self._var_value)) + + @classmethod + def create(cls, value: bool, _var_data: VarData | None = None): + """Create the boolean var. + + Args: + value: The value of the var. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The boolean var. + """ + return cls( + _js_expr="true" if value else "false", + _var_type=bool, + _var_data=_var_data, + _var_value=value, + ) + + number_types = Union[NumberVar, int, float] boolean_types = Union[BooleanVar, bool] -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToNumberVarOperation(ToOperation, NumberVar): - """Base class for immutable number vars that are the result of a number operation.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = float - - -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToBooleanVarOperation(ToOperation, BooleanVar): - """Base class for immutable boolean vars that are the result of a boolean operation.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = bool - - _IS_TRUE_IMPORT: ImportDict = { f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } @@ -1140,8 +1111,12 @@ def boolify(value: Var): ) +T = TypeVar("T") +U = TypeVar("U") + + @var_operation -def ternary_operation(condition: BooleanVar, if_true: Var, if_false: Var): +def ternary_operation(condition: BooleanVar, if_true: Var[T], if_false: Var[U]): """Create a ternary operation. Args: @@ -1152,10 +1127,14 @@ def ternary_operation(condition: BooleanVar, if_true: Var, if_false: Var): Returns: The ternary operation. """ - return var_operation_return( - js_expression=f"({condition} ? {if_true} : {if_false})", - var_type=unionize(if_true._var_type, if_false._var_type), + type_value: Union[Type[T], Type[U]] = unionize( + if_true._var_type, if_false._var_type ) + value: CustomVarOperationReturn[Union[T, U]] = var_operation_return( + js_expression=f"({condition} ? {if_true} : {if_false})", + var_type=type_value, + ) + return value NUMBER_TYPES = (int, float, NumberVar) diff --git a/reflex/vars/object.py b/reflex/vars/object.py index 38add7779..56f3535d8 100644 --- a/reflex/vars/object.py +++ b/reflex/vars/object.py @@ -8,7 +8,6 @@ import typing from inspect import isclass from typing import ( Any, - ClassVar, Dict, List, NoReturn, @@ -27,7 +26,6 @@ from reflex.utils.types import GenericType, get_attribute_access_type, get_origi from .base import ( CachedVarOperation, LiteralVar, - ToOperation, Var, VarData, cached_property_no_lock, @@ -48,7 +46,7 @@ ARRAY_INNER_TYPE = TypeVar("ARRAY_INNER_TYPE") OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE") -class ObjectVar(Var[OBJECT_TYPE]): +class ObjectVar(Var[OBJECT_TYPE], python_types=dict): """Base class for immutable object vars.""" def _key_type(self) -> Type: @@ -521,34 +519,6 @@ class ObjectItemOperation(CachedVarOperation, Var): ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToObjectOperation(ToOperation, ObjectVar): - """Operation to convert a var to an object.""" - - _original: Var = dataclasses.field( - default_factory=lambda: LiteralObjectVar.create({}) - ) - - _default_var_type: ClassVar[GenericType] = dict - - def __getattr__(self, name: str) -> Any: - """Get an attribute of the var. - - Args: - name: The name of the attribute. - - Returns: - The attribute of the var. - """ - if name == "_js_expr": - return self._original._js_expr - return ObjectVar.__getattr__(self, name) - - @var_operation def object_has_own_property_operation(object: ObjectVar, key: Var): """Check if an object has a key. diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index e3b422f35..149300028 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -11,7 +11,6 @@ import typing from typing import ( TYPE_CHECKING, Any, - ClassVar, Dict, List, Literal, @@ -19,27 +18,28 @@ from typing import ( Set, Tuple, Type, - TypeVar, Union, overload, ) +from typing_extensions import TypeVar + from reflex import constants from reflex.constants.base import REFLEX_VAR_OPENING_TAG +from reflex.constants.colors import Color from reflex.utils.exceptions import VarTypeError from reflex.utils.types import GenericType, get_origin from .base import ( CachedVarOperation, CustomVarOperationReturn, - LiteralNoneVar, LiteralVar, - ToOperation, Var, VarData, _global_vars, cached_property_no_lock, figure_out_type, + get_python_literal, get_unique_variable_name, unionize, var_operation, @@ -50,13 +50,16 @@ from .number import ( LiteralNumberVar, NumberVar, raise_unsupported_operand_types, + ternary_operation, ) if TYPE_CHECKING: from .object import ObjectVar +STRING_TYPE = TypeVar("STRING_TYPE", default=str) -class StringVar(Var[str]): + +class StringVar(Var[STRING_TYPE], python_types=str): """Base class for immutable string vars.""" @overload @@ -350,7 +353,7 @@ class StringVar(Var[str]): @var_operation -def string_lt_operation(lhs: StringVar | str, rhs: StringVar | str): +def string_lt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): """Check if a string is less than another string. Args: @@ -364,7 +367,7 @@ def string_lt_operation(lhs: StringVar | str, rhs: StringVar | str): @var_operation -def string_gt_operation(lhs: StringVar | str, rhs: StringVar | str): +def string_gt_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): """Check if a string is greater than another string. Args: @@ -378,7 +381,7 @@ def string_gt_operation(lhs: StringVar | str, rhs: StringVar | str): @var_operation -def string_le_operation(lhs: StringVar | str, rhs: StringVar | str): +def string_le_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): """Check if a string is less than or equal to another string. Args: @@ -392,7 +395,7 @@ def string_le_operation(lhs: StringVar | str, rhs: StringVar | str): @var_operation -def string_ge_operation(lhs: StringVar | str, rhs: StringVar | str): +def string_ge_operation(lhs: StringVar[Any] | str, rhs: StringVar[Any] | str): """Check if a string is greater than or equal to another string. Args: @@ -406,7 +409,7 @@ def string_ge_operation(lhs: StringVar | str, rhs: StringVar | str): @var_operation -def string_lower_operation(string: StringVar): +def string_lower_operation(string: StringVar[Any]): """Convert a string to lowercase. Args: @@ -419,7 +422,7 @@ def string_lower_operation(string: StringVar): @var_operation -def string_upper_operation(string: StringVar): +def string_upper_operation(string: StringVar[Any]): """Convert a string to uppercase. Args: @@ -432,7 +435,7 @@ def string_upper_operation(string: StringVar): @var_operation -def string_strip_operation(string: StringVar): +def string_strip_operation(string: StringVar[Any]): """Strip a string. Args: @@ -446,7 +449,7 @@ def string_strip_operation(string: StringVar): @var_operation def string_contains_field_operation( - haystack: StringVar, needle: StringVar | str, field: StringVar | str + haystack: StringVar[Any], needle: StringVar[Any] | str, field: StringVar[Any] | str ): """Check if a string contains another string. @@ -465,7 +468,7 @@ def string_contains_field_operation( @var_operation -def string_contains_operation(haystack: StringVar, needle: StringVar | str): +def string_contains_operation(haystack: StringVar[Any], needle: StringVar[Any] | str): """Check if a string contains another string. Args: @@ -481,7 +484,9 @@ def string_contains_operation(haystack: StringVar, needle: StringVar | str): @var_operation -def string_starts_with_operation(full_string: StringVar, prefix: StringVar | str): +def string_starts_with_operation( + full_string: StringVar[Any], prefix: StringVar[Any] | str +): """Check if a string starts with a prefix. Args: @@ -497,7 +502,7 @@ def string_starts_with_operation(full_string: StringVar, prefix: StringVar | str @var_operation -def string_item_operation(string: StringVar, index: NumberVar | int): +def string_item_operation(string: StringVar[Any], index: NumberVar | int): """Get an item from a string. Args: @@ -511,7 +516,7 @@ def string_item_operation(string: StringVar, index: NumberVar | int): @var_operation -def array_join_operation(array: ArrayVar, sep: StringVar | str = ""): +def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""): """Join the elements of an array. Args: @@ -536,7 +541,7 @@ _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL) frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class LiteralStringVar(LiteralVar, StringVar): +class LiteralStringVar(LiteralVar, StringVar[str]): """Base class for immutable literal string vars.""" _var_value: str = dataclasses.field(default="") @@ -658,7 +663,7 @@ class LiteralStringVar(LiteralVar, StringVar): frozen=True, **{"slots": True} if sys.version_info >= (3, 10) else {}, ) -class ConcatVarOperation(CachedVarOperation, StringVar): +class ConcatVarOperation(CachedVarOperation, StringVar[str]): """Representing a concatenation of literal string vars.""" _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple) @@ -742,7 +747,7 @@ KEY_TYPE = TypeVar("KEY_TYPE") VALUE_TYPE = TypeVar("VALUE_TYPE") -class ArrayVar(Var[ARRAY_VAR_TYPE]): +class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(list, tuple, set)): """Base class for immutable array vars.""" @overload @@ -1275,7 +1280,7 @@ class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]): @var_operation -def string_split_operation(string: StringVar, sep: StringVar | str = ""): +def string_split_operation(string: StringVar[Any], sep: StringVar | str = ""): """Split a string. Args: @@ -1572,32 +1577,6 @@ def array_contains_operation(haystack: ArrayVar, needle: Any | Var): ) -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToStringOperation(ToOperation, StringVar): - """Base class for immutable string vars that are the result of a to string operation.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = str - - -@dataclasses.dataclass( - eq=False, - frozen=True, - **{"slots": True} if sys.version_info >= (3, 10) else {}, -) -class ToArrayOperation(ToOperation, ArrayVar): - """Base class for immutable array vars that are the result of a to array operation.""" - - _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create()) - - _default_var_type: ClassVar[Type] = List[Any] - - @var_operation def repeat_array_operation( array: ArrayVar[ARRAY_VAR_TYPE], count: NumberVar | int @@ -1657,3 +1636,134 @@ def array_concat_operation( js_expression=f"[...{lhs}, ...{rhs}]", var_type=Union[lhs._var_type, rhs._var_type], ) + + +class ColorVar(StringVar[Color], python_types=Color): + """Base class for immutable color vars.""" + + +@dataclasses.dataclass( + eq=False, + frozen=True, + **{"slots": True} if sys.version_info >= (3, 10) else {}, +) +class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar): + """Base class for immutable literal color vars.""" + + _var_value: Color = dataclasses.field(default_factory=lambda: Color(color="black")) + + @classmethod + def create( + cls, + value: Color, + _var_type: Type[Color] | None = None, + _var_data: VarData | None = None, + ) -> ColorVar: + """Create a var from a string value. + + Args: + value: The value to create the var from. + _var_type: The type of the var. + _var_data: Additional hooks and imports associated with the Var. + + Returns: + The var. + """ + return cls( + _js_expr="", + _var_type=_var_type or Color, + _var_data=_var_data, + _var_value=value, + ) + + def __hash__(self) -> int: + """Get the hash of the var. + + Returns: + The hash of the var. + """ + return hash( + ( + self.__class__.__name__, + self._var_value.color, + self._var_value.alpha, + self._var_value.shade, + ) + ) + + @cached_property_no_lock + def _cached_var_name(self) -> str: + """The name of the var. + + Returns: + The name of the var. + """ + alpha = self._var_value.alpha + alpha = ( + ternary_operation( + alpha, + LiteralStringVar.create("a"), + LiteralStringVar.create(""), + ) + if isinstance(alpha, Var) + else LiteralStringVar.create("a" if alpha else "") + ) + + shade = self._var_value.shade + shade = ( + shade.to_string(use_json=False) + if isinstance(shade, Var) + else LiteralStringVar.create(str(shade)) + ) + return str( + ConcatVarOperation.create( + LiteralStringVar.create("var(--"), + self._var_value.color, + LiteralStringVar.create("-"), + alpha, + shade, + LiteralStringVar.create(")"), + ) + ) + + @cached_property_no_lock + def _cached_get_all_var_data(self) -> VarData | None: + """Get all the var data. + + Returns: + The var data. + """ + return VarData.merge( + *[ + LiteralVar.create(var)._get_all_var_data() + for var in ( + self._var_value.color, + self._var_value.alpha, + self._var_value.shade, + ) + ], + self._var_data, + ) + + def json(self) -> str: + """Get the JSON representation of the var. + + Returns: + The JSON representation of the var. + + Raises: + TypeError: If the color is not a valid color. + """ + color, alpha, shade = map( + get_python_literal, + (self._var_value.color, self._var_value.alpha, self._var_value.shade), + ) + if color is None or alpha is None or shade is None: + raise TypeError("Cannot serialize color that contains non-literal vars.") + if ( + not isinstance(color, str) + or not isinstance(alpha, bool) + or not isinstance(shade, int) + ): + raise TypeError("Color is not a valid color.") + return f"var(--{color}-{'a' if alpha else ''}{shade})" diff --git a/tests/units/components/core/test_colors.py b/tests/units/components/core/test_colors.py index a6175d56a..74fbeb20f 100644 --- a/tests/units/components/core/test_colors.py +++ b/tests/units/components/core/test_colors.py @@ -14,6 +14,7 @@ class ColorState(rx.State): color: str = "mint" color_part: str = "tom" shade: int = 4 + alpha: bool = False color_state_name = ColorState.get_full_name().replace(".", "__") @@ -31,7 +32,14 @@ def create_color_var(color): (create_color_var(rx.color("mint", 3, True)), '"var(--mint-a3)"', Color), ( create_color_var(rx.color(ColorState.color, ColorState.shade)), # type: ignore - f'("var(--"+{str(color_state_name)}.color+"-"+{str(color_state_name)}.shade+")")', + f'("var(--"+{str(color_state_name)}.color+"-"+(((__to_string) => __to_string.toString())({str(color_state_name)}.shade))+")")', + Color, + ), + ( + create_color_var( + rx.color(ColorState.color, ColorState.shade, ColorState.alpha) # type: ignore + ), + f'("var(--"+{str(color_state_name)}.color+"-"+({str(color_state_name)}.alpha ? "a" : "")+(((__to_string) => __to_string.toString())({str(color_state_name)}.shade))+")")', Color, ), ( @@ -43,7 +51,7 @@ def create_color_var(color): create_color_var( rx.color(f"{ColorState.color_part}ato", f"{ColorState.shade}") # type: ignore ), - f'("var(--"+{str(color_state_name)}.color_part+"ato-"+{str(color_state_name)}.shade+")")', + f'("var(--"+({str(color_state_name)}.color_part+"ato")+"-"+{str(color_state_name)}.shade+")")', Color, ), ( diff --git a/tests/units/test_var.py b/tests/units/test_var.py index c04e554a9..a8b4b759d 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -519,8 +519,8 @@ def test_var_indexing_types(var, type_): type_ : The type on indexed object. """ - assert var[2]._var_type == type_[0] - assert var[3]._var_type == type_[1] + assert var[0]._var_type == type_[0] + assert var[1]._var_type == type_[1] def test_var_indexing_str(): From 45959881ac2490f629fc6bd60f5d577f1f9dc656 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 18:17:06 -0700 Subject: [PATCH 19/27] add type hinting to error boundary (#4182) * add type hinting to error boundary * remove logFrontendError * fix other calls to handle_frontend_exception --- reflex/.templates/web/utils/state.js | 2 + reflex/components/base/error_boundary.py | 59 ++++++++++++++--------- reflex/components/base/error_boundary.pyi | 15 +++--- reflex/constants/compiler.py | 10 ---- reflex/state.py | 5 +- 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js index 0fe0db8c1..24092f235 100644 --- a/reflex/.templates/web/utils/state.js +++ b/reflex/.templates/web/utils/state.js @@ -743,6 +743,7 @@ export const useEventLoop = ( addEvents([ Event(`${exception_state_name}.handle_frontend_exception`, { stack: error.stack, + component_stack: "", }), ]); return false; @@ -754,6 +755,7 @@ export const useEventLoop = ( addEvents([ Event(`${exception_state_name}.handle_frontend_exception`, { stack: event.reason.stack, + component_stack: "", }), ]); return false; diff --git a/reflex/components/base/error_boundary.py b/reflex/components/base/error_boundary.py index 66a9c43c8..b35c69a60 100644 --- a/reflex/components/base/error_boundary.py +++ b/reflex/components/base/error_boundary.py @@ -2,16 +2,30 @@ from __future__ import annotations -from typing import List +from typing import Dict, List, Tuple from reflex.compiler.compiler import _compile_component from reflex.components.component import Component from reflex.components.el import div, p -from reflex.constants import Hooks, Imports -from reflex.event import EventChain, EventHandler -from reflex.utils.imports import ImportVar +from reflex.event import EventHandler +from reflex.state import FrontendEventExceptionState from reflex.vars.base import Var -from reflex.vars.function import FunctionVar + + +def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str], Var[str]]: + """The spec for the on_error event handler. + + Args: + error: The error message. + info: Additional information about the error. + + Returns: + The arguments for the event handler. + """ + return ( + error.stack, + info.componentStack, + ) class ErrorBoundary(Component): @@ -21,31 +35,13 @@ class ErrorBoundary(Component): tag = "ErrorBoundary" # Fired when the boundary catches an error. - on_error: EventHandler[lambda error, info: [error, info]] = Var( # type: ignore - "logFrontendError" - ).to(FunctionVar, EventChain) + on_error: EventHandler[on_error_spec] # Rendered instead of the children when an error is caught. Fallback_component: Var[Component] = Var(_js_expr="Fallback")._replace( _var_type=Component ) - def add_imports(self) -> dict[str, list[ImportVar]]: - """Add imports for the component. - - Returns: - The imports to add. - """ - return Imports.EVENTS - - def add_hooks(self) -> List[str | Var]: - """Add hooks for the component. - - Returns: - The hooks to add. - """ - return [Hooks.EVENTS, Hooks.FRONTEND_ERRORS] - def add_custom_code(self) -> List[str]: """Add custom Javascript code into the page that contains this component. @@ -75,5 +71,20 @@ class ErrorBoundary(Component): """ ] + @classmethod + def create(cls, *children, **props): + """Create an ErrorBoundary component. + + Args: + *children: The children of the component. + **props: The props of the component. + + Returns: + The ErrorBoundary component. + """ + if "on_error" not in props: + props["on_error"] = FrontendEventExceptionState.handle_frontend_exception + return super().create(*children, **props) + error_boundary = ErrorBoundary.create diff --git a/reflex/components/base/error_boundary.pyi b/reflex/components/base/error_boundary.pyi index aaf5584e4..94b129bdc 100644 --- a/reflex/components/base/error_boundary.pyi +++ b/reflex/components/base/error_boundary.pyi @@ -3,17 +3,18 @@ # ------------------- DO NOT EDIT ---------------------- # This file was generated by `reflex/utils/pyi_generator.py`! # ------------------------------------------------------ -from typing import Any, Dict, List, Optional, Union, overload +from typing import Any, Dict, List, Optional, Tuple, Union, overload from reflex.components.component import Component from reflex.event import EventType from reflex.style import Style -from reflex.utils.imports import ImportVar from reflex.vars.base import Var +def on_error_spec( + error: Var, info: Var[Dict[str, str]] +) -> Tuple[Var[str], Var[str]]: ... + class ErrorBoundary(Component): - def add_imports(self) -> dict[str, list[ImportVar]]: ... - def add_hooks(self) -> List[str | Var]: ... def add_custom_code(self) -> List[str]: ... @overload @classmethod @@ -31,7 +32,7 @@ class ErrorBoundary(Component): on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_error: Optional[EventType] = None, + on_error: Optional[EventType[str, str]] = None, on_focus: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, @@ -45,7 +46,7 @@ class ErrorBoundary(Component): on_unmount: Optional[EventType[[]]] = None, **props, ) -> "ErrorBoundary": - """Create the component. + """Create an ErrorBoundary component. Args: *children: The children of the component. @@ -59,7 +60,7 @@ class ErrorBoundary(Component): **props: The props of the component. Returns: - The component. + The ErrorBoundary component. """ ... diff --git a/reflex/constants/compiler.py b/reflex/constants/compiler.py index 557a92092..318a93783 100644 --- a/reflex/constants/compiler.py +++ b/reflex/constants/compiler.py @@ -132,16 +132,6 @@ class Hooks(SimpleNamespace): } })""" - FRONTEND_ERRORS = f""" - const logFrontendError = (error, info) => {{ - if (process.env.NODE_ENV === "production") {{ - addEvents([Event("{CompileVars.FRONTEND_EXCEPTION_STATE_FULL}.handle_frontend_exception", {{ - stack: error.stack, - }})]) - }} - }} - """ - class MemoizationDisposition(enum.Enum): """The conditions under which a component should be memoized.""" diff --git a/reflex/state.py b/reflex/state.py index 0634ad5b7..3422d1ba7 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -39,6 +39,7 @@ from typing import ( from sqlalchemy.orm import DeclarativeBase from typing_extensions import Self +from reflex import event from reflex.config import get_config from reflex.istate.data import RouterData from reflex.vars.base import ( @@ -2094,7 +2095,8 @@ class State(BaseState): class FrontendEventExceptionState(State): """Substate for handling frontend exceptions.""" - def handle_frontend_exception(self, stack: str) -> None: + @event + def handle_frontend_exception(self, stack: str, component_stack: str) -> None: """Handle frontend exceptions. If a frontend exception handler is provided, it will be called. @@ -2102,6 +2104,7 @@ class FrontendEventExceptionState(State): Args: stack: The stack trace of the exception. + component_stack: The stack trace of the component where the exception occurred. """ app_instance = getattr(prerequisites.get_app(), constants.CompileVars.APP) From 3ab750fecd3cebd2d13c40730d639a48e07eadc4 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 18:17:27 -0700 Subject: [PATCH 20/27] add event types to suneditor (#4209) --- reflex/components/suneditor/editor.py | 56 ++++++++++++++++++-------- reflex/components/suneditor/editor.pyi | 18 +++++---- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/reflex/components/suneditor/editor.py b/reflex/components/suneditor/editor.py index 6f5b6e4b4..3bca8a3f6 100644 --- a/reflex/components/suneditor/editor.py +++ b/reflex/components/suneditor/editor.py @@ -3,11 +3,11 @@ from __future__ import annotations import enum -from typing import Dict, List, Literal, Optional, Union +from typing import Dict, List, Literal, Optional, Tuple, Union from reflex.base import Base from reflex.components.component import Component, NoSSRComponent -from reflex.event import EventHandler +from reflex.event import EventHandler, empty_event, identity_event from reflex.utils.format import to_camel_case from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import Var @@ -68,6 +68,35 @@ class EditorOptions(Base): button_list: Optional[List[Union[List[str], str]]] +def on_blur_spec(e: Var, content: Var[str]) -> Tuple[Var[str]]: + """A helper function to specify the on_blur event handler. + + Args: + e: The event. + content: The content of the editor. + + Returns: + A tuple containing the content of the editor. + """ + return (content,) + + +def on_paste_spec( + e: Var, clean_data: Var[str], max_char_count: Var[bool] +) -> Tuple[Var[str], Var[bool]]: + """A helper function to specify the on_paste event handler. + + Args: + e: The event. + clean_data: The clean data. + max_char_count: The maximum character count. + + Returns: + A tuple containing the clean data and the maximum character count. + """ + return (clean_data, max_char_count) + + class Editor(NoSSRComponent): """A Rich Text Editor component based on SunEditor. Not every JS prop is listed here (some are not easily usable from python), @@ -178,36 +207,31 @@ class Editor(NoSSRComponent): disable_toolbar: Var[bool] # Fired when the editor content changes. - on_change: EventHandler[lambda content: [content]] + on_change: EventHandler[identity_event(str)] # Fired when the something is inputted in the editor. - on_input: EventHandler[lambda e: [e]] + on_input: EventHandler[empty_event] # Fired when the editor loses focus. - on_blur: EventHandler[lambda e, content: [content]] + on_blur: EventHandler[on_blur_spec] # Fired when the editor is loaded. - on_load: EventHandler[lambda reload: [reload]] - - # Fired when the editor is resized. - on_resize_editor: EventHandler[lambda height, prev_height: [height, prev_height]] + on_load: EventHandler[identity_event(bool)] # Fired when the editor content is copied. - on_copy: EventHandler[lambda e, clipboard_data: [clipboard_data]] + on_copy: EventHandler[empty_event] # Fired when the editor content is cut. - on_cut: EventHandler[lambda e, clipboard_data: [clipboard_data]] + on_cut: EventHandler[empty_event] # Fired when the editor content is pasted. - on_paste: EventHandler[ - lambda e, clean_data, max_char_count: [clean_data, max_char_count] - ] + on_paste: EventHandler[on_paste_spec] # Fired when the code view is toggled. - toggle_code_view: EventHandler[lambda is_code_view: [is_code_view]] + toggle_code_view: EventHandler[identity_event(bool)] # Fired when the full screen mode is toggled. - toggle_full_screen: EventHandler[lambda is_full_screen: [is_full_screen]] + toggle_full_screen: EventHandler[identity_event(bool)] def add_imports(self) -> ImportDict: """Add imports for the Editor component. diff --git a/reflex/components/suneditor/editor.pyi b/reflex/components/suneditor/editor.pyi index f7149a02c..5cc45dc98 100644 --- a/reflex/components/suneditor/editor.pyi +++ b/reflex/components/suneditor/editor.pyi @@ -4,7 +4,7 @@ # This file was generated by `reflex/utils/pyi_generator.py`! # ------------------------------------------------------ import enum -from typing import Any, Dict, List, Literal, Optional, Union, overload +from typing import Any, Dict, List, Literal, Optional, Tuple, Union, overload from reflex.base import Base from reflex.components.component import NoSSRComponent @@ -44,6 +44,11 @@ class EditorOptions(Base): rtl: Optional[bool] button_list: Optional[List[Union[List[str], str]]] +def on_blur_spec(e: Var, content: Var[str]) -> Tuple[Var[str]]: ... +def on_paste_spec( + e: Var, clean_data: Var[str], max_char_count: Var[bool] +) -> Tuple[Var[str], Var[bool]]: ... + class Editor(NoSSRComponent): def add_imports(self) -> ImportDict: ... @overload @@ -122,15 +127,15 @@ class Editor(NoSSRComponent): class_name: Optional[Any] = None, autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[EventType] = None, + on_blur: Optional[EventType[str]] = None, on_change: Optional[EventType] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, - on_copy: Optional[EventType] = None, - on_cut: Optional[EventType] = None, + on_copy: Optional[EventType[[]]] = None, + on_cut: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None, - on_input: Optional[EventType] = None, + on_input: Optional[EventType[[]]] = None, on_load: Optional[EventType] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, @@ -140,8 +145,7 @@ class Editor(NoSSRComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_paste: Optional[EventType] = None, - on_resize_editor: Optional[EventType] = None, + on_paste: Optional[EventType[str, bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, toggle_code_view: Optional[EventType] = None, From c103ab5e2872ca9496978360802d84c930e50b4e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 18:53:51 -0700 Subject: [PATCH 21/27] Add type hinting to dataeditor events (#4210) --- reflex/components/base/error_boundary.py | 4 +- reflex/components/base/error_boundary.pyi | 2 +- reflex/components/core/clipboard.pyi | 2 +- reflex/components/datadisplay/dataeditor.py | 88 +++++++++++++++++-- reflex/components/datadisplay/dataeditor.pyi | 78 +++++++++++++--- reflex/components/moment/moment.pyi | 2 +- reflex/components/radix/primitives/drawer.pyi | 4 +- reflex/components/radix/themes/color_mode.pyi | 2 +- .../radix/themes/components/alert_dialog.pyi | 2 +- .../radix/themes/components/checkbox.pyi | 6 +- .../radix/themes/components/context_menu.pyi | 2 +- .../radix/themes/components/dialog.pyi | 4 +- .../radix/themes/components/dropdown_menu.pyi | 4 +- .../radix/themes/components/hover_card.pyi | 4 +- .../radix/themes/components/popover.pyi | 2 +- .../radix/themes/components/radio_cards.pyi | 2 +- .../radix/themes/components/radio_group.pyi | 2 +- .../radix/themes/components/select.pyi | 12 +-- .../radix/themes/components/switch.pyi | 2 +- .../radix/themes/components/tabs.pyi | 4 +- .../radix/themes/components/tooltip.pyi | 2 +- reflex/components/react_player/audio.pyi | 4 +- .../components/react_player/react_player.pyi | 4 +- reflex/components/react_player/video.pyi | 4 +- reflex/components/suneditor/editor.pyi | 8 +- reflex/event.py | 82 +++++++++++++++-- reflex/experimental/layout.pyi | 2 +- reflex/utils/pyi_generator.py | 78 +++++++++++++++- tests/integration/test_lifespan.py | 1 + 29 files changed, 342 insertions(+), 71 deletions(-) diff --git a/reflex/components/base/error_boundary.py b/reflex/components/base/error_boundary.py index b35c69a60..83becc034 100644 --- a/reflex/components/base/error_boundary.py +++ b/reflex/components/base/error_boundary.py @@ -12,7 +12,9 @@ from reflex.state import FrontendEventExceptionState from reflex.vars.base import Var -def on_error_spec(error: Var, info: Var[Dict[str, str]]) -> Tuple[Var[str], Var[str]]: +def on_error_spec( + error: Var[Dict[str, str]], info: Var[Dict[str, str]] +) -> Tuple[Var[str], Var[str]]: """The spec for the on_error event handler. Args: diff --git a/reflex/components/base/error_boundary.pyi b/reflex/components/base/error_boundary.pyi index 94b129bdc..c84742851 100644 --- a/reflex/components/base/error_boundary.pyi +++ b/reflex/components/base/error_boundary.pyi @@ -11,7 +11,7 @@ from reflex.style import Style from reflex.vars.base import Var def on_error_spec( - error: Var, info: Var[Dict[str, str]] + error: Var[Dict[str, str]], info: Var[Dict[str, str]] ) -> Tuple[Var[str], Var[str]]: ... class ErrorBoundary(Component): diff --git a/reflex/components/core/clipboard.pyi b/reflex/components/core/clipboard.pyi index e2f6afc8d..489a9bcc5 100644 --- a/reflex/components/core/clipboard.pyi +++ b/reflex/components/core/clipboard.pyi @@ -40,7 +40,7 @@ class Clipboard(Fragment): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_paste: Optional[EventType] = None, + on_paste: Optional[EventType[list[tuple[str, str]]]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index 01255dd14..a192c7a45 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -5,6 +5,8 @@ from __future__ import annotations from enum import Enum from typing import Any, Dict, List, Literal, Optional, Tuple, Union +from typing_extensions import TypedDict + from reflex.base import Base from reflex.components.component import Component, NoSSRComponent from reflex.components.literals import LiteralRowMarker @@ -120,6 +122,78 @@ def on_edit_spec(pos, data: dict[str, Any]): return [pos, data] +class Bounds(TypedDict): + """The bounds of the group header.""" + + x: int + y: int + width: int + height: int + + +class CompatSelection(TypedDict): + """The selection.""" + + items: list + + +class Rectangle(TypedDict): + """The bounds of the group header.""" + + x: int + y: int + width: int + height: int + + +class GridSelectionCurrent(TypedDict): + """The current selection.""" + + cell: list[int] + range: Rectangle + rangeStack: list[Rectangle] + + +class GridSelection(TypedDict): + """The grid selection.""" + + current: Optional[GridSelectionCurrent] + columns: CompatSelection + rows: CompatSelection + + +class GroupHeaderClickedEventArgs(TypedDict): + """The arguments for the group header clicked event.""" + + kind: str + group: str + location: list[int] + bounds: Bounds + isEdge: bool + shiftKey: bool + ctrlKey: bool + metaKey: bool + isTouch: bool + localEventX: int + localEventY: int + button: int + buttons: int + scrollEdge: list[int] + + +class GridCell(TypedDict): + """The grid cell.""" + + span: Optional[List[int]] + + +class GridColumn(TypedDict): + """The grid column.""" + + title: str + group: Optional[str] + + class DataEditor(NoSSRComponent): """The DataEditor Component.""" @@ -238,10 +312,12 @@ class DataEditor(NoSSRComponent): on_group_header_clicked: EventHandler[on_edit_spec] # Fired when a group header is right-clicked. - on_group_header_context_menu: EventHandler[lambda grp_idx, data: [grp_idx, data]] + on_group_header_context_menu: EventHandler[ + identity_event(int, GroupHeaderClickedEventArgs) + ] # Fired when a group header is renamed. - on_group_header_renamed: EventHandler[lambda idx, val: [idx, val]] + on_group_header_renamed: EventHandler[identity_event(str, str)] # Fired when a header is clicked. on_header_clicked: EventHandler[identity_event(Tuple[int, int])] @@ -250,16 +326,16 @@ class DataEditor(NoSSRComponent): on_header_context_menu: EventHandler[identity_event(Tuple[int, int])] # Fired when a header menu item is clicked. - on_header_menu_click: EventHandler[lambda col, pos: [col, pos]] + on_header_menu_click: EventHandler[identity_event(int, Rectangle)] # Fired when an item is hovered. on_item_hovered: EventHandler[identity_event(Tuple[int, int])] # Fired when a selection is deleted. - on_delete: EventHandler[lambda selection: [selection]] + on_delete: EventHandler[identity_event(GridSelection)] # Fired when editing is finished. - on_finished_editing: EventHandler[lambda new_value, movement: [new_value, movement]] + on_finished_editing: EventHandler[identity_event(Union[GridCell, None], list[int])] # Fired when a row is appended. on_row_appended: EventHandler[empty_event] @@ -268,7 +344,7 @@ class DataEditor(NoSSRComponent): on_selection_cleared: EventHandler[empty_event] # Fired when a column is resized. - on_column_resize: EventHandler[lambda col, width: [col, width]] + on_column_resize: EventHandler[identity_event(GridColumn, int)] def add_imports(self) -> ImportDict: """Add imports for the component. diff --git a/reflex/components/datadisplay/dataeditor.pyi b/reflex/components/datadisplay/dataeditor.pyi index 1b8fed287..aadd9666e 100644 --- a/reflex/components/datadisplay/dataeditor.pyi +++ b/reflex/components/datadisplay/dataeditor.pyi @@ -6,6 +6,8 @@ from enum import Enum from typing import Any, Dict, List, Literal, Optional, Union, overload +from typing_extensions import TypedDict + from reflex.base import Base from reflex.components.component import NoSSRComponent from reflex.event import EventType @@ -78,6 +80,54 @@ class DataEditorTheme(Base): def on_edit_spec(pos, data: dict[str, Any]): ... +class Bounds(TypedDict): + x: int + y: int + width: int + height: int + +class CompatSelection(TypedDict): + items: list + +class Rectangle(TypedDict): + x: int + y: int + width: int + height: int + +class GridSelectionCurrent(TypedDict): + cell: list[int] + range: Rectangle + rangeStack: list[Rectangle] + +class GridSelection(TypedDict): + current: Optional[GridSelectionCurrent] + columns: CompatSelection + rows: CompatSelection + +class GroupHeaderClickedEventArgs(TypedDict): + kind: str + group: str + location: list[int] + bounds: Bounds + isEdge: bool + shiftKey: bool + ctrlKey: bool + metaKey: bool + isTouch: bool + localEventX: int + localEventY: int + button: int + buttons: int + scrollEdge: list[int] + +class GridCell(TypedDict): + span: Optional[List[int]] + +class GridColumn(TypedDict): + title: str + group: Optional[str] + class DataEditor(NoSSRComponent): def add_imports(self) -> ImportDict: ... def add_hooks(self) -> list[str]: ... @@ -136,24 +186,28 @@ class DataEditor(NoSSRComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_cell_activated: Optional[EventType] = None, - on_cell_clicked: Optional[EventType] = None, - on_cell_context_menu: Optional[EventType] = None, + on_cell_activated: Optional[EventType[tuple[int, int]]] = None, + on_cell_clicked: Optional[EventType[tuple[int, int]]] = None, + on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None, on_cell_edited: Optional[EventType] = None, on_click: Optional[EventType[[]]] = None, - on_column_resize: Optional[EventType] = None, + on_column_resize: Optional[EventType[GridColumn, int]] = None, on_context_menu: Optional[EventType[[]]] = None, - on_delete: Optional[EventType] = None, + on_delete: Optional[EventType[GridSelection]] = None, on_double_click: Optional[EventType[[]]] = None, - on_finished_editing: Optional[EventType] = None, + on_finished_editing: Optional[ + EventType[Union[GridCell, None], list[int]] + ] = None, on_focus: Optional[EventType[[]]] = None, on_group_header_clicked: Optional[EventType] = None, - on_group_header_context_menu: Optional[EventType] = None, - on_group_header_renamed: Optional[EventType] = None, - on_header_clicked: Optional[EventType] = None, - on_header_context_menu: Optional[EventType] = None, - on_header_menu_click: Optional[EventType] = None, - on_item_hovered: Optional[EventType] = None, + on_group_header_context_menu: Optional[ + EventType[int, GroupHeaderClickedEventArgs] + ] = None, + on_group_header_renamed: Optional[EventType[str, str]] = None, + on_header_clicked: Optional[EventType[tuple[int, int]]] = None, + on_header_context_menu: Optional[EventType[tuple[int, int]]] = None, + on_header_menu_click: Optional[EventType[int, Rectangle]] = None, + on_item_hovered: Optional[EventType[tuple[int, int]]] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, on_mouse_enter: Optional[EventType[[]]] = None, diff --git a/reflex/components/moment/moment.pyi b/reflex/components/moment/moment.pyi index 4f58fda7d..ccffbb8d1 100644 --- a/reflex/components/moment/moment.pyi +++ b/reflex/components/moment/moment.pyi @@ -58,7 +58,7 @@ class Moment(NoSSRComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/primitives/drawer.pyi b/reflex/components/radix/primitives/drawer.pyi index 9c5463e56..c4493ee9b 100644 --- a/reflex/components/radix/primitives/drawer.pyi +++ b/reflex/components/radix/primitives/drawer.pyi @@ -101,7 +101,7 @@ class DrawerRoot(DrawerComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -511,7 +511,7 @@ class Drawer(ComponentNamespace): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/color_mode.pyi b/reflex/components/radix/themes/color_mode.pyi index 43703dc37..d856b0bbd 100644 --- a/reflex/components/radix/themes/color_mode.pyi +++ b/reflex/components/radix/themes/color_mode.pyi @@ -383,7 +383,7 @@ class ColorModeSwitch(Switch): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[bool]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/themes/components/alert_dialog.pyi b/reflex/components/radix/themes/components/alert_dialog.pyi index d63fcae53..771def7d9 100644 --- a/reflex/components/radix/themes/components/alert_dialog.pyi +++ b/reflex/components/radix/themes/components/alert_dialog.pyi @@ -42,7 +42,7 @@ class AlertDialogRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/checkbox.pyi b/reflex/components/radix/themes/components/checkbox.pyi index fad4f5210..90a9220ef 100644 --- a/reflex/components/radix/themes/components/checkbox.pyi +++ b/reflex/components/radix/themes/components/checkbox.pyi @@ -116,7 +116,7 @@ class Checkbox(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[bool]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -263,7 +263,7 @@ class HighLevelCheckbox(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[bool]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -407,7 +407,7 @@ class CheckboxNamespace(ComponentNamespace): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[bool]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/themes/components/context_menu.pyi b/reflex/components/radix/themes/components/context_menu.pyi index fbefa88de..56d8200e0 100644 --- a/reflex/components/radix/themes/components/context_menu.pyi +++ b/reflex/components/radix/themes/components/context_menu.pyi @@ -39,7 +39,7 @@ class ContextMenuRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/dialog.pyi b/reflex/components/radix/themes/components/dialog.pyi index e3f17d7e8..0713461e9 100644 --- a/reflex/components/radix/themes/components/dialog.pyi +++ b/reflex/components/radix/themes/components/dialog.pyi @@ -40,7 +40,7 @@ class DialogRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -382,7 +382,7 @@ class Dialog(ComponentNamespace): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/dropdown_menu.pyi b/reflex/components/radix/themes/components/dropdown_menu.pyi index dba619e7d..8de273be9 100644 --- a/reflex/components/radix/themes/components/dropdown_menu.pyi +++ b/reflex/components/radix/themes/components/dropdown_menu.pyi @@ -49,7 +49,7 @@ class DropdownMenuRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -363,7 +363,7 @@ class DropdownMenuSub(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/hover_card.pyi b/reflex/components/radix/themes/components/hover_card.pyi index 8924ef1a8..fa169c852 100644 --- a/reflex/components/radix/themes/components/hover_card.pyi +++ b/reflex/components/radix/themes/components/hover_card.pyi @@ -43,7 +43,7 @@ class HoverCardRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -256,7 +256,7 @@ class HoverCard(ComponentNamespace): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/popover.pyi b/reflex/components/radix/themes/components/popover.pyi index 984a139d0..218392517 100644 --- a/reflex/components/radix/themes/components/popover.pyi +++ b/reflex/components/radix/themes/components/popover.pyi @@ -41,7 +41,7 @@ class PopoverRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/radio_cards.pyi b/reflex/components/radix/themes/components/radio_cards.pyi index d73447622..d2f6a1425 100644 --- a/reflex/components/radix/themes/components/radio_cards.pyi +++ b/reflex/components/radix/themes/components/radio_cards.pyi @@ -177,7 +177,7 @@ class RadioCardsRoot(RadixThemesComponent): on_mouse_up: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, - on_value_change: Optional[EventType] = None, + on_value_change: Optional[EventType[str]] = None, **props, ) -> "RadioCardsRoot": """Create a new component instance. diff --git a/reflex/components/radix/themes/components/radio_group.pyi b/reflex/components/radix/themes/components/radio_group.pyi index c984fa1f2..f928421c5 100644 --- a/reflex/components/radix/themes/components/radio_group.pyi +++ b/reflex/components/radix/themes/components/radio_group.pyi @@ -113,7 +113,7 @@ class RadioGroupRoot(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/themes/components/select.pyi b/reflex/components/radix/themes/components/select.pyi index c43d58ada..e0e184482 100644 --- a/reflex/components/radix/themes/components/select.pyi +++ b/reflex/components/radix/themes/components/select.pyi @@ -44,7 +44,7 @@ class SelectRoot(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -57,7 +57,7 @@ class SelectRoot(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -680,7 +680,7 @@ class HighLevelSelect(SelectRoot): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -693,7 +693,7 @@ class HighLevelSelect(SelectRoot): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, @@ -854,7 +854,7 @@ class Select(ComponentNamespace): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -867,7 +867,7 @@ class Select(ComponentNamespace): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/radix/themes/components/switch.pyi b/reflex/components/radix/themes/components/switch.pyi index ba9c2595e..f8871872a 100644 --- a/reflex/components/radix/themes/components/switch.pyi +++ b/reflex/components/radix/themes/components/switch.pyi @@ -119,7 +119,7 @@ class Switch(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[bool]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/themes/components/tabs.pyi b/reflex/components/radix/themes/components/tabs.pyi index 7b67bad6e..4272bf2a3 100644 --- a/reflex/components/radix/themes/components/tabs.pyi +++ b/reflex/components/radix/themes/components/tabs.pyi @@ -41,7 +41,7 @@ class TabsRoot(RadixThemesComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, @@ -340,7 +340,7 @@ class Tabs(ComponentNamespace): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[[]]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, diff --git a/reflex/components/radix/themes/components/tooltip.pyi b/reflex/components/radix/themes/components/tooltip.pyi index ad7c4402f..ac2a36368 100644 --- a/reflex/components/radix/themes/components/tooltip.pyi +++ b/reflex/components/radix/themes/components/tooltip.pyi @@ -76,7 +76,7 @@ class Tooltip(RadixThemesComponent): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_pointer_down_outside: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, diff --git a/reflex/components/react_player/audio.pyi b/reflex/components/react_player/audio.pyi index d1f29f508..2556c8e83 100644 --- a/reflex/components/react_player/audio.pyi +++ b/reflex/components/react_player/audio.pyi @@ -41,7 +41,7 @@ class Audio(ReactPlayer): on_context_menu: Optional[EventType[[]]] = None, on_disable_pip: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_duration: Optional[EventType] = None, + on_duration: Optional[EventType[float]] = None, on_enable_pip: Optional[EventType[[]]] = None, on_ended: Optional[EventType[[]]] = None, on_error: Optional[EventType[[]]] = None, @@ -61,7 +61,7 @@ class Audio(ReactPlayer): on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, - on_seek: Optional[EventType] = None, + on_seek: Optional[EventType[float]] = None, on_start: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/react_player/react_player.pyi b/reflex/components/react_player/react_player.pyi index 940b09e51..9a445c294 100644 --- a/reflex/components/react_player/react_player.pyi +++ b/reflex/components/react_player/react_player.pyi @@ -39,7 +39,7 @@ class ReactPlayer(NoSSRComponent): on_context_menu: Optional[EventType[[]]] = None, on_disable_pip: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_duration: Optional[EventType] = None, + on_duration: Optional[EventType[float]] = None, on_enable_pip: Optional[EventType[[]]] = None, on_ended: Optional[EventType[[]]] = None, on_error: Optional[EventType[[]]] = None, @@ -59,7 +59,7 @@ class ReactPlayer(NoSSRComponent): on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, - on_seek: Optional[EventType] = None, + on_seek: Optional[EventType[float]] = None, on_start: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/react_player/video.pyi b/reflex/components/react_player/video.pyi index a50ccf71f..d46e2617d 100644 --- a/reflex/components/react_player/video.pyi +++ b/reflex/components/react_player/video.pyi @@ -41,7 +41,7 @@ class Video(ReactPlayer): on_context_menu: Optional[EventType[[]]] = None, on_disable_pip: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None, - on_duration: Optional[EventType] = None, + on_duration: Optional[EventType[float]] = None, on_enable_pip: Optional[EventType[[]]] = None, on_ended: Optional[EventType[[]]] = None, on_error: Optional[EventType[[]]] = None, @@ -61,7 +61,7 @@ class Video(ReactPlayer): on_progress: Optional[EventType] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, - on_seek: Optional[EventType] = None, + on_seek: Optional[EventType[float]] = None, on_start: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/components/suneditor/editor.pyi b/reflex/components/suneditor/editor.pyi index 5cc45dc98..73dd38fdc 100644 --- a/reflex/components/suneditor/editor.pyi +++ b/reflex/components/suneditor/editor.pyi @@ -128,7 +128,7 @@ class Editor(NoSSRComponent): autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, on_blur: Optional[EventType[str]] = None, - on_change: Optional[EventType] = None, + on_change: Optional[EventType[str]] = None, on_click: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None, on_copy: Optional[EventType[[]]] = None, @@ -136,7 +136,7 @@ class Editor(NoSSRComponent): on_double_click: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None, on_input: Optional[EventType[[]]] = None, - on_load: Optional[EventType] = None, + on_load: Optional[EventType[bool]] = None, on_mount: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None, on_mouse_enter: Optional[EventType[[]]] = None, @@ -148,8 +148,8 @@ class Editor(NoSSRComponent): on_paste: Optional[EventType[str, bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, - toggle_code_view: Optional[EventType] = None, - toggle_full_screen: Optional[EventType] = None, + toggle_code_view: Optional[EventType[bool]] = None, + toggle_full_screen: Optional[EventType[bool]] = None, **props, ) -> "Editor": """Create an instance of Editor. No children allowed. diff --git a/reflex/event.py b/reflex/event.py index 76a465739..cfe40ef9a 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -24,7 +24,7 @@ from typing import ( overload, ) -from typing_extensions import ParamSpec, get_args, get_origin +from typing_extensions import ParamSpec, Protocol, get_args, get_origin from reflex import constants from reflex.utils import console, format @@ -465,33 +465,97 @@ prevent_default = EventChain(events=[], args_spec=empty_event).prevent_default T = TypeVar("T") +U = TypeVar("U") -def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]: +# def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]: +# """A helper function that returns the input event as output. + +# Args: +# event_type: The type of the event. + +# Returns: +# A function that returns the input event as output. +# """ + +# def inner(ev: Var[T]) -> Tuple[Var[T]]: +# return (ev,) + +# inner.__signature__ = inspect.signature(inner).replace( # type: ignore +# parameters=[ +# inspect.Parameter( +# "ev", +# kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, +# annotation=Var[event_type], +# ) +# ], +# return_annotation=Tuple[Var[event_type]], +# ) +# inner.__annotations__["ev"] = Var[event_type] +# inner.__annotations__["return"] = Tuple[Var[event_type]] + +# return inner + + +class IdentityEventReturn(Generic[T], Protocol): + """Protocol for an identity event return.""" + + def __call__(self, *values: Var[T]) -> Tuple[Var[T], ...]: + """Return the input values. + + Args: + *values: The values to return. + + Returns: + The input values. + """ + return values + + +@overload +def identity_event(event_type: Type[T], /) -> Callable[[Var[T]], Tuple[Var[T]]]: ... # type: ignore + + +@overload +def identity_event( + event_type_1: Type[T], event_type2: Type[U], / +) -> Callable[[Var[T], Var[U]], Tuple[Var[T], Var[U]]]: ... + + +@overload +def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]: ... + + +def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]: # type: ignore """A helper function that returns the input event as output. Args: - event_type: The type of the event. + *event_types: The types of the events. Returns: A function that returns the input event as output. """ - def inner(ev: Var[T]) -> Tuple[Var[T]]: - return (ev,) + def inner(*values: Var[T]) -> Tuple[Var[T], ...]: + return values + + inner_type = tuple(Var[event_type] for event_type in event_types) + return_annotation = Tuple[inner_type] # type: ignore inner.__signature__ = inspect.signature(inner).replace( # type: ignore parameters=[ inspect.Parameter( - "ev", + f"ev_{i}", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Var[event_type], ) + for i, event_type in enumerate(event_types) ], - return_annotation=Tuple[Var[event_type]], + return_annotation=return_annotation, ) - inner.__annotations__["ev"] = Var[event_type] - inner.__annotations__["return"] = Tuple[Var[event_type]] + for i, event_type in enumerate(event_types): + inner.__annotations__[f"ev_{i}"] = Var[event_type] + inner.__annotations__["return"] = return_annotation return inner diff --git a/reflex/experimental/layout.pyi b/reflex/experimental/layout.pyi index e4c82b351..dcdac5b5d 100644 --- a/reflex/experimental/layout.pyi +++ b/reflex/experimental/layout.pyi @@ -129,7 +129,7 @@ class DrawerSidebar(DrawerRoot): on_mouse_out: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, - on_open_change: Optional[EventType] = None, + on_open_change: Optional[EventType[bool]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, **props, diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index fd76576b9..026a53bca 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -16,7 +16,7 @@ from itertools import chain from multiprocessing import Pool, cpu_count from pathlib import Path from types import ModuleType, SimpleNamespace -from typing import Any, Callable, Iterable, Type, get_args +from typing import Any, Callable, Iterable, Type, get_args, get_origin from reflex.components.component import Component from reflex.utils import types as rx_types @@ -372,6 +372,53 @@ def _extract_class_props_as_ast_nodes( return kwargs +def type_to_ast(typ) -> ast.AST: + """Converts any type annotation into its AST representation. + Handles nested generic types, unions, etc. + + Args: + typ: The type annotation to convert. + + Returns: + The AST representation of the type annotation. + """ + if typ is type(None): + return ast.Name(id="None") + + origin = get_origin(typ) + + # Handle plain types (int, str, custom classes, etc.) + if origin is None: + if hasattr(typ, "__name__"): + return ast.Name(id=typ.__name__) + elif hasattr(typ, "_name"): + return ast.Name(id=typ._name) + return ast.Name(id=str(typ)) + + # Get the base type name (List, Dict, Optional, etc.) + base_name = origin._name if hasattr(origin, "_name") else origin.__name__ + + # Get type arguments + args = get_args(typ) + + # Handle empty type arguments + if not args: + return ast.Name(id=base_name) + + # Convert all type arguments recursively + arg_nodes = [type_to_ast(arg) for arg in args] + + # Special case for single-argument types (like List[T] or Optional[T]) + if len(arg_nodes) == 1: + slice_value = arg_nodes[0] + else: + slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load()) + + return ast.Subscript( + value=ast.Name(id=base_name), slice=ast.Index(value=slice_value), ctx=ast.Load() + ) + + def _get_parent_imports(func): _imports = {"reflex.vars": ["Var"]} for type_hint in inspect.get_annotations(func).values(): @@ -430,13 +477,40 @@ def _generate_component_create_functiondef( def figure_out_return_type(annotation: Any): if inspect.isclass(annotation) and issubclass(annotation, inspect._empty): return ast.Name(id="Optional[EventType]") + + if not isinstance(annotation, str) and get_origin(annotation) is tuple: + arguments = get_args(annotation) + + arguments_without_var = [ + get_args(argument)[0] if get_origin(argument) == Var else argument + for argument in arguments + ] + + # Convert each argument type to its AST representation + type_args = [type_to_ast(arg) for arg in arguments_without_var] + + # Join the type arguments with commas for EventType + args_str = ", ".join(ast.unparse(arg) for arg in type_args) + + # Create EventType using the joined string + event_type = ast.Name(id=f"EventType[{args_str}]") + + # Wrap in Optional + optional_type = ast.Subscript( + value=ast.Name(id="Optional"), + slice=ast.Index(value=event_type), + ctx=ast.Load(), + ) + + return ast.Name(id=ast.unparse(optional_type)) + if isinstance(annotation, str) and annotation.startswith("Tuple["): inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]") if inside_of_tuple == "()": return ast.Name(id="Optional[EventType[[]]]") - arguments: list[str] = [""] + arguments = [""] bracket_count = 0 diff --git a/tests/integration/test_lifespan.py b/tests/integration/test_lifespan.py index f8bcf2397..22c399c07 100644 --- a/tests/integration/test_lifespan.py +++ b/tests/integration/test_lifespan.py @@ -51,6 +51,7 @@ def LifespanApp(): def context_global(self) -> int: return lifespan_context_global + @rx.event def tick(self, date): pass From d63b3a2bceb4ed1029487e7aff6c51ebd8b0fc3f Mon Sep 17 00:00:00 2001 From: Elijah Ahianyo Date: Tue, 22 Oct 2024 17:01:34 +0000 Subject: [PATCH 22/27] [ENG-3848][ENG-3861]Shiki Code block Experimental (#4030) * Shiki Code block Experimental * refactor * update code * remove console.log * add transformers to namespace * some validations * fix components paths * fix ruff * add a high-level component * fix mapping * fix mapping * python 3.9+ * see if this fixes the tests * fix pyi and annotations * minimal update of theme and language map * add hack for reflex-web/flexdown * unit test file commit * [ENG-3895] [ENG-3896] Update styling for shiki code block * strip transformer triggers * minor refactor * linter * fix pyright * pyi fix * add unit tests * sneaky pyright ignore * the transformer trigger regex should remove the language comment character * minor refactor * fix silly mistake * component mapping in markdown should use the first child for codeblock * use ternary operator in numbers.py, move code block args to class for docs discoverability * precommit * pyright fix * remove id on copy button animation * pyright fix for real * pyi fix * pyi fix fr * check if svg exists * copy event chain * do a concatenation instead of first child * added comment --------- Co-authored-by: Carlos --- .../.templates/web/components/shiki/code.js | 29 + .../datadisplay/shiki_code_block.py | 813 ++++++ .../datadisplay/shiki_code_block.pyi | 2211 +++++++++++++++++ reflex/components/markdown/markdown.py | 13 +- reflex/experimental/__init__.py | 2 + reflex/vars/function.py | 1 + reflex/vars/sequence.py | 20 + .../components/datadisplay/test_shiki_code.py | 172 ++ 8 files changed, 3260 insertions(+), 1 deletion(-) create mode 100644 reflex/.templates/web/components/shiki/code.js create mode 100644 reflex/components/datadisplay/shiki_code_block.py create mode 100644 reflex/components/datadisplay/shiki_code_block.pyi create mode 100644 tests/units/components/datadisplay/test_shiki_code.py diff --git a/reflex/.templates/web/components/shiki/code.js b/reflex/.templates/web/components/shiki/code.js new file mode 100644 index 000000000..c655c3a60 --- /dev/null +++ b/reflex/.templates/web/components/shiki/code.js @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react" +import { codeToHtml} from "shiki" + +export function Code ({code, theme, language, transformers, ...divProps}) { + const [codeResult, setCodeResult] = useState("") + useEffect(() => { + async function fetchCode() { + let final_code; + + if (Array.isArray(code)) { + final_code = code[0]; + } else { + final_code = code; + } + const result = await codeToHtml(final_code, { + lang: language, + theme, + transformers + }); + setCodeResult(result); + } + fetchCode(); + }, [code, language, theme, transformers] + + ) + return ( +
+ ) +} diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py new file mode 100644 index 000000000..46199a6e4 --- /dev/null +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -0,0 +1,813 @@ +"""Shiki syntax hghlighter component.""" + +from __future__ import annotations + +import re +from collections import defaultdict +from typing import Any, Literal, Optional, Union + +from reflex.base import Base +from reflex.components.component import Component, ComponentNamespace +from reflex.components.core.colors import color +from reflex.components.core.cond import color_mode_cond +from reflex.components.el.elements.forms import Button +from reflex.components.lucide.icon import Icon +from reflex.components.radix.themes.layout.box import Box +from reflex.event import call_script, set_clipboard +from reflex.style import Style +from reflex.utils.exceptions import VarTypeError +from reflex.utils.imports import ImportVar +from reflex.vars.base import LiteralVar, Var +from reflex.vars.function import FunctionStringVar +from reflex.vars.sequence import StringVar, string_replace_operation + + +def copy_script() -> Any: + """Copy script for the code block and modify the child SVG element. + + + Returns: + Any: The result of calling the script. + """ + return call_script( + f""" +// Event listener for the parent click +document.addEventListener('click', function(event) {{ + // Find the closest div (parent element) + const parent = event.target.closest('div'); + // If the parent is found + if (parent) {{ + // Find the SVG element within the parent + const svgIcon = parent.querySelector('svg'); + // If the SVG exists, proceed with the script + if (svgIcon) {{ + const originalPath = svgIcon.innerHTML; + const checkmarkPath = ''; // Checkmark SVG path + function transition(element, scale, opacity) {{ + element.style.transform = `scale(${{scale}})`; + element.style.opacity = opacity; + }} + // Animate the SVG + transition(svgIcon, 0, '0'); + setTimeout(() => {{ + svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark + svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary + transition(svgIcon, 1, '1'); + setTimeout(() => {{ + transition(svgIcon, 0, '0'); + setTimeout(() => {{ + svgIcon.innerHTML = originalPath; // Restore original SVG content + transition(svgIcon, 1, '1'); + }}, 125); + }}, 600); + }}, 125); + }} else {{ + // console.error('SVG element not found within the parent.'); + }} + }} else {{ + // console.error('Parent element not found.'); + }} +}}); +""" + ) + + +SHIKIJS_TRANSFORMER_FNS = { + "transformerNotationDiff", + "transformerNotationHighlight", + "transformerNotationWordHighlight", + "transformerNotationFocus", + "transformerNotationErrorLevel", + "transformerRenderWhitespace", + "transformerMetaHighlight", + "transformerMetaWordHighlight", + "transformerCompactLineOptions", + # TODO: this transformer when included adds a weird behavior which removes other code lines. Need to figure out why. + # "transformerRemoveLineBreak", + "transformerRemoveNotationEscape", +} +LINE_NUMBER_STYLING = { + "code": { + "counter-reset": "step", + "counter-increment": "step 0", + "display": "grid", + "line-height": "1.7", + "font-size": "0.875em", + }, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, +} +BOX_PARENT_STYLING = { + "pre": { + "margin": "0", + "padding": "24px", + "background": "transparent", + "overflow-x": "auto", + "border-radius": "6px", + }, +} + +THEME_MAPPING = { + "light": "one-light", + "dark": "one-dark-pro", + "a11y-dark": "github-dark", +} +LANGUAGE_MAPPING = {"bash": "shellscript"} +LiteralCodeLanguage = Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", +] +LiteralCodeTheme = Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", +] + + +class ShikiBaseTransformers(Base): + """Base for creating transformers.""" + + library: str + fns: list[FunctionStringVar] + style: Optional[Style] + + +class ShikiJsTransformer(ShikiBaseTransformers): + """A Wrapped shikijs transformer.""" + + library: str = "@shikijs/transformers" + fns: list[FunctionStringVar] = [ + FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS + ] + style: Optional[Style] = Style( + { + "code": {"line-height": "1.7", "font-size": "0.875em", "display": "grid"}, + # Diffs + ".diff": { + "margin": "0 -24px", + "padding": "0 24px", + "width": "calc(100% + 48px)", + "display": "inline-block", + }, + ".diff.add": { + "background-color": "rgba(16, 185, 129, .14)", + "position": "relative", + }, + ".diff.remove": { + "background-color": "rgba(244, 63, 94, .14)", + "opacity": "0.7", + "position": "relative", + }, + ".diff.remove:after": { + "position": "absolute", + "left": "10px", + "content": "'-'", + "color": "#b34e52", + }, + ".diff.add:after": { + "position": "absolute", + "left": "10px", + "content": "'+'", + "color": "#18794e", + }, + # Highlight + ".highlighted": { + "background-color": "rgba(142, 150, 170, .14)", + "margin": "0 -24px", + "padding": "0 24px", + "width": "calc(100% + 48px)", + "display": "inline-block", + }, + ".highlighted.error": { + "background-color": "rgba(244, 63, 94, .14)", + }, + ".highlighted.warning": { + "background-color": "rgba(234, 179, 8, .14)", + }, + # Highlighted Word + ".highlighted-word": { + "background-color": color("gray", 2), + "border": f"1px solid {color('gray', 5)}", + "padding": "1px 3px", + "margin": "-1px -3px", + "border-radius": "4px", + }, + # Focused Lines + ".has-focused .line:not(.focused)": { + "opacity": "0.7", + "filter": "blur(0.095rem)", + "transition": "filter .35s, opacity .35s", + }, + ".has-focused:hover .line:not(.focused)": { + "opacity": "1", + "filter": "none", + }, + # White Space + # ".tab, .space": { + # "position": "relative", + # }, + # ".tab::before": { + # "content": "'⇥'", + # "position": "absolute", + # "opacity": "0.3", + # }, + # ".space::before": { + # "content": "'·'", + # "position": "absolute", + # "opacity": "0.3", + # }, + } + ) + + def __init__(self, **kwargs): + """Initialize the transformer. + + Args: + kwargs: Kwargs to initialize the props. + + """ + fns = kwargs.pop("fns", None) + style = kwargs.pop("style", None) + if fns: + kwargs["fns"] = [ + ( + FunctionStringVar.create(x) + if not isinstance(x, FunctionStringVar) + else x + ) + for x in fns + ] + + if style: + kwargs["style"] = Style(style) + super().__init__(**kwargs) + + +class ShikiCodeBlock(Component): + """A Code block.""" + + library = "/components/shiki/code" + + tag = "Code" + + alias = "ShikiCode" + + lib_dependencies: list[str] = ["shiki"] + + # The language to use. + language: Var[LiteralCodeLanguage] = Var.create("python") + + # The theme to use ("light" or "dark"). + theme: Var[LiteralCodeTheme] = Var.create("one-light") + + # The set of themes to use for different modes. + themes: Var[Union[list[dict[str, Any]], dict[str, str]]] + + # The code to display. + code: Var[str] + + # The transformers to use for the syntax highlighter. + transformers: Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]] = Var.create( + [] + ) + + @classmethod + def create( + cls, + *children, + **props, + ) -> Component: + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + **props: The props to pass to the component. + + Returns: + The code block component. + """ + # Separate props for the code block and the wrapper + code_block_props = {} + code_wrapper_props = {} + + class_props = cls.get_props() + + # Distribute props between the code block and wrapper + for key, value in props.items(): + (code_block_props if key in class_props else code_wrapper_props)[key] = ( + value + ) + + code_block_props["code"] = children[0] + code_block = super().create(**code_block_props) + + transformer_styles = {} + # Collect styles from transformers and wrapper + for transformer in code_block.transformers._var_value: # type: ignore + if isinstance(transformer, ShikiBaseTransformers) and transformer.style: + transformer_styles.update(transformer.style) + transformer_styles.update(code_wrapper_props.pop("style", {})) + + return Box.create( + code_block, + *children[1:], + style=Style({**transformer_styles, **BOX_PARENT_STYLING}), + **code_wrapper_props, + ) + + def add_imports(self) -> dict[str, list[str]]: + """Add the necessary imports. + We add all referenced transformer functions as imports from their corresponding + libraries. + + Returns: + Imports for the component. + """ + imports = defaultdict(list) + for transformer in self.transformers._var_value: + if isinstance(transformer, ShikiBaseTransformers): + imports[transformer.library].extend( + [ImportVar(tag=str(fn)) for fn in transformer.fns] + ) + ( + self.lib_dependencies.append(transformer.library) + if transformer.library not in self.lib_dependencies + else None + ) + return imports + + @classmethod + def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: + """Create a transformer from a third party library. + + Args: + library: The name of the library. + fns: The str names of the functions/callables to invoke from the library. + + Returns: + A transformer for the specified library. + + Raises: + ValueError: If a supplied function name is not valid str. + """ + if any(not isinstance(fn_name, str) for fn_name in fns): + raise ValueError( + f"the function names should be str names of functions in the specified transformer: {library!r}" + ) + return ShikiBaseTransformers( # type: ignore + library=library, fns=[FunctionStringVar.create(fn) for fn in fns] + ) + + def _render(self, props: dict[str, Any] | None = None): + """Renders the component with the given properties, processing transformers if present. + + Args: + props: Optional properties to pass to the render function. + + Returns: + Rendered component output. + """ + # Ensure props is initialized from class attributes if not provided + props = props or { + attr.rstrip("_"): getattr(self, attr) for attr in self.get_props() + } + + # Extract transformers and apply transformations + transformers = props.get("transformers") + if transformers is not None: + transformed_values = self._process_transformers(transformers._var_value) + props["transformers"] = LiteralVar.create(transformed_values) + + return super()._render(props) + + def _process_transformers(self, transformer_list: list) -> list: + """Processes a list of transformers, applying transformations where necessary. + + Args: + transformer_list: List of transformer objects or values. + + Returns: + list: A list of transformed values. + """ + processed = [] + + for transformer in transformer_list: + if isinstance(transformer, ShikiBaseTransformers): + processed.extend(fn.call() for fn in transformer.fns) + else: + processed.append(transformer) + + return processed + + +class ShikiHighLevelCodeBlock(ShikiCodeBlock): + """High level component for the shiki syntax highlighter.""" + + # If this is enabled, the default transformers(shikijs transformer) will be used. + use_transformers: Var[bool] + + # If this is enabled line numbers will be shown next to the code block. + show_line_numbers: Var[bool] + + # Whether a copy button should appear. + can_copy: Var[bool] = Var.create(False) + + # copy_button: A custom copy button to override the default one. + copy_button: Var[Optional[Union[Component, bool]]] = Var.create(None) + + @classmethod + def create( + cls, + *children, + **props, + ) -> Component: + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + **props: The props to pass to the component. + + Returns: + The code block component. + """ + use_transformers = props.pop("use_transformers", False) + show_line_numbers = props.pop("show_line_numbers", False) + language = props.pop("language", None) + can_copy = props.pop("can_copy", False) + copy_button = props.pop("copy_button", None) + + if use_transformers: + props["transformers"] = [ShikiJsTransformer()] + + if language is not None: + props["language"] = cls._map_languages(language) + + # line numbers are generated via css + if show_line_numbers: + props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})} + + theme = props.pop("theme", None) + props["theme"] = props["theme"] = ( + cls._map_themes(theme) + if theme is not None + else color_mode_cond( # Default color scheme responds to global color mode. + light="one-light", + dark="one-dark-pro", + ) + ) + + if can_copy: + code = children[0] + copy_button = ( # type: ignore + copy_button + if copy_button is not None + else Button.create( + Icon.create(tag="copy", size=16, color=color("gray", 11)), + on_click=[ + set_clipboard(cls._strip_transformer_triggers(code)), # type: ignore + copy_script(), + ], + style=Style( + { + "position": "absolute", + "top": "4px", + "right": "4px", + "background": color("gray", 3), + "border": "1px solid", + "border-color": color("gray", 5), + "border-radius": "6px", + "padding": "5px", + "opacity": "1", + "cursor": "pointer", + "_hover": { + "background": color("gray", 4), + }, + "transition": "background 0.250s ease-out", + "&>svg": { + "transition": "transform 0.250s ease-out, opacity 0.250s ease-out", + }, + "_active": { + "background": color("gray", 5), + }, + } + ), + ) + ) + + if copy_button: + return ShikiCodeBlock.create( + children[0], copy_button, position="relative", **props + ) + else: + return ShikiCodeBlock.create(children[0], **props) + + @staticmethod + def _map_themes(theme: str) -> str: + if isinstance(theme, str) and theme in THEME_MAPPING: + return THEME_MAPPING[theme] + return theme + + @staticmethod + def _map_languages(language: str) -> str: + if isinstance(language, str) and language in LANGUAGE_MAPPING: + return LANGUAGE_MAPPING[language] + return language + + @staticmethod + def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: + if not isinstance(code, (StringVar, str)): + raise VarTypeError( + f"code should be string literal or a StringVar type. Got {type(code)} instead." + ) + regex_pattern = r"[\/#]+ *\[!code.*?\]" + + if isinstance(code, Var): + return string_replace_operation( + code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), "" + ) + if isinstance(code, str): + return re.sub(regex_pattern, "", code) + + +class TransformerNamespace(ComponentNamespace): + """Namespace for the Transformers.""" + + shikijs = ShikiJsTransformer + + +class CodeblockNamespace(ComponentNamespace): + """Namespace for the CodeBlock component.""" + + root = staticmethod(ShikiCodeBlock.create) + create_transformer = staticmethod(ShikiCodeBlock.create_transformer) + transformers = TransformerNamespace() + __call__ = staticmethod(ShikiHighLevelCodeBlock.create) + + +code_block = CodeblockNamespace() diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi new file mode 100644 index 000000000..bcf2719e9 --- /dev/null +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -0,0 +1,2211 @@ +"""Stub file for reflex/components/datadisplay/shiki_code_block.py""" + +# ------------------- DO NOT EDIT ---------------------- +# This file was generated by `reflex/utils/pyi_generator.py`! +# ------------------------------------------------------ +from typing import Any, Dict, Literal, Optional, Union, overload + +from reflex.base import Base +from reflex.components.component import Component, ComponentNamespace +from reflex.event import EventType +from reflex.style import Style +from reflex.vars.base import Var +from reflex.vars.function import FunctionStringVar + +def copy_script() -> Any: ... + +SHIKIJS_TRANSFORMER_FNS = { + "transformerNotationDiff", + "transformerNotationHighlight", + "transformerNotationWordHighlight", + "transformerNotationFocus", + "transformerNotationErrorLevel", + "transformerRenderWhitespace", + "transformerMetaHighlight", + "transformerMetaWordHighlight", + "transformerCompactLineOptions", + "transformerRemoveNotationEscape", +} +LINE_NUMBER_STYLING = { + "code": { + "counter-reset": "step", + "counter-increment": "step 0", + "display": "grid", + "line-height": "1.7", + "font-size": "0.875em", + }, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, +} +BOX_PARENT_STYLING = { + "pre": { + "margin": "0", + "padding": "24px", + "background": "transparent", + "overflow-x": "auto", + "border-radius": "6px", + } +} +THEME_MAPPING = { + "light": "one-light", + "dark": "one-dark-pro", + "a11y-dark": "github-dark", +} +LANGUAGE_MAPPING = {"bash": "shellscript"} +LiteralCodeLanguage = Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", +] +LiteralCodeTheme = Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", +] + +class ShikiBaseTransformers(Base): + library: str + fns: list[FunctionStringVar] + style: Optional[Style] + +class ShikiJsTransformer(ShikiBaseTransformers): + library: str + fns: list[FunctionStringVar] + style: Optional[Style] + +class ShikiCodeBlock(Component): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]] = None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]] = None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, + **props, + ) -> "ShikiCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + + def add_imports(self) -> dict[str, list[str]]: ... + @classmethod + def create_transformer( + cls, library: str, fns: list[str] + ) -> ShikiBaseTransformers: ... + +class ShikiHighLevelCodeBlock(ShikiCodeBlock): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + use_transformers: Optional[Union[Var[bool], bool]] = None, + show_line_numbers: Optional[Union[Var[bool], bool]] = None, + can_copy: Optional[Union[Var[bool], bool]] = None, + copy_button: Optional[ + Union[Component, Var[Optional[Union[Component, bool]]], bool] + ] = None, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]] = None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]] = None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, + **props, + ) -> "ShikiHighLevelCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. + show_line_numbers: If this is enabled line numbers will be shown next to the code block. + can_copy: Whether a copy button should appear. + copy_button: copy_button: A custom copy button to override the default one. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + +class TransformerNamespace(ComponentNamespace): + shikijs = ShikiJsTransformer + +class CodeblockNamespace(ComponentNamespace): + root = staticmethod(ShikiCodeBlock.create) + create_transformer = staticmethod(ShikiCodeBlock.create_transformer) + transformers = TransformerNamespace() + + @staticmethod + def __call__( + *children, + use_transformers: Optional[Union[Var[bool], bool]] = None, + show_line_numbers: Optional[Union[Var[bool], bool]] = None, + can_copy: Optional[Union[Var[bool], bool]] = None, + copy_button: Optional[ + Union[Component, Var[Optional[Union[Component, bool]]], bool] + ] = None, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plain", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]] = None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]] = None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, + **props, + ) -> "ShikiHighLevelCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. + show_line_numbers: If this is enabled line numbers will be shown next to the code block. + can_copy: Whether a copy button should appear. + copy_button: copy_button: A custom copy button to override the default one. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + +code_block = CodeblockNamespace() diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 1665144fd..b790bf7a1 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -20,6 +20,8 @@ from reflex.components.tags.tag import Tag from reflex.utils import types from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var +from reflex.vars.function import ARRAY_ISARRAY +from reflex.vars.number import ternary_operation # Special vars used in the component map. _CHILDREN = Var(_js_expr="children", _var_type=str) @@ -199,7 +201,16 @@ class Markdown(Component): raise ValueError(f"No markdown component found for tag: {tag}.") special_props = [_PROPS_IN_TAG] - children = [_CHILDREN] + children = [ + _CHILDREN + if tag != "codeblock" + # For codeblock, the mapping for some cases returns an array of elements. Let's join them into a string. + else ternary_operation( + ARRAY_ISARRAY.call(_CHILDREN), # type: ignore + _CHILDREN.to(list).join("\n"), + _CHILDREN, + ).to(str) + ] # For certain tags, the props from the markdown renderer are not actually valid for the component. if tag in NO_PROPS_TAGS: diff --git a/reflex/experimental/__init__.py b/reflex/experimental/__init__.py index 0c11deb85..164790fe5 100644 --- a/reflex/experimental/__init__.py +++ b/reflex/experimental/__init__.py @@ -2,6 +2,7 @@ from types import SimpleNamespace +from reflex.components.datadisplay.shiki_code_block import code_block as code_block from reflex.components.props import PropsBase from reflex.components.radix.themes.components.progress import progress as progress from reflex.components.sonner.toast import toast as toast @@ -67,4 +68,5 @@ _x = ExperimentalNamespace( layout=layout, PropsBase=PropsBase, run_in_thread=run_in_thread, + code_block=code_block, ) diff --git a/reflex/vars/function.py b/reflex/vars/function.py index a1f7fb7bd..9d734a458 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -180,6 +180,7 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar): JSON_STRINGIFY = FunctionStringVar.create("JSON.stringify") +ARRAY_ISARRAY = FunctionStringVar.create("Array.isArray") PROTOTYPE_TO_STRING = FunctionStringVar.create( "((__to_string) => __to_string.toString())" ) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 149300028..39139ce3f 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -529,6 +529,26 @@ def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""): return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) +@var_operation +def string_replace_operation( + string: StringVar, search_value: StringVar | str, new_value: StringVar | str +): + """Replace a string with a value. + + Args: + string: The string. + search_value: The string to search. + new_value: The value to be replaced with. + + Returns: + The string replace operation. + """ + return var_operation_return( + js_expression=f"{string}.replace({search_value}, {new_value})", + var_type=str, + ) + + # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" diff --git a/tests/units/components/datadisplay/test_shiki_code.py b/tests/units/components/datadisplay/test_shiki_code.py new file mode 100644 index 000000000..eb473ba06 --- /dev/null +++ b/tests/units/components/datadisplay/test_shiki_code.py @@ -0,0 +1,172 @@ +import pytest + +from reflex.components.datadisplay.shiki_code_block import ( + ShikiBaseTransformers, + ShikiCodeBlock, + ShikiHighLevelCodeBlock, + ShikiJsTransformer, +) +from reflex.components.el.elements.forms import Button +from reflex.components.lucide.icon import Icon +from reflex.components.radix.themes.layout.box import Box +from reflex.style import Style +from reflex.vars import Var + + +@pytest.mark.parametrize( + "library, fns, expected_output, raises_exception", + [ + ("some_library", ["function_one"], ["function_one"], False), + ("some_library", [123], None, True), + ("some_library", [], [], False), + ( + "some_library", + ["function_one", "function_two"], + ["function_one", "function_two"], + False, + ), + ("", ["function_one"], ["function_one"], False), + ("some_library", ["function_one", 789], None, True), + ("", [], [], False), + ], +) +def test_create_transformer(library, fns, expected_output, raises_exception): + if raises_exception: + # Ensure ValueError is raised for invalid cases + with pytest.raises(ValueError): + ShikiCodeBlock.create_transformer(library, fns) + else: + transformer = ShikiCodeBlock.create_transformer(library, fns) + assert isinstance(transformer, ShikiBaseTransformers) + assert transformer.library == library + + # Verify that the functions are correctly wrapped in FunctionStringVar + function_names = [str(fn) for fn in transformer.fns] + assert function_names == expected_output + + +@pytest.mark.parametrize( + "code_block, children, props, expected_first_child, expected_styles", + [ + ("print('Hello')", ["print('Hello')"], {}, "print('Hello')", {}), + ( + "print('Hello')", + ["print('Hello')", "More content"], + {}, + "print('Hello')", + {}, + ), + ( + "print('Hello')", + ["print('Hello')"], + { + "transformers": [ + ShikiBaseTransformers( + library="lib", fns=[], style=Style({"color": "red"}) + ) + ] + }, + "print('Hello')", + {"color": "red"}, + ), + ( + "print('Hello')", + ["print('Hello')"], + { + "transformers": [ + ShikiBaseTransformers( + library="lib", fns=[], style=Style({"color": "red"}) + ) + ], + "style": {"background": "blue"}, + }, + "print('Hello')", + {"color": "red", "background": "blue"}, + ), + ], +) +def test_create_shiki_code_block( + code_block, children, props, expected_first_child, expected_styles +): + component = ShikiCodeBlock.create(code_block, *children, **props) + + # Test that the created component is a Box + assert isinstance(component, Box) + + # Test that the first child is the code + code_block_component = component.children[0] + assert code_block_component.code._var_value == expected_first_child # type: ignore + + applied_styles = component.style + for key, value in expected_styles.items(): + assert Var.create(applied_styles[key])._var_value == value + + +@pytest.mark.parametrize( + "children, props, expected_transformers, expected_button_type", + [ + (["print('Hello')"], {"use_transformers": True}, [ShikiJsTransformer], None), + (["print('Hello')"], {"can_copy": True}, None, Button), + ( + ["print('Hello')"], + { + "can_copy": True, + "copy_button": Button.create(Icon.create(tag="a_arrow_down")), + }, + None, + Button, + ), + ], +) +def test_create_shiki_high_level_code_block( + children, props, expected_transformers, expected_button_type +): + component = ShikiHighLevelCodeBlock.create(*children, **props) + + # Test that the created component is a Box + assert isinstance(component, Box) + + # Test that the first child is the code block component + code_block_component = component.children[0] + assert code_block_component.code._var_value == children[0] # type: ignore + + # Check if the transformer is set correctly if expected + if expected_transformers: + exp_trans_names = [t.__name__ for t in expected_transformers] + for transformer in code_block_component.transformers._var_value: # type: ignore + assert type(transformer).__name__ in exp_trans_names + + # Check if the second child is the copy button if can_copy is True + if props.get("can_copy", False): + if props.get("copy_button"): + assert isinstance(component.children[1], expected_button_type) + assert component.children[1] == props["copy_button"] + else: + assert isinstance(component.children[1], expected_button_type) + else: + assert len(component.children) == 1 + + +@pytest.mark.parametrize( + "children, props", + [ + (["print('Hello')"], {"theme": "dark"}), + (["print('Hello')"], {"language": "javascript"}), + ], +) +def test_shiki_high_level_code_block_theme_language_mapping(children, props): + component = ShikiHighLevelCodeBlock.create(*children, **props) + + # Test that the theme is mapped correctly + if "theme" in props: + assert component.children[ + 0 + ].theme._var_value == ShikiHighLevelCodeBlock._map_themes(props["theme"]) # type: ignore + + # Test that the language is mapped correctly + if "language" in props: + assert component.children[ + 0 + ].language._var_value == ShikiHighLevelCodeBlock._map_languages( # type: ignore + props["language"] + ) From 993bfaef2d668dc134440b4dd86f649a7a1f0f39 Mon Sep 17 00:00:00 2001 From: Simon Young <40179067+Kastier1@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:32:13 -0700 Subject: [PATCH 23/27] HOS-92: added max-request support to gunicorn (#4217) Co-authored-by: simon --- reflex/config.py | 6 ++++++ reflex/utils/exec.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/reflex/config.py b/reflex/config.py index 2e16e2eb0..00f33b653 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -418,6 +418,12 @@ class Config(Base): # Number of gunicorn workers from user gunicorn_workers: Optional[int] = None + # Number of requests before a worker is restarted + gunicorn_max_requests: int = 100 + + # Variance limit for max requests; gunicorn only + gunicorn_max_requests_jitter: int = 25 + # Indicate which type of state manager to use state_manager_mode: constants.StateManagerMode = constants.StateManagerMode.DISK diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index f5521c91f..bdc9be4ae 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -337,8 +337,8 @@ def run_uvicorn_backend_prod(host, port, loglevel): app_module = get_app_module() - RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split() - RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split() + RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split() + RUN_BACKEND_PROD_WINDOWS = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split() command = ( [ *RUN_BACKEND_PROD_WINDOWS, From 0aca89781fcd62ed7f1b5cc18a35d3a1d35b6e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Brand=C3=A9ho?= Date: Tue, 22 Oct 2024 11:48:47 -0700 Subject: [PATCH 24/27] bump ruff to 0.7.0 (#4213) * bump ruff to 0.7.0 * ignore SIM115 and reverse change for it --- .pre-commit-config.yaml | 2 +- poetry.lock | 46 ++++++++++++++++++++--------------------- pyproject.toml | 4 ++-- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4d2e76b31..e2983d1d1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ fail_fast: true repos: - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.6.9 + rev: v0.7.0 hooks: - id: ruff-format args: [reflex, tests] diff --git a/poetry.lock b/poetry.lock index 2f77234d4..bbd2735ac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -970,13 +970,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] [[package]] name = "mako" -version = "1.3.5" +version = "1.3.6" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, - {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, + {file = "Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a"}, + {file = "mako-1.3.6.tar.gz", hash = "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d"}, ] [package.dependencies] @@ -2272,29 +2272,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.6.9" +version = "0.7.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.9-py3-none-linux_armv6l.whl", hash = "sha256:064df58d84ccc0ac0fcd63bc3090b251d90e2a372558c0f057c3f75ed73e1ccd"}, - {file = "ruff-0.6.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:140d4b5c9f5fc7a7b074908a78ab8d384dd7f6510402267bc76c37195c02a7ec"}, - {file = "ruff-0.6.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53fd8ca5e82bdee8da7f506d7b03a261f24cd43d090ea9db9a1dc59d9313914c"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645d7d8761f915e48a00d4ecc3686969761df69fb561dd914a773c1a8266e14e"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eae02b700763e3847595b9d2891488989cac00214da7f845f4bcf2989007d577"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d5ccc9e58112441de8ad4b29dcb7a86dc25c5f770e3c06a9d57e0e5eba48829"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:417b81aa1c9b60b2f8edc463c58363075412866ae4e2b9ab0f690dc1e87ac1b5"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c866b631f5fbce896a74a6e4383407ba7507b815ccc52bcedabb6810fdb3ef7"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b118afbb3202f5911486ad52da86d1d52305b59e7ef2031cea3425142b97d6f"}, - {file = "ruff-0.6.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67267654edc23c97335586774790cde402fb6bbdb3c2314f1fc087dee320bfa"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3ef0cc774b00fec123f635ce5c547dac263f6ee9fb9cc83437c5904183b55ceb"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:12edd2af0c60fa61ff31cefb90aef4288ac4d372b4962c2864aeea3a1a2460c0"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:55bb01caeaf3a60b2b2bba07308a02fca6ab56233302406ed5245180a05c5625"}, - {file = "ruff-0.6.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:925d26471fa24b0ce5a6cdfab1bb526fb4159952385f386bdcc643813d472039"}, - {file = "ruff-0.6.9-py3-none-win32.whl", hash = "sha256:eb61ec9bdb2506cffd492e05ac40e5bc6284873aceb605503d8494180d6fc84d"}, - {file = "ruff-0.6.9-py3-none-win_amd64.whl", hash = "sha256:785d31851c1ae91f45b3d8fe23b8ae4b5170089021fbb42402d811135f0b7117"}, - {file = "ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93"}, - {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, + {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, + {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, + {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, + {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, + {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, + {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, + {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, ] [[package]] @@ -3033,4 +3033,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "edb7145394dd61f5a665b5519cc0c091c8c1628200ea1170857cff1a6bdb829e" +content-hash = "8090ccaeca173bd8612e17a0b8d157d7492618e49450abd1c8373e2976349db0" diff --git a/pyproject.toml b/pyproject.toml index d4f583189..93f3c5d50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ darglint = ">=1.8.1,<2.0" toml = ">=0.10.2,<1.0" pytest-asyncio = ">=0.24.0" pytest-cov = ">=4.0.0,<6.0" -ruff = "^0.6.9" +ruff = "^0.7.0" pandas = ">=2.1.1,<3.0" pillow = ">=10.0.0,<12.0" plotly = ">=5.13.0,<6.0" @@ -91,7 +91,7 @@ build-backend = "poetry.core.masonry.api" [tool.ruff] target-version = "py39" lint.select = ["B", "D", "E", "F", "I", "SIM", "W"] -lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"] +lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541", "SIM115"] lint.pydocstyle.convention = "google" [tool.ruff.lint.per-file-ignores] From 13591793de2a5b870c7181c99b132fbc463ffc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Brand=C3=A9ho?= Date: Tue, 22 Oct 2024 12:17:31 -0700 Subject: [PATCH 25/27] move client storage classes to their own file (#4216) * move client storage classes to their own file * fix 3.9 annotations --- reflex/__init__.py | 8 ++- reflex/__init__.pyi | 6 +- reflex/compiler/utils.py | 3 +- reflex/istate/storage.py | 144 +++++++++++++++++++++++++++++++++++++++ reflex/state.py | 140 +------------------------------------ 5 files changed, 157 insertions(+), 144 deletions(-) create mode 100644 reflex/istate/storage.py diff --git a/reflex/__init__.py b/reflex/__init__.py index ad51d2cf4..979e5499a 100644 --- a/reflex/__init__.py +++ b/reflex/__init__.py @@ -320,13 +320,15 @@ _MAPPING: dict = { "upload_files", "window_alert", ], + "istate.storage": [ + "Cookie", + "LocalStorage", + "SessionStorage", + ], "middleware": ["middleware", "Middleware"], "model": ["session", "Model"], "state": [ "var", - "Cookie", - "LocalStorage", - "SessionStorage", "ComponentState", "State", ], diff --git a/reflex/__init__.pyi b/reflex/__init__.pyi index d928778d8..aeebaadf8 100644 --- a/reflex/__init__.pyi +++ b/reflex/__init__.pyi @@ -174,15 +174,15 @@ from .event import stop_propagation as stop_propagation from .event import upload_files as upload_files from .event import window_alert as window_alert from .experimental import _x as _x +from .istate.storage import Cookie as Cookie +from .istate.storage import LocalStorage as LocalStorage +from .istate.storage import SessionStorage as SessionStorage from .middleware import Middleware as Middleware from .middleware import middleware as middleware from .model import Model as Model from .model import session as session from .page import page as page from .state import ComponentState as ComponentState -from .state import Cookie as Cookie -from .state import LocalStorage as LocalStorage -from .state import SessionStorage as SessionStorage from .state import State as State from .state import var as var from .style import Style as Style diff --git a/reflex/compiler/utils.py b/reflex/compiler/utils.py index 6f4fa2d1b..40640faa6 100644 --- a/reflex/compiler/utils.py +++ b/reflex/compiler/utils.py @@ -28,7 +28,8 @@ from reflex.components.base import ( Title, ) from reflex.components.component import Component, ComponentStyle, CustomComponent -from reflex.state import BaseState, Cookie, LocalStorage, SessionStorage +from reflex.istate.storage import Cookie, LocalStorage, SessionStorage +from reflex.state import BaseState from reflex.style import Style from reflex.utils import console, format, imports, path_ops from reflex.utils.imports import ImportVar, ParsedImportDict diff --git a/reflex/istate/storage.py b/reflex/istate/storage.py new file mode 100644 index 000000000..85e21ffa7 --- /dev/null +++ b/reflex/istate/storage.py @@ -0,0 +1,144 @@ +"""Client-side storage classes for reflex state variables.""" + +from __future__ import annotations + +from typing import Any + +from reflex.utils import format + + +class ClientStorageBase: + """Base class for client-side storage.""" + + def options(self) -> dict[str, Any]: + """Get the options for the storage. + + Returns: + All set options for the storage (not None). + """ + return { + format.to_camel_case(k): v for k, v in vars(self).items() if v is not None + } + + +class Cookie(ClientStorageBase, str): + """Represents a state Var that is stored as a cookie in the browser.""" + + name: str | None + path: str + max_age: int | None + domain: str | None + secure: bool | None + same_site: str + + def __new__( + cls, + object: Any = "", + encoding: str | None = None, + errors: str | None = None, + /, + name: str | None = None, + path: str = "/", + max_age: int | None = None, + domain: str | None = None, + secure: bool | None = None, + same_site: str = "lax", + ): + """Create a client-side Cookie (str). + + Args: + object: The initial object. + encoding: The encoding to use. + errors: The error handling scheme to use. + name: The name of the cookie on the client side. + path: Cookie path. Use / as the path if the cookie should be accessible on all pages. + max_age: Relative max age of the cookie in seconds from when the client receives it. + domain: Domain for the cookie (sub.domain.com or .allsubdomains.com). + secure: Is the cookie only accessible through HTTPS? + same_site: Whether the cookie is sent with third party requests. + One of (true|false|none|lax|strict) + + Returns: + The client-side Cookie object. + + Note: expires (absolute Date) is not supported at this time. + """ + if encoding or errors: + inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") + else: + inst = super().__new__(cls, object) + inst.name = name + inst.path = path + inst.max_age = max_age + inst.domain = domain + inst.secure = secure + inst.same_site = same_site + return inst + + +class LocalStorage(ClientStorageBase, str): + """Represents a state Var that is stored in localStorage in the browser.""" + + name: str | None + sync: bool = False + + def __new__( + cls, + object: Any = "", + encoding: str | None = None, + errors: str | None = None, + /, + name: str | None = None, + sync: bool = False, + ) -> "LocalStorage": + """Create a client-side localStorage (str). + + Args: + object: The initial object. + encoding: The encoding to use. + errors: The error handling scheme to use. + name: The name of the storage key on the client side. + sync: Whether changes should be propagated to other tabs. + + Returns: + The client-side localStorage object. + """ + if encoding or errors: + inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") + else: + inst = super().__new__(cls, object) + inst.name = name + inst.sync = sync + return inst + + +class SessionStorage(ClientStorageBase, str): + """Represents a state Var that is stored in sessionStorage in the browser.""" + + name: str | None + + def __new__( + cls, + object: Any = "", + encoding: str | None = None, + errors: str | None = None, + /, + name: str | None = None, + ) -> "SessionStorage": + """Create a client-side sessionStorage (str). + + Args: + object: The initial object. + encoding: The encoding to use. + errors: The error handling scheme to use + name: The name of the storage on the client side + + Returns: + The client-side sessionStorage object. + """ + if encoding or errors: + inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") + else: + inst = super().__new__(cls, object) + inst.name = name + return inst diff --git a/reflex/state.py b/reflex/state.py index 3422d1ba7..dcd576b3a 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -42,6 +42,9 @@ from typing_extensions import Self from reflex import event from reflex.config import get_config from reflex.istate.data import RouterData +from reflex.istate.storage import ( + ClientStorageBase, +) from reflex.vars.base import ( ComputedVar, DynamicRouteVar, @@ -3349,143 +3352,6 @@ def get_state_manager() -> StateManager: return app.state_manager -class ClientStorageBase: - """Base class for client-side storage.""" - - def options(self) -> dict[str, Any]: - """Get the options for the storage. - - Returns: - All set options for the storage (not None). - """ - return { - format.to_camel_case(k): v for k, v in vars(self).items() if v is not None - } - - -class Cookie(ClientStorageBase, str): - """Represents a state Var that is stored as a cookie in the browser.""" - - name: str | None - path: str - max_age: int | None - domain: str | None - secure: bool | None - same_site: str - - def __new__( - cls, - object: Any = "", - encoding: str | None = None, - errors: str | None = None, - /, - name: str | None = None, - path: str = "/", - max_age: int | None = None, - domain: str | None = None, - secure: bool | None = None, - same_site: str = "lax", - ): - """Create a client-side Cookie (str). - - Args: - object: The initial object. - encoding: The encoding to use. - errors: The error handling scheme to use. - name: The name of the cookie on the client side. - path: Cookie path. Use / as the path if the cookie should be accessible on all pages. - max_age: Relative max age of the cookie in seconds from when the client receives it. - domain: Domain for the cookie (sub.domain.com or .allsubdomains.com). - secure: Is the cookie only accessible through HTTPS? - same_site: Whether the cookie is sent with third party requests. - One of (true|false|none|lax|strict) - - Returns: - The client-side Cookie object. - - Note: expires (absolute Date) is not supported at this time. - """ - if encoding or errors: - inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") - else: - inst = super().__new__(cls, object) - inst.name = name - inst.path = path - inst.max_age = max_age - inst.domain = domain - inst.secure = secure - inst.same_site = same_site - return inst - - -class LocalStorage(ClientStorageBase, str): - """Represents a state Var that is stored in localStorage in the browser.""" - - name: str | None - sync: bool = False - - def __new__( - cls, - object: Any = "", - encoding: str | None = None, - errors: str | None = None, - /, - name: str | None = None, - sync: bool = False, - ) -> "LocalStorage": - """Create a client-side localStorage (str). - - Args: - object: The initial object. - encoding: The encoding to use. - errors: The error handling scheme to use. - name: The name of the storage key on the client side. - sync: Whether changes should be propagated to other tabs. - - Returns: - The client-side localStorage object. - """ - if encoding or errors: - inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") - else: - inst = super().__new__(cls, object) - inst.name = name - inst.sync = sync - return inst - - -class SessionStorage(ClientStorageBase, str): - """Represents a state Var that is stored in sessionStorage in the browser.""" - - name: str | None - - def __new__( - cls, - object: Any = "", - encoding: str | None = None, - errors: str | None = None, - /, - name: str | None = None, - ) -> "SessionStorage": - """Create a client-side sessionStorage (str). - - Args: - object: The initial object. - encoding: The encoding to use. - errors: The error handling scheme to use - name: The name of the storage on the client side - - Returns: - The client-side sessionStorage object. - """ - if encoding or errors: - inst = super().__new__(cls, object, encoding or "utf-8", errors or "strict") - else: - inst = super().__new__(cls, object) - inst.name = name - return inst - - class MutableProxy(wrapt.ObjectProxy): """A proxy for a mutable object that tracks changes.""" From 227fb2cb75176147356435de487097c4e8e574f5 Mon Sep 17 00:00:00 2001 From: Simon Young <40179067+Kastier1@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:37:17 -0700 Subject: [PATCH 26/27] HOS-93: add support for .env file (#4219) * HOS-93: add support for .env file * HOS-93: remove stray print * HOS-93: poetry lock * HOS-93: update comment --------- Co-authored-by: simon --- poetry.lock | 36 +++++++++++++++++++++++++----------- pyproject.toml | 1 + reflex/config.py | 10 ++++++++++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index bbd2735ac..7161e6af7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -570,18 +570,18 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.2" +version = "0.115.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"}, - {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"}, + {file = "fastapi-0.115.3-py3-none-any.whl", hash = "sha256:8035e8f9a2b0aa89cea03b6c77721178ed5358e1aea4cd8570d9466895c0638c"}, + {file = "fastapi-0.115.3.tar.gz", hash = "sha256:c091c6a35599c036d676fa24bd4a6e19fa30058d93d950216cdc672881f6f7db"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.41.0" +starlette = ">=0.40.0,<0.42.0" typing-extensions = ">=4.8.0" [package.extras] @@ -1977,6 +1977,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python-engineio" version = "4.10.1" @@ -2253,13 +2267,13 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.9.2" +version = "13.9.3" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, + {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, + {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, ] [package.dependencies] @@ -2525,13 +2539,13 @@ SQLAlchemy = ">=2.0.14,<2.1.0" [[package]] name = "starlette" -version = "0.40.0" +version = "0.41.0" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, - {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, + {file = "starlette-0.41.0-py3-none-any.whl", hash = "sha256:a0193a3c413ebc9c78bff1c3546a45bb8c8bcb4a84cae8747d650a65bd37210a"}, + {file = "starlette-0.41.0.tar.gz", hash = "sha256:39cbd8768b107d68bfe1ff1672b38a2c38b49777de46d2a592841d58e3bf7c2a"}, ] [package.dependencies] @@ -3033,4 +3047,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "8090ccaeca173bd8612e17a0b8d157d7492618e49450abd1c8373e2976349db0" +content-hash = "c5da15520cef58124f6699007c81158036840469d4f9972592d72bd456c45e7e" diff --git a/pyproject.toml b/pyproject.toml index 93f3c5d50..3fe6041f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ jinja2 = ">=3.1.2,<4.0" psutil = ">=5.9.4,<7.0" pydantic = ">=1.10.2,<3.0" python-multipart = ">=0.0.5,<0.1" +python-dotenv = ">=1.0.1" python-socketio = ">=5.7.0,<6.0" redis = ">=4.3.5,<6.0" rich = ">=13.0.0,<14.0" diff --git a/reflex/config.py b/reflex/config.py index 00f33b653..eb319c5dd 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -436,6 +436,9 @@ class Config(Base): # Attributes that were explicitly set by the user. _non_default_attributes: Set[str] = pydantic.PrivateAttr(set()) + # Path to file containing key-values pairs to override in the environment; Dotenv format. + env_file: Optional[str] = None + def __init__(self, *args, **kwargs): """Initialize the config values. @@ -477,6 +480,7 @@ class Config(Base): def update_from_env(self) -> dict[str, Any]: """Update the config values based on set environment variables. + If there is a set env_file, it is loaded first. Returns: The updated config values. @@ -486,6 +490,12 @@ class Config(Base): """ from reflex.utils.exceptions import EnvVarValueError + if self.env_file: + from dotenv import load_dotenv + + # load env file if exists + load_dotenv(self.env_file, override=True) + updated_values = {} # Iterate over the fields. for key, field in self.__fields__.items(): From a65fc2e90b27af682181a47e8e2dc73820d1732a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 22 Oct 2024 13:09:14 -0700 Subject: [PATCH 27/27] Add on progress typing to react player (#4211) * add on progress typing to react player * fix pyi file * have the pyi here as well * more pyi changes * fix imports * run pyi * for some reason it want event on three lines no clue why * simplify case for when type is in the same module * run pyi * remove last missing type for datadisplay --- reflex/components/datadisplay/dataeditor.py | 27 ++++++------------- reflex/components/datadisplay/dataeditor.pyi | 14 +++++----- .../radix/themes/components/tooltip.pyi | 4 ++- reflex/components/react_player/__init__.py | 1 + reflex/components/react_player/audio.pyi | 5 +++- .../components/react_player/react_player.py | 13 ++++++++- .../components/react_player/react_player.pyi | 10 ++++++- reflex/components/react_player/video.pyi | 5 +++- reflex/utils/pyi_generator.py | 22 ++++++++++++--- 9 files changed, 65 insertions(+), 36 deletions(-) diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index a192c7a45..b9bd4cebf 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -109,19 +109,6 @@ class DataEditorTheme(Base): text_medium: Optional[str] = None -def on_edit_spec(pos, data: dict[str, Any]): - """The on edit spec function. - - Args: - pos: The position of the edit event. - data: The data of the edit event. - - Returns: - The position and data. - """ - return [pos, data] - - class Bounds(TypedDict): """The bounds of the group header.""" @@ -149,7 +136,7 @@ class Rectangle(TypedDict): class GridSelectionCurrent(TypedDict): """The current selection.""" - cell: list[int] + cell: tuple[int, int] range: Rectangle rangeStack: list[Rectangle] @@ -167,7 +154,7 @@ class GroupHeaderClickedEventArgs(TypedDict): kind: str group: str - location: list[int] + location: tuple[int, int] bounds: Bounds isEdge: bool shiftKey: bool @@ -178,7 +165,7 @@ class GroupHeaderClickedEventArgs(TypedDict): localEventY: int button: int buttons: int - scrollEdge: list[int] + scrollEdge: tuple[int, int] class GridCell(TypedDict): @@ -306,10 +293,10 @@ class DataEditor(NoSSRComponent): on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])] # Fired when a cell is edited. - on_cell_edited: EventHandler[on_edit_spec] + on_cell_edited: EventHandler[identity_event(Tuple[int, int], GridCell)] # Fired when a group header is clicked. - on_group_header_clicked: EventHandler[on_edit_spec] + on_group_header_clicked: EventHandler[identity_event(Tuple[int, int], GridCell)] # Fired when a group header is right-clicked. on_group_header_context_menu: EventHandler[ @@ -335,7 +322,9 @@ class DataEditor(NoSSRComponent): on_delete: EventHandler[identity_event(GridSelection)] # Fired when editing is finished. - on_finished_editing: EventHandler[identity_event(Union[GridCell, None], list[int])] + on_finished_editing: EventHandler[ + identity_event(Union[GridCell, None], tuple[int, int]) + ] # Fired when a row is appended. on_row_appended: EventHandler[empty_event] diff --git a/reflex/components/datadisplay/dataeditor.pyi b/reflex/components/datadisplay/dataeditor.pyi index aadd9666e..6402aaf42 100644 --- a/reflex/components/datadisplay/dataeditor.pyi +++ b/reflex/components/datadisplay/dataeditor.pyi @@ -78,8 +78,6 @@ class DataEditorTheme(Base): text_light: Optional[str] text_medium: Optional[str] -def on_edit_spec(pos, data: dict[str, Any]): ... - class Bounds(TypedDict): x: int y: int @@ -96,7 +94,7 @@ class Rectangle(TypedDict): height: int class GridSelectionCurrent(TypedDict): - cell: list[int] + cell: tuple[int, int] range: Rectangle rangeStack: list[Rectangle] @@ -108,7 +106,7 @@ class GridSelection(TypedDict): class GroupHeaderClickedEventArgs(TypedDict): kind: str group: str - location: list[int] + location: tuple[int, int] bounds: Bounds isEdge: bool shiftKey: bool @@ -119,7 +117,7 @@ class GroupHeaderClickedEventArgs(TypedDict): localEventY: int button: int buttons: int - scrollEdge: list[int] + scrollEdge: tuple[int, int] class GridCell(TypedDict): span: Optional[List[int]] @@ -189,17 +187,17 @@ class DataEditor(NoSSRComponent): on_cell_activated: Optional[EventType[tuple[int, int]]] = None, on_cell_clicked: Optional[EventType[tuple[int, int]]] = None, on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None, - on_cell_edited: Optional[EventType] = None, + on_cell_edited: Optional[EventType[tuple[int, int], GridCell]] = None, on_click: Optional[EventType[[]]] = None, on_column_resize: Optional[EventType[GridColumn, int]] = None, on_context_menu: Optional[EventType[[]]] = None, on_delete: Optional[EventType[GridSelection]] = None, on_double_click: Optional[EventType[[]]] = None, on_finished_editing: Optional[ - EventType[Union[GridCell, None], list[int]] + EventType[Union[GridCell, None], tuple[int, int]] ] = None, on_focus: Optional[EventType[[]]] = None, - on_group_header_clicked: Optional[EventType] = None, + on_group_header_clicked: Optional[EventType[tuple[int, int], GridCell]] = None, on_group_header_context_menu: Optional[ EventType[int, GroupHeaderClickedEventArgs] ] = None, diff --git a/reflex/components/radix/themes/components/tooltip.pyi b/reflex/components/radix/themes/components/tooltip.pyi index ac2a36368..e78dd926d 100644 --- a/reflex/components/radix/themes/components/tooltip.pyi +++ b/reflex/components/radix/themes/components/tooltip.pyi @@ -5,7 +5,9 @@ # ------------------------------------------------------ from typing import Any, Dict, Literal, Optional, Union, overload -from reflex.event import EventType +from reflex.event import ( + EventType, +) from reflex.style import Style from reflex.vars.base import Var diff --git a/reflex/components/react_player/__init__.py b/reflex/components/react_player/__init__.py index 8c4a4486f..3f807b1a0 100644 --- a/reflex/components/react_player/__init__.py +++ b/reflex/components/react_player/__init__.py @@ -1,5 +1,6 @@ """React Player component for audio and video.""" +from . import react_player from .audio import Audio from .video import Video diff --git a/reflex/components/react_player/audio.pyi b/reflex/components/react_player/audio.pyi index 2556c8e83..1841829af 100644 --- a/reflex/components/react_player/audio.pyi +++ b/reflex/components/react_player/audio.pyi @@ -5,6 +5,7 @@ # ------------------------------------------------------ from typing import Any, Dict, Optional, Union, overload +import reflex from reflex.components.react_player.react_player import ReactPlayer from reflex.event import EventType from reflex.style import Style @@ -58,7 +59,9 @@ class Audio(ReactPlayer): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType] = None, + on_progress: Optional[ + EventType[reflex.components.react_player.react_player.Progress] + ] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType[float]] = None, diff --git a/reflex/components/react_player/react_player.py b/reflex/components/react_player/react_player.py index 7ad45b093..b2c58b754 100644 --- a/reflex/components/react_player/react_player.py +++ b/reflex/components/react_player/react_player.py @@ -2,11 +2,22 @@ from __future__ import annotations +from typing_extensions import TypedDict + from reflex.components.component import NoSSRComponent from reflex.event import EventHandler, empty_event, identity_event from reflex.vars.base import Var +class Progress(TypedDict): + """Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds.""" + + played: float + playedSeconds: float + loaded: float + loadedSeconds: float + + class ReactPlayer(NoSSRComponent): """Using react-player and not implement all props and callback yet. reference: https://github.com/cookpete/react-player. @@ -55,7 +66,7 @@ class ReactPlayer(NoSSRComponent): on_play: EventHandler[empty_event] # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 } - on_progress: EventHandler[lambda progress: [progress]] + on_progress: EventHandler[identity_event(Progress)] # Callback containing duration of the media, in seconds. on_duration: EventHandler[identity_event(float)] diff --git a/reflex/components/react_player/react_player.pyi b/reflex/components/react_player/react_player.pyi index 9a445c294..e4027cf40 100644 --- a/reflex/components/react_player/react_player.pyi +++ b/reflex/components/react_player/react_player.pyi @@ -5,11 +5,19 @@ # ------------------------------------------------------ from typing import Any, Dict, Optional, Union, overload +from typing_extensions import TypedDict + from reflex.components.component import NoSSRComponent from reflex.event import EventType from reflex.style import Style from reflex.vars.base import Var +class Progress(TypedDict): + played: float + playedSeconds: float + loaded: float + loadedSeconds: float + class ReactPlayer(NoSSRComponent): @overload @classmethod @@ -56,7 +64,7 @@ class ReactPlayer(NoSSRComponent): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType] = None, + on_progress: Optional[EventType[Progress]] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType[float]] = None, diff --git a/reflex/components/react_player/video.pyi b/reflex/components/react_player/video.pyi index d46e2617d..a05e3747b 100644 --- a/reflex/components/react_player/video.pyi +++ b/reflex/components/react_player/video.pyi @@ -5,6 +5,7 @@ # ------------------------------------------------------ from typing import Any, Dict, Optional, Union, overload +import reflex from reflex.components.react_player.react_player import ReactPlayer from reflex.event import EventType from reflex.style import Style @@ -58,7 +59,9 @@ class Video(ReactPlayer): on_play: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None, - on_progress: Optional[EventType] = None, + on_progress: Optional[ + EventType[reflex.components.react_player.react_player.Progress] + ] = None, on_ready: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_seek: Optional[EventType[float]] = None, diff --git a/reflex/utils/pyi_generator.py b/reflex/utils/pyi_generator.py index 026a53bca..1fc17341b 100644 --- a/reflex/utils/pyi_generator.py +++ b/reflex/utils/pyi_generator.py @@ -214,7 +214,9 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str: return res -def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]: +def _generate_imports( + typing_imports: Iterable[str], +) -> list[ast.ImportFrom | ast.Import]: """Generate the import statements for the stub file. Args: @@ -228,6 +230,7 @@ def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]: ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values]) for name, values in DEFAULT_IMPORTS.items() ], + ast.Import([ast.alias("reflex")]), ] @@ -372,12 +375,13 @@ def _extract_class_props_as_ast_nodes( return kwargs -def type_to_ast(typ) -> ast.AST: +def type_to_ast(typ, cls: type) -> ast.AST: """Converts any type annotation into its AST representation. Handles nested generic types, unions, etc. Args: typ: The type annotation to convert. + cls: The class where the type annotation is used. Returns: The AST representation of the type annotation. @@ -390,6 +394,16 @@ def type_to_ast(typ) -> ast.AST: # Handle plain types (int, str, custom classes, etc.) if origin is None: if hasattr(typ, "__name__"): + if typ.__module__.startswith("reflex."): + typ_parts = typ.__module__.split(".") + cls_parts = cls.__module__.split(".") + + zipped = list(zip(typ_parts, cls_parts, strict=False)) + + if all(a == b for a, b in zipped) and len(typ_parts) == len(cls_parts): + return ast.Name(id=typ.__name__) + + return ast.Name(id=typ.__module__ + "." + typ.__name__) return ast.Name(id=typ.__name__) elif hasattr(typ, "_name"): return ast.Name(id=typ._name) @@ -406,7 +420,7 @@ def type_to_ast(typ) -> ast.AST: return ast.Name(id=base_name) # Convert all type arguments recursively - arg_nodes = [type_to_ast(arg) for arg in args] + arg_nodes = [type_to_ast(arg, cls) for arg in args] # Special case for single-argument types (like List[T] or Optional[T]) if len(arg_nodes) == 1: @@ -487,7 +501,7 @@ def _generate_component_create_functiondef( ] # Convert each argument type to its AST representation - type_args = [type_to_ast(arg) for arg in arguments_without_var] + type_args = [type_to_ast(arg, cls=clz) for arg in arguments_without_var] # Join the type arguments with commas for EventType args_str = ", ".join(ast.unparse(arg) for arg in type_args)