소스 검색

Improve AppHarness behavior when app already exists in a relative directory (#5025)

* cached_procedure can accept a cache_file_fn

If the cache file should be determined at runtime instead of at import time.

* if the app already exists, reinit the web folder

for example, when a test sets REFLEX_WEB_WORKDIR, then we must copy the
template files in there.
Masen Furer 2 달 전
부모
커밋
df9fcf9273
3개의 변경된 파일41개의 추가작업 그리고 7개의 파일을 삭제
  1. 4 0
      reflex/testing.py
  2. 22 6
      reflex/utils/prerequisites.py
  3. 15 1
      tests/units/test_prerequisites.py

+ 4 - 0
reflex/testing.py

@@ -268,6 +268,10 @@ class AppHarness:
                     loglevel=reflex.constants.LogLevel.INFO,
                     loglevel=reflex.constants.LogLevel.INFO,
                 )
                 )
                 self.app_module_path.write_text(source_code)
                 self.app_module_path.write_text(source_code)
+        else:
+            # Just initialize the web folder.
+            with chdir(self.app_path):
+                reflex.utils.prerequisites.initialize_frontend_dependencies()
         with chdir(self.app_path):
         with chdir(self.app_path):
             # ensure config and app are reloaded when testing different app
             # ensure config and app are reloaded when testing different app
             reflex.config.get_config(reload=True)
             reflex.config.get_config(reload=True)

+ 22 - 6
reflex/utils/prerequisites.py

@@ -1180,26 +1180,39 @@ def _clear_cached_procedure_file(cache_file: str | Path):
         cache_file.unlink()
         cache_file.unlink()
 
 
 
 
-def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
+def cached_procedure(
+    cache_file: str | None,
+    payload_fn: Callable[..., str],
+    cache_file_fn: Callable[[], str] | None = None,
+):
     """Decorator to cache the runs of a procedure on disk. Procedures should not have
     """Decorator to cache the runs of a procedure on disk. Procedures should not have
        a return value.
        a return value.
 
 
     Args:
     Args:
         cache_file: The file to store the cache payload in.
         cache_file: The file to store the cache payload in.
-        payload_fn: Function that computes cache payload from function args
+        payload_fn: Function that computes cache payload from function args.
+        cache_file_fn: Function that computes the cache file name at runtime.
 
 
     Returns:
     Returns:
         The decorated function.
         The decorated function.
+
+    Raises:
+        ValueError: If both cache_file and cache_file_fn are provided.
     """
     """
+    if cache_file and cache_file_fn is not None:
+        raise ValueError("cache_file and cache_file_fn cannot both be provided.")
 
 
     def _inner_decorator(func: Callable):
     def _inner_decorator(func: Callable):
         def _inner(*args, **kwargs):
         def _inner(*args, **kwargs):
-            payload = _read_cached_procedure_file(cache_file)
+            _cache_file = cache_file_fn() if cache_file_fn is not None else cache_file
+            if not _cache_file:
+                raise ValueError("Unknown cache file, cannot cache result.")
+            payload = _read_cached_procedure_file(_cache_file)
             new_payload = payload_fn(*args, **kwargs)
             new_payload = payload_fn(*args, **kwargs)
             if payload != new_payload:
             if payload != new_payload:
-                _clear_cached_procedure_file(cache_file)
+                _clear_cached_procedure_file(_cache_file)
                 func(*args, **kwargs)
                 func(*args, **kwargs)
-                _write_cached_procedure_file(new_payload, cache_file)
+                _write_cached_procedure_file(new_payload, _cache_file)
 
 
         return _inner
         return _inner
 
 
@@ -1207,8 +1220,11 @@ def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
 
 
 
 
 @cached_procedure(
 @cached_procedure(
-    cache_file=str(get_web_dir() / "reflex.install_frontend_packages.cached"),
+    cache_file_fn=lambda: str(
+        get_web_dir() / "reflex.install_frontend_packages.cached"
+    ),
     payload_fn=lambda p, c: f"{sorted(p)!r},{c.json()}",
     payload_fn=lambda p, c: f"{sorted(p)!r},{c.json()}",
+    cache_file=None,
 )
 )
 def install_frontend_packages(packages: set[str], config: Config):
 def install_frontend_packages(packages: set[str], config: Config):
     """Installs the base and custom frontend packages.
     """Installs the base and custom frontend packages.

+ 15 - 1
tests/units/test_prerequisites.py

@@ -206,7 +206,7 @@ def test_cached_procedure():
     call_count = 0
     call_count = 0
 
 
     @cached_procedure(
     @cached_procedure(
-        tempfile.mktemp(),
+        cache_file=tempfile.mktemp(),
         payload_fn=lambda *args, **kwargs: f"{repr(args), repr(kwargs)}",
         payload_fn=lambda *args, **kwargs: f"{repr(args), repr(kwargs)}",
     )
     )
     def _function_with_some_args(*args, **kwargs):
     def _function_with_some_args(*args, **kwargs):
@@ -222,6 +222,20 @@ def test_cached_procedure():
     _function_with_some_args(100, y=300)
     _function_with_some_args(100, y=300)
     assert call_count == 2
     assert call_count == 2
 
 
+    call_count = 0
+
+    @cached_procedure(
+        cache_file=None, cache_file_fn=tempfile.mktemp, payload_fn=lambda: "constant"
+    )
+    def _function_with_no_args_fn():
+        nonlocal call_count
+        call_count += 1
+
+    _function_with_no_args_fn()
+    assert call_count == 1
+    _function_with_no_args_fn()
+    assert call_count == 2
+
 
 
 def test_get_cpu_info():
 def test_get_cpu_info():
     cpu_info = get_cpu_info()
     cpu_info = get_cpu_info()