minor improvements, add more tests

This commit is contained in:
Benedikt Bartscher 2024-07-04 23:26:54 +02:00
parent 1c4c9fd515
commit bec31ff406
No known key found for this signature in database
2 changed files with 79 additions and 14 deletions

View File

@ -9,7 +9,7 @@ from reflex import constants
def asset(
filename: str,
path: str,
subfolder: Optional[str] = None,
shared: Optional[bool] = None,
) -> str:
@ -29,7 +29,7 @@ def asset(
```
Args:
filename: The relative filename of the asset.
path: The relative path of the asset.
subfolder: The directory to place the asset in.
shared: Whether to expose the asset to other apps. None means auto-detect.
@ -44,16 +44,13 @@ def asset(
calling_file = inspect.stack()[1].filename
module = inspect.getmodule(inspect.stack()[1][0])
assert module is not None
caller_module_path = module.__name__.replace(".", "/")
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
cwd = Path.cwd()
assets = constants.Dirs.APP_ASSETS
external = constants.Dirs.EXTERNAL_APP_ASSETS
src_file_shared = Path(calling_file).parent / filename
src_file_local = cwd / assets / subfolder / filename
src_file_shared = Path(calling_file).parent / path
src_file_local = cwd / assets / path
shared_exists = src_file_shared.exists()
local_exists = src_file_local.exists()
@ -62,7 +59,7 @@ def asset(
if shared is None:
if shared_exists and local_exists:
raise ValueError(
f"Both shared and local assets exist for {filename}. "
f"Both shared and local assets exist for {path}. "
+ "Please explicitly set shared=True or shared=False."
)
if not shared_exists and not local_exists:
@ -73,26 +70,31 @@ def asset(
# Local asset handling
if not shared:
if subfolder is not None:
raise ValueError("Subfolder is not supported for local assets.")
if not local_exists:
raise FileNotFoundError(f"File not found: {src_file_local}")
return f"/{filename}"
return f"/{path}"
# Shared asset handling
if not shared_exists:
raise FileNotFoundError(f"File not found: {src_file_shared}")
caller_module_path = module.__name__.replace(".", "/")
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
# Symlink the asset to the app's external assets directory if running frontend.
if not os.environ.get(constants.ENV_BACKEND_ONLY):
# Create the asset folder in the currently compiling app.
asset_folder = Path.cwd() / assets / external / subfolder
asset_folder.mkdir(parents=True, exist_ok=True)
dst_file = asset_folder / filename
dst_file = asset_folder / path
if not dst_file.exists() and (
not dst_file.is_symlink() or dst_file.resolve() != src_file_shared.resolve()
):
dst_file.symlink_to(src_file_shared)
asset_url = f"/{external}/{subfolder}/{filename}"
asset_url = f"/{external}/{subfolder}/{path}"
return asset_url

View File

@ -1,14 +1,15 @@
import shutil
from collections.abc import Generator
from pathlib import Path
import pytest
import reflex as rx
import reflex.constants as constants
def test_asset():
# Test the asset function.
def test_shared_asset() -> None:
"""Test shared assets."""
# The asset function copies a file to the app's external assets directory.
asset = rx._x.asset("custom_script.js", "subfolder")
assert asset == "/external/test_assets/subfolder/custom_script.js"
@ -34,3 +35,65 @@ def test_asset():
# Nothing is done to assets when file does not exist.
assert not Path(Path.cwd() / "assets/external").exists()
@pytest.mark.parametrize(
"path,shared",
[
pytest.param("non_existing_file", True),
pytest.param("non_existing_file", False),
],
)
def test_invalid_assets(path: str, shared: bool) -> None:
"""Test that asset raises an error when the file does not exist.
Args:
path: The path to the asset.
shared: Whether the asset should be shared.
"""
with pytest.raises(FileNotFoundError):
_ = rx._x.asset(path, shared=shared)
@pytest.fixture
def custom_script_in_asset_dir() -> Generator[Path, None, None]:
"""Create a custom_script.js file in the app's assets directory.
Yields:
The path to the custom_script.js file.
"""
asset_dir = Path.cwd() / constants.Dirs.APP_ASSETS
asset_dir.mkdir(exist_ok=True)
path = asset_dir / "custom_script.js"
path.touch()
yield path
path.unlink()
def test_both_existing_implicit(custom_script_in_asset_dir: Path) -> None:
"""Test that asset raises an error if shared is not set and both files exist.
Args:
custom_script_in_asset_dir: Fixture that creates a custom_script.js file in the app's assets directory.
"""
with pytest.raises(ValueError) as e:
_ = rx._x.asset("custom_script.js")
assert (
str(e.value)
== "Both shared and local assets exist for custom_script.js. Please explicitly set shared=True or shared=False."
)
def test_both_existing_explicit(custom_script_in_asset_dir: Path) -> None:
"""Test that no error is raised if shared is set and both files exist.
Args:
custom_script_in_asset_dir: Fixture that creates a custom_script.js file in the app's assets directory.
"""
asset = rx._x.asset("custom_script.js", shared=True)
assert asset == "/external/test_assets/custom_script.js"
asset = rx._x.asset("custom_script.js", shared=False)
assert asset == "/custom_script.js"