Skip to content

Commit

Permalink
Merge branch 'main' into lendemor/lift_restriction_on_node_version
Browse files Browse the repository at this point in the history
  • Loading branch information
Lendemor committed Sep 25, 2024
2 parents ecd728e + 5e738fd commit f12adc9
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 46 deletions.
8 changes: 4 additions & 4 deletions reflex/compiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,11 @@ def add_meta(
Returns:
The component with the metadata added.
"""
meta_tags = [Meta.create(**item) for item in meta]

children: list[Any] = [
Title.create(title),
meta_tags = [
item if isinstance(item, Component) else Meta.create(**item) for item in meta
]

children: list[Any] = [Title.create(title)]
if description:
children.append(Description.create(content=description))
children.append(Image.create(content=image))
Expand Down
2 changes: 1 addition & 1 deletion reflex/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ def __init__(self, *args, **kwargs):
# Merge styles, the later ones overriding keys in the earlier ones.
style = {k: v for style_dict in style for k, v in style_dict.items()}

if isinstance(style, Breakpoints):
if isinstance(style, (Breakpoints, Var)):
style = {
# Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict.
"&": style,
Expand Down
42 changes: 22 additions & 20 deletions reflex/components/datadisplay/logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,33 @@ def logo(**props):
Returns:
The logo component.
"""
light_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.6V0.400024H8.96V4.88002H6.72V2.64002H2.24V4.88002H6.72V7.12002H2.24V11.6H0ZM6.72 11.6V7.12002H8.96V11.6H6.72Z" fill="#110F1F"/>
<path d="M11.2 11.6V0.400024H17.92V2.64002H13.44V4.88002H17.92V7.12002H13.44V9.36002H17.92V11.6H11.2Z" fill="#110F1F"/>
<path d="M20.16 11.6V0.400024H26.88V2.64002H22.4V4.88002H26.88V7.12002H22.4V11.6H20.16Z" fill="#110F1F"/>
<path d="M29.12 11.6V0.400024H31.36V9.36002H35.84V11.6H29.12Z" fill="#110F1F"/>
<path d="M38.08 11.6V0.400024H44.8V2.64002H40.32V4.88002H44.8V7.12002H40.32V9.36002H44.8V11.6H38.08Z" fill="#110F1F"/>
<path d="M47.04 4.88002V0.400024H49.28V4.88002H47.04ZM53.76 4.88002V0.400024H56V4.88002H53.76ZM49.28 7.12002V4.88002H53.76V7.12002H49.28ZM47.04 11.6V7.12002H49.28V11.6H47.04ZM53.76 11.6V7.12002H56V11.6H53.76Z" fill="#110F1F"/>
</svg>"""

dark_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z" fill="white"/>
<path d="M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z" fill="white"/>
<path d="M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z" fill="white"/>
<path d="M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z" fill="white"/>
<path d="M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z" fill="white"/>
<path d="M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z" fill="white"/>
</svg>"""

def logo_path(d):
return rx.el.svg.path(
d=d,
fill=rx.color_mode_cond("#110F1F", "white"),
)

paths = [
"M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z",
"M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z",
"M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z",
"M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z",
"M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z",
"M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z",
]

return rx.center(
rx.link(
rx.hstack(
"Built with ",
rx.color_mode_cond(
rx.html(light_mode_logo),
rx.html(dark_mode_logo),
rx.el.svg(
*[logo_path(d) for d in paths],
width="56",
height="12",
viewBox="0 0 56 12",
fill="none",
xmlns="http://www.w3.org/2000/svg",
),
text_align="center",
align="center",
Expand Down
14 changes: 12 additions & 2 deletions reflex/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var
from reflex.vars.function import FunctionVar
from reflex.vars.object import ObjectVar

SYSTEM_COLOR_MODE: str = "system"
LIGHT_COLOR_MODE: str = "light"
Expand Down Expand Up @@ -188,7 +189,16 @@ def update_out_dict(return_value, keys_to_update):
out[k] = return_value

for key, value in style_dict.items():
keys = format_style_key(key)
keys = (
format_style_key(key)
if not isinstance(value, (dict, ObjectVar))
or (
isinstance(value, Breakpoints)
and all(not isinstance(v, dict) for v in value.values())
)
else (key,)
)

if isinstance(value, Var):
return_val = value
new_var_data = value._get_all_var_data()
Expand Down Expand Up @@ -283,7 +293,7 @@ def _format_emotion_style_pseudo_selector(key: str) -> str:
"""Format a pseudo selector for emotion CSS-in-JS.
Args:
key: Underscore-prefixed or colon-prefixed pseudo selector key (_hover).
key: Underscore-prefixed or colon-prefixed pseudo selector key (_hover/:hover).
Returns:
A self-referential pseudo selector key (&:hover).
Expand Down
163 changes: 147 additions & 16 deletions reflex/utils/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,33 @@ def run_frontend_prod(root: Path, port: str, backend_present=True):
)


def should_use_granian():
"""Whether to use Granian for backend.
Returns:
True if Granian should be used.
"""
return os.getenv("REFLEX_USE_GRANIAN", "0") == "1"


def get_app_module():
"""Get the app module for the backend.
Returns:
The app module for the backend.
"""
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"


def get_granian_target():
"""Get the Granian target for the backend.
Returns:
The Granian target for the backend.
"""
return get_app_module() + f".{constants.CompileVars.API}"


def run_backend(
host: str,
port: int,
Expand All @@ -184,24 +211,76 @@ def run_backend(
port: The app port
loglevel: The log level.
"""
import uvicorn

config = get_config()
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"

web_dir = get_web_dir()
# Create a .nocompile file to skip compile for backend.
if web_dir.exists():
(web_dir / constants.NOCOMPILE_FILE).touch()

# Run the backend in development mode.
if should_use_granian():
run_granian_backend(host, port, loglevel)
else:
run_uvicorn_backend(host, port, loglevel)


def run_uvicorn_backend(host, port, loglevel):
"""Run the backend in development mode using Uvicorn.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
import uvicorn

uvicorn.run(
app=f"{app_module}.{constants.CompileVars.API}",
app=f"{get_app_module()}.{constants.CompileVars.API}",
host=host,
port=port,
log_level=loglevel.value,
reload=True,
reload_dirs=[config.app_name],
reload_dirs=[get_config().app_name],
)


def run_granian_backend(host, port, loglevel):
"""Run the backend in development mode using Granian.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
console.debug("Using Granian for backend")
try:
from granian import Granian # type: ignore
from granian.constants import Interfaces # type: ignore
from granian.log import LogLevels # type: ignore

Granian(
target=get_granian_target(),
address=host,
port=port,
interface=Interfaces.ASGI,
log_level=LogLevels(loglevel.value),
reload=True,
reload_paths=[Path(get_config().app_name)],
reload_ignore_dirs=[".web"],
).serve()
except ImportError:
console.error(
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
)
os._exit(1)


def _get_backend_workers():
from reflex.utils import processes

config = get_config()
return (
processes.get_num_workers()
if not config.gunicorn_workers
else config.gunicorn_workers
)


Expand All @@ -212,6 +291,20 @@ def run_backend_prod(
):
"""Run the backend.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
if should_use_granian():
run_granian_backend_prod(host, port, loglevel)
else:
run_uvicorn_backend_prod(host, port, loglevel)


def run_uvicorn_backend_prod(host, port, loglevel):
"""Run the backend in production mode using Uvicorn.
Args:
host: The app host
port: The app port
Expand All @@ -220,14 +313,11 @@ def run_backend_prod(
from reflex.utils import processes

config = get_config()
num_workers = (
processes.get_num_workers()
if not config.gunicorn_workers
else config.gunicorn_workers
)

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()
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
command = (
[
*RUN_BACKEND_PROD_WINDOWS,
Expand All @@ -243,7 +333,7 @@ def run_backend_prod(
"--bind",
f"{host}:{port}",
"--threads",
str(num_workers),
str(_get_backend_workers()),
f"{app_module}()",
]
)
Expand All @@ -252,7 +342,7 @@ def run_backend_prod(
"--log-level",
loglevel.value,
"--workers",
str(num_workers),
str(_get_backend_workers()),
]
processes.new_process(
command,
Expand All @@ -262,6 +352,47 @@ def run_backend_prod(
)


def run_granian_backend_prod(host, port, loglevel):
"""Run the backend in production mode using Granian.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
from reflex.utils import processes

try:
from granian.constants import Interfaces # type: ignore

command = [
"granian",
"--workers",
str(_get_backend_workers()),
"--log-level",
"critical",
"--host",
host,
"--port",
str(port),
"--interface",
str(Interfaces.ASGI),
get_granian_target(),
]
processes.new_process(
command,
run=True,
show_logs=True,
env={
constants.SKIP_COMPILE_ENV_VAR: "yes"
}, # skip compile for prod backend
)
except ImportError:
console.error(
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
)


def output_system_info():
"""Show system information if the loglevel is in DEBUG."""
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
Expand Down
6 changes: 3 additions & 3 deletions tests/units/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
({"a": 1}, {"a": 1}),
({"a": LiteralVar.create("abc")}, {"a": "abc"}),
({"test_case": 1}, {"testCase": 1}),
({"test_case": {"a": 1}}, {"testCase": {"a": 1}}),
({":test_case": {"a": 1}}, {":testCase": {"a": 1}}),
({"::test_case": {"a": 1}}, {"::testCase": {"a": 1}}),
({"test_case": {"a": 1}}, {"test_case": {"a": 1}}),
({":test_case": {"a": 1}}, {":test_case": {"a": 1}}),
({"::test_case": {"a": 1}}, {"::test_case": {"a": 1}}),
(
{"::-webkit-scrollbar": {"display": "none"}},
{"::-webkit-scrollbar": {"display": "none"}},
Expand Down

0 comments on commit f12adc9

Please sign in to comment.