Skip frontend packages install if previously done ()

This commit is contained in:
jackie-pc 2024-01-16 17:52:28 -08:00 committed by GitHub
parent 1aca1b677f
commit 2c270585ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 6 deletions

View File

@ -595,7 +595,7 @@ class App(Base):
continue
_frontend_packages.append(package)
page_imports.update(_frontend_packages)
prerequisites.install_frontend_packages(page_imports)
prerequisites.install_frontend_packages(page_imports, get_config())
def _app_root(self, app_wrappers: dict[tuple[int, str], Component]) -> Component:
for component in tuple(app_wrappers.values()):

View File

@ -16,6 +16,7 @@ import zipfile
from fileinput import FileInput
from pathlib import Path
from types import ModuleType
from typing import Callable
import httpx
import pkg_resources
@ -26,7 +27,7 @@ from redis.asyncio import Redis
from reflex import constants, model
from reflex.compiler import templates
from reflex.config import get_config
from reflex.config import Config, get_config
from reflex.utils import console, path_ops, processes
@ -619,14 +620,64 @@ def install_bun():
)
def install_frontend_packages(packages: set[str]):
def _write_cached_procedure_file(payload: str, cache_file: str):
with open(cache_file, "w") as f:
f.write(payload)
def _read_cached_procedure_file(cache_file: str) -> str | None:
if os.path.exists(cache_file):
with open(cache_file, "r") as f:
return f.read()
return None
def _clear_cached_procedure_file(cache_file: str):
if os.path.exists(cache_file):
os.remove(cache_file)
def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
"""Decorator to cache the runs of a procedure on disk. Procedures should not have
a return value.
Args:
cache_file: The file to store the cache payload in.
payload_fn: Function that computes cache payload from function args
Returns:
The decorated function.
"""
def _inner_decorator(func):
def _inner(*args, **kwargs):
payload = _read_cached_procedure_file(cache_file)
new_payload = payload_fn(*args, **kwargs)
if payload != new_payload:
_clear_cached_procedure_file(cache_file)
func(*args, **kwargs)
_write_cached_procedure_file(new_payload, cache_file)
return _inner
return _inner_decorator
@cached_procedure(
cache_file=os.path.join(
constants.Dirs.WEB, "reflex.install_frontend_packages.cached"
),
payload_fn=lambda p, c: f"{repr(sorted(list(p)))},{c.json()}",
)
def install_frontend_packages(packages: set[str], config: Config):
"""Installs the base and custom frontend packages.
Args:
packages: A list of package names to be installed.
config: The config object.
Example:
>>> install_frontend_packages(["react", "react-dom"])
>>> install_frontend_packages(["react", "react-dom"], get_config())
"""
# Install the base packages.
process = processes.new_process(
@ -637,7 +688,6 @@ def install_frontend_packages(packages: set[str]):
processes.show_status("Installing base frontend packages", process)
config = get_config()
if config.tailwind is not None:
# install tailwind and tailwind plugins as dev dependencies.
process = processes.new_process(

View File

@ -1,10 +1,15 @@
import tempfile
from unittest.mock import Mock, mock_open
import pytest
from reflex import constants
from reflex.config import Config
from reflex.utils.prerequisites import _update_next_config, initialize_requirements_txt
from reflex.utils.prerequisites import (
_update_next_config,
cached_procedure,
initialize_requirements_txt,
)
@pytest.mark.parametrize(
@ -139,3 +144,36 @@ def test_requirements_txt_other_encoding(mocker):
open_mock().write.call_args[0][0]
== f"\n{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
)
def test_cached_procedure():
call_count = 0
@cached_procedure(tempfile.mktemp(), payload_fn=lambda: "constant")
def _function_with_no_args():
nonlocal call_count
call_count += 1
_function_with_no_args()
assert call_count == 1
_function_with_no_args()
assert call_count == 1
call_count = 0
@cached_procedure(
tempfile.mktemp(),
payload_fn=lambda *args, **kwargs: f"{repr(args), repr(kwargs)}",
)
def _function_with_some_args(*args, **kwargs):
nonlocal call_count
call_count += 1
_function_with_some_args(1, y=2)
assert call_count == 1
_function_with_some_args(1, y=2)
assert call_count == 1
_function_with_some_args(100, y=300)
assert call_count == 2
_function_with_some_args(100, y=300)
assert call_count == 2