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

View File

@ -1,14 +1,15 @@
import shutil import shutil
from collections.abc import Generator
from pathlib import Path from pathlib import Path
import pytest import pytest
import reflex as rx import reflex as rx
import reflex.constants as constants
def test_asset(): def test_shared_asset() -> None:
# Test the asset function. """Test shared assets."""
# The asset function copies a file to the app's external assets directory. # The asset function copies a file to the app's external assets directory.
asset = rx._x.asset("custom_script.js", "subfolder") asset = rx._x.asset("custom_script.js", "subfolder")
assert asset == "/external/test_assets/subfolder/custom_script.js" 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. # Nothing is done to assets when file does not exist.
assert not Path(Path.cwd() / "assets/external").exists() 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"