only allow explicit shared, only validate local assets if not backend_only
This commit is contained in:
parent
868fffcf83
commit
0b240dec42
@ -10,8 +10,8 @@ from reflex import constants
|
|||||||
|
|
||||||
def asset(
|
def asset(
|
||||||
path: str,
|
path: str,
|
||||||
|
shared: bool = False,
|
||||||
subfolder: Optional[str] = None,
|
subfolder: Optional[str] = None,
|
||||||
shared: Optional[bool] = None,
|
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Add an asset to the app, either shared as a symlink or local.
|
"""Add an asset to the app, either shared as a symlink or local.
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ def asset(
|
|||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
# my_custom_javascript.js is a shared asset located next to the including python file.
|
# my_custom_javascript.js is a shared asset located next to the including python file.
|
||||||
rx.script(src=rx._x.asset(path="my_custom_javascript.js"))
|
rx.script(src=rx._x.asset(path="my_custom_javascript.js", shared=True))
|
||||||
rx.image(src=rx._x.asset(path="test_image.png", subfolder="subfolder"))
|
rx.image(src=rx._x.asset(path="test_image.png", shared=True, subfolder="subfolder"))
|
||||||
```
|
```
|
||||||
|
|
||||||
Local/Internal assets:
|
Local/Internal assets:
|
||||||
@ -33,67 +33,48 @@ def asset(
|
|||||||
```python
|
```python
|
||||||
# local_image.png is an asset located in the app's assets/ directory. It cannot be shared when developing a library.
|
# local_image.png is an asset located in the app's assets/ directory. It cannot be shared when developing a library.
|
||||||
rx.image(src=rx._x.asset(path="local_image.png"))
|
rx.image(src=rx._x.asset(path="local_image.png"))
|
||||||
# Explicit shared=False is only needed if you have a local_image.png next to the including python file.
|
|
||||||
rx.image(src=rx._x.asset(path="local_image.png", shared=False))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The relative path of the asset.
|
path: The relative path of the asset.
|
||||||
subfolder: The directory to place the shared asset in.
|
subfolder: The directory to place the shared asset in.
|
||||||
shared: Whether to expose the asset to other apps. None means auto-detect.
|
shared: Whether to expose the asset to other apps.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If the file does not exist.
|
FileNotFoundError: If the file does not exist.
|
||||||
ValueError: If shared is not explicitly set and both shared and local assets exist.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The relative URL to the asset.
|
The relative URL to the asset.
|
||||||
"""
|
"""
|
||||||
|
assets = constants.Dirs.APP_ASSETS
|
||||||
|
backend_only = os.environ.get(constants.ENV_BACKEND_ONLY)
|
||||||
|
|
||||||
|
# Local asset handling
|
||||||
|
if not shared:
|
||||||
|
cwd = Path.cwd()
|
||||||
|
src_file_local = cwd / assets / path
|
||||||
|
if subfolder is not None:
|
||||||
|
raise ValueError("Subfolder is not supported for local assets.")
|
||||||
|
if not backend_only and not src_file_local.exists():
|
||||||
|
raise FileNotFoundError(f"File not found: {src_file_local}")
|
||||||
|
return f"/{path}"
|
||||||
|
|
||||||
|
# Shared asset handling
|
||||||
# Determine the file by which the asset is exposed.
|
# Determine the file by which the asset is exposed.
|
||||||
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
|
||||||
|
|
||||||
cwd = Path.cwd()
|
|
||||||
assets = constants.Dirs.APP_ASSETS
|
|
||||||
external = constants.Dirs.EXTERNAL_APP_ASSETS
|
external = constants.Dirs.EXTERNAL_APP_ASSETS
|
||||||
|
|
||||||
src_file_shared = Path(calling_file).parent / path
|
src_file_shared = Path(calling_file).parent / path
|
||||||
src_file_local = cwd / assets / path
|
if not src_file_shared.exists():
|
||||||
|
|
||||||
shared_exists = src_file_shared.exists()
|
|
||||||
local_exists = src_file_local.exists()
|
|
||||||
|
|
||||||
# Determine whether the asset is shared or local.
|
|
||||||
if shared is None:
|
|
||||||
if shared_exists and local_exists:
|
|
||||||
raise ValueError(
|
|
||||||
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:
|
|
||||||
raise FileNotFoundError(
|
|
||||||
f"Could not find file, neither at shared location {src_file_shared} nor at local location {src_file_local}"
|
|
||||||
)
|
|
||||||
shared = shared_exists
|
|
||||||
|
|
||||||
# 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"/{path}"
|
|
||||||
|
|
||||||
# Shared asset handling
|
|
||||||
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(".", "/")
|
caller_module_path = module.__name__.replace(".", "/")
|
||||||
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path
|
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 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)
|
||||||
@ -105,5 +86,4 @@ def asset(
|
|||||||
):
|
):
|
||||||
dst_file.symlink_to(src_file_shared)
|
dst_file.symlink_to(src_file_shared)
|
||||||
|
|
||||||
asset_url = f"/{external}/{subfolder}/{path}"
|
return f"/{external}/{subfolder}/{path}"
|
||||||
return asset_url
|
|
||||||
|
@ -11,7 +11,7 @@ import reflex.constants as constants
|
|||||||
def test_shared_asset() -> None:
|
def test_shared_asset() -> None:
|
||||||
"""Test shared assets."""
|
"""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(path="custom_script.js", shared=True, subfolder="subfolder")
|
||||||
assert asset == "/external/test_assets/subfolder/custom_script.js"
|
assert asset == "/external/test_assets/subfolder/custom_script.js"
|
||||||
result_file = Path(
|
result_file = Path(
|
||||||
Path.cwd(), "assets/external/test_assets/subfolder/custom_script.js"
|
Path.cwd(), "assets/external/test_assets/subfolder/custom_script.js"
|
||||||
@ -19,10 +19,10 @@ def test_shared_asset() -> None:
|
|||||||
assert result_file.exists()
|
assert result_file.exists()
|
||||||
|
|
||||||
# Running a second time should not raise an error.
|
# Running a second time should not raise an error.
|
||||||
asset = rx._x.asset("custom_script.js", "subfolder")
|
asset = rx._x.asset(path="custom_script.js", shared=True, subfolder="subfolder")
|
||||||
|
|
||||||
# Test the asset function without a subfolder.
|
# Test the asset function without a subfolder.
|
||||||
asset = rx._x.asset("custom_script.js")
|
asset = rx._x.asset(path="custom_script.js", shared=True)
|
||||||
assert asset == "/external/test_assets/custom_script.js"
|
assert asset == "/external/test_assets/custom_script.js"
|
||||||
result_file = Path(Path.cwd(), "assets/external/test_assets/custom_script.js")
|
result_file = Path(Path.cwd(), "assets/external/test_assets/custom_script.js")
|
||||||
assert result_file.exists()
|
assert result_file.exists()
|
||||||
@ -70,30 +70,12 @@ def custom_script_in_asset_dir() -> Generator[Path, None, None]:
|
|||||||
path.unlink()
|
path.unlink()
|
||||||
|
|
||||||
|
|
||||||
def test_both_existing_implicit(custom_script_in_asset_dir: Path) -> None:
|
def test_local_asset(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.
|
"""Test that no error is raised if shared is set and both files exist.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
custom_script_in_asset_dir: Fixture that creates a custom_script.js file in the app's assets directory.
|
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)
|
asset = rx._x.asset("custom_script.js", shared=False)
|
||||||
assert asset == "/custom_script.js"
|
assert asset == "/custom_script.js"
|
||||||
|
Loading…
Reference in New Issue
Block a user