Compare commits

...

4 Commits
main ... v0.5.3

Author SHA1 Message Date
Masen Furer
1f3fee598b
[REF-3004] Use relative path to stylesheet for postcss-import compat (#3435)
* test_tailwind: include custom stylesheet

* [REF-3004] Use relative path to stylesheet for postcss-import compat

postcss-import balls all of the CSS up into a single file, which happens at
compile time. So, replace the `@/` with `../public` so the import paths can be
resolved relative to the `styles` directory.

* test_compiler: fix compile_stylesheets expectations

* Use constants.Dirs.PUBLIC instead of "public"
2024-06-05 10:06:10 -07:00
Masen Furer
8df959dc43
bump version to 0.5.3 (#3446) 2024-06-05 10:06:10 -07:00
Elijah Ahianyo
63e9ae869f
[REF-3010] Circular Imports Fix (#3433) 2024-06-05 10:06:09 -07:00
HongyuHansonYao
5a68e9d72f
Set use resize handler default to true (#3424)
* init

* empty commit

* set true

* set true

* set true

---------

Co-authored-by: Hongyu Yao <hongyuyao@hongyus-mbp-3.lan>
2024-06-05 10:06:09 -07:00
15 changed files with 59 additions and 118 deletions

View File

@ -25,6 +25,8 @@ def TailwindApp(
paragraph_text: Text for the paragraph.
paragraph_class_name: Tailwind class_name for the paragraph.
"""
from pathlib import Path
import reflex as rx
class UnusedState(rx.State):
@ -35,10 +37,15 @@ def TailwindApp(
rx.chakra.text(paragraph_text, class_name=paragraph_class_name),
rx.el.p(paragraph_text, class_name=paragraph_class_name),
rx.text(paragraph_text, as_="p", class_name=paragraph_class_name),
rx.el.div("Test external stylesheet", class_name="external"),
id="p-content",
)
app = rx.App(style={"font_family": "monospace"})
assets = Path(__file__).resolve().parent.parent / "assets"
assets.mkdir(exist_ok=True)
stylesheet = assets / "test_styles.css"
stylesheet.write_text(".external { color: rgba(0, 0, 255, 0.5) }")
app = rx.App(style={"font_family": "monospace"}, stylesheets=[stylesheet.name])
app.add_page(index)
if tailwind_disabled:
config = rx.config.get_config()
@ -107,3 +114,9 @@ def test_tailwind_app(tailwind_app: AppHarness, tailwind_disabled: bool):
else:
# expect "text-red-500" from tailwind utility class
assert p.value_of_css_property("color") in TEXT_RED_500_COLOR
# Assert external stylesheet is applying rules
external = driver.find_elements(By.CLASS_NAME, "external")
assert len(external) == 1
for ext_div in external:
assert ext_div.value_of_css_property("color") == "rgba(0, 0, 255, 0.5)"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.5.2"
version = "0.5.3"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [

View File

@ -169,7 +169,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
raise FileNotFoundError(
f"The stylesheet file {stylesheet_full_path} does not exist."
)
stylesheet = f"@/{stylesheet.strip('/')}"
stylesheet = f"../{constants.Dirs.PUBLIC}/{stylesheet.strip('/')}"
sheets.append(stylesheet) if stylesheet not in sheets else None
return templates.STYLE.render(stylesheets=sheets)

View File

@ -9,7 +9,7 @@ class Html(Div):
"""Render the html.
Returns:
The code to render the html component.
The code to render the html component.
"""
# The HTML to render.

View File

@ -185,7 +185,7 @@ from .elements.tables import tfoot as tfoot
from .elements.tables import th as th
from .elements.tables import thead as thead
from .elements.tables import tr as tr
from .elements.tables import Tbody as Tbody
from .elements.tables import tbody as tbody
from .elements.tables import Caption as Caption
from .elements.tables import Col as Col
from .elements.tables import Colgroup as Colgroup
@ -195,6 +195,7 @@ from .elements.tables import Tfoot as Tfoot
from .elements.tables import Th as Th
from .elements.tables import Thead as Thead
from .elements.tables import Tr as Tr
from .elements.tables import Tbody as Tbody
from .elements.typography import blockquote as blockquote
from .elements.typography import dd as dd
from .elements.typography import div as div

View File

@ -102,7 +102,7 @@ _MAPPING = {
"th",
"thead",
"tr",
"Tbody",
"tbody",
],
"typography": [
"blockquote",

View File

@ -183,7 +183,7 @@ from .tables import tfoot as tfoot
from .tables import th as th
from .tables import thead as thead
from .tables import tr as tr
from .tables import Tbody as Tbody
from .tables import tbody as tbody
from .tables import Caption as Caption
from .tables import Col as Col
from .tables import Colgroup as Colgroup
@ -193,6 +193,7 @@ from .tables import Tfoot as Tfoot
from .tables import Th as Th
from .tables import Thead as Thead
from .tables import Tr as Tr
from .tables import Tbody as Tbody
from .typography import blockquote as blockquote
from .typography import dd as dd
from .typography import div as div
@ -316,7 +317,7 @@ _MAPPING = {
"th",
"thead",
"tr",
"Tbody",
"tbody",
],
"typography": [
"blockquote",

View File

@ -109,7 +109,7 @@ class Plotly(PlotlyLib):
config: Var[Dict]
# If true, the graph will resize when the window is resized.
use_resize_handler: Var[bool]
use_resize_handler: Var[bool] = Var.create_safe(True)
# Fired after the plot is redrawn.
on_after_plot: EventHandler[_passthrough_signature]

View File

@ -27,6 +27,8 @@ class Dirs(SimpleNamespace):
UTILS = "utils"
# The name of the output static directory.
STATIC = "_static"
# The name of the public html directory served at "/"
PUBLIC = "public"
# The name of the state file.
STATE_PATH = "/".join([UTILS, "state"])
# The name of the components file.
@ -40,7 +42,7 @@ class Dirs(SimpleNamespace):
# The directory where the utils file is located.
WEB_UTILS = os.path.join(WEB, UTILS)
# The directory where the assets are located.
WEB_ASSETS = os.path.join(WEB, "public")
WEB_ASSETS = os.path.join(WEB, PUBLIC)
# The env json file.
ENV_JSON = os.path.join(WEB, "env.json")
# The reflex json file.

View File

@ -1,99 +0,0 @@
"""Stub file for reflex/constants/base.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.vars import Var, BaseVar, ComputedVar
from reflex.event import EventChain, EventHandler, EventSpec
from reflex.style import Style
import os
import platform
from enum import Enum
from importlib import metadata
from types import SimpleNamespace
from platformdirs import PlatformDirs
IS_WINDOWS = platform.system() == "Windows"
IS_WINDOWS_BUN_SUPPORTED_MACHINE = IS_WINDOWS and platform.machine() in [
"AMD64",
"x86_64",
]
class Dirs(SimpleNamespace):
WEB = ".web"
APP_ASSETS = "assets"
EXTERNAL_APP_ASSETS = "external"
UTILS = "utils"
STATIC = "_static"
STATE_PATH = "/".join([UTILS, "state"])
COMPONENTS_PATH = "/".join([UTILS, "components"])
CONTEXTS_PATH = "/".join([UTILS, "context"])
WEB_PAGES = os.path.join(WEB, "pages")
WEB_STATIC = os.path.join(WEB, STATIC)
WEB_UTILS = os.path.join(WEB, UTILS)
WEB_ASSETS = os.path.join(WEB, "public")
ENV_JSON = os.path.join(WEB, "env.json")
REFLEX_JSON = os.path.join(WEB, "reflex.json")
POSTCSS_JS = os.path.join(WEB, "postcss.config.js")
class Reflex(SimpleNamespace):
MODULE_NAME = "reflex"
VERSION = metadata.version(MODULE_NAME)
JSON = os.path.join(Dirs.WEB, "reflex.json")
_dir = os.environ.get("REFLEX_DIR", "")
DIR = _dir or PlatformDirs(MODULE_NAME, False).user_data_dir
ROOT_DIR = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
class ReflexHostingCLI(SimpleNamespace):
MODULE_NAME = "reflex-hosting-cli"
class Templates(SimpleNamespace):
APP_TEMPLATES_ROUTE = "/app-templates"
DEFAULT = "blank"
class Dirs(SimpleNamespace):
BASE = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates")
WEB_TEMPLATE = os.path.join(BASE, "web")
JINJA_TEMPLATE = os.path.join(BASE, "jinja")
CODE = "code"
class Next(SimpleNamespace):
CONFIG_FILE = "next.config.js"
SITEMAP_CONFIG_FILE = os.path.join(Dirs.WEB, "next-sitemap.config.js")
NODE_MODULES = "node_modules"
PACKAGE_LOCK = "package-lock.json"
FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"
class ColorMode(SimpleNamespace):
NAME = "colorMode"
USE = "useColorMode"
TOGGLE = "toggleColorMode"
class Env(str, Enum):
DEV = "dev"
PROD = "prod"
class LogLevel(str, Enum):
DEBUG = "debug"
INFO = "info"
WARNING = "warning"
ERROR = "error"
CRITICAL = "critical"
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
class Ping(SimpleNamespace):
INTERVAL = 25
TIMEOUT = 120
COOKIES = "cookies"
LOCAL_STORAGE = "local_storage"
SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
ENV_MODE_ENV_VAR = "REFLEX_ENV_MODE"
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
RELOAD_CONFIG = "__REFLEX_RELOAD_CONFIG"
REFLEX_VAR_OPENING_TAG = "<reflex.Var>"
REFLEX_VAR_CLOSING_TAG = "</reflex.Var>"

View File

@ -9,8 +9,7 @@ import re
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
from reflex import constants
from reflex.utils import exceptions, serializers, types
from reflex.utils.serializers import serialize
from reflex.utils import exceptions, types
from reflex.vars import BaseVar, Var
if TYPE_CHECKING:
@ -400,6 +399,7 @@ def format_prop(
"""
# import here to avoid circular import.
from reflex.event import EventChain
from reflex.utils import serializers
try:
# Handle var props.
@ -687,6 +687,8 @@ def format_state(value: Any, key: Optional[str] = None) -> Any:
Raises:
TypeError: If the given value is not a valid state.
"""
from reflex.utils import serializers
# Handle dicts.
if isinstance(value, dict):
return {k: format_state(v, k) for k, v in value.items()}
@ -700,7 +702,7 @@ def format_state(value: Any, key: Optional[str] = None) -> Any:
return value
# Serialize the value.
serialized = serialize(value)
serialized = serializers.serialize(value)
if serialized is not None:
return serialized
@ -803,7 +805,9 @@ def json_dumps(obj: Any) -> str:
Returns:
A string
"""
return json.dumps(obj, ensure_ascii=False, default=serialize)
from reflex.utils import serializers
return json.dumps(obj, ensure_ascii=False, default=serializers.serialize)
def unwrap_vars(value: str) -> str:

View File

@ -12,7 +12,7 @@ from typing import Any, Callable, Dict, List, Set, Tuple, Type, Union, get_type_
from reflex.base import Base
from reflex.constants.colors import Color, format_color
from reflex.utils import exceptions, format, types
from reflex.utils import exceptions, types
# Mapping from type to a serializer.
# The serializer should convert the type to a JSON object.
@ -154,6 +154,8 @@ def serialize_primitive(value: Union[bool, int, float, None]) -> str:
Returns:
The serialized number/bool/None.
"""
from reflex.utils import format
return format.json_dumps(value)
@ -180,6 +182,8 @@ def serialize_list(value: Union[List, Tuple, Set]) -> str:
Returns:
The serialized list.
"""
from reflex.utils import format
# Dump the list to a string.
fprop = format.json_dumps(list(value))
@ -202,6 +206,7 @@ def serialize_dict(prop: Dict[str, Any]) -> str:
"""
# Import here to avoid circular imports.
from reflex.event import EventHandler
from reflex.utils import format
prop_dict = {}

View File

@ -42,7 +42,7 @@ from sqlalchemy.orm import (
from reflex import constants
from reflex.base import Base
from reflex.utils import console, serializers
from reflex.utils import console
if sys.version_info >= (3, 12):
from typing import override
@ -392,6 +392,8 @@ def is_valid_var_type(type_: Type) -> bool:
Returns:
Whether the type is a valid prop type.
"""
from reflex.utils import serializers
if is_union(type_):
return all((is_valid_var_type(arg) for arg in get_args(type_)))
return _issubclass(type_, StateVar) or serializers.has_serializer(type_)

View File

@ -35,7 +35,7 @@ from typing import (
from reflex import constants
from reflex.base import Base
from reflex.utils import console, format, imports, serializers, types
from reflex.utils import console, imports, serializers, types
from reflex.utils.exceptions import VarAttributeError, VarTypeError, VarValueError
# This module used to export ImportVar itself, so we still import it for export here
@ -364,6 +364,8 @@ class Var:
Raises:
VarTypeError: If the value is JSON-unserializable.
"""
from reflex.utils import format
# Check for none values.
if value is None:
return None
@ -543,6 +545,8 @@ class Var:
Returns:
The wrapped var, i.e. {state.var}.
"""
from reflex.utils import format
out = (
self._var_full_name
if self._var_is_local
@ -600,6 +604,8 @@ class Var:
Raises:
VarTypeError: If the var is not indexable.
"""
from reflex.utils import format
# Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
if not (
types._issubclass(self._var_type, Union[List, Dict, Tuple, str])
@ -793,6 +799,8 @@ class Var:
VarTypeError: If the operation between two operands is invalid.
VarValueError: If flip is set to true and value of operand is not provided
"""
from reflex.utils import format
if isinstance(other, str):
other = Var.create(json.dumps(other))
else:
@ -1671,6 +1679,8 @@ class Var:
Returns:
The full name of the var.
"""
from reflex.utils import format
if not self._var_full_name_needs_state_prefix:
return self._var_name
return (
@ -1690,6 +1700,8 @@ class Var:
Returns:
The var with the set state.
"""
from reflex.utils import format
state_name = state if isinstance(state, str) else state.get_full_name()
new_var_data = VarData(
state=state_name,

View File

@ -135,7 +135,7 @@ def test_compile_stylesheets(tmp_path, mocker):
f"@import url('./tailwind.css'); \n"
f"@import url('https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple'); \n"
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'); \n"
f"@import url('@/styles.css'); \n"
f"@import url('../public/styles.css'); \n"
f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css'); \n",
)
@ -166,7 +166,7 @@ def test_compile_stylesheets_exclude_tailwind(tmp_path, mocker):
assert compiler.compile_root_stylesheet(stylesheets) == (
os.path.join(".web", "styles", "styles.css"),
"@import url('@/styles.css'); \n",
"@import url('../public/styles.css'); \n",
)