reflex/reflex/experimental/assets.py
2024-07-04 20:55:09 +02:00

99 lines
3.2 KiB
Python

"""Helper functions for adding assets to the app."""
import inspect
import os
from pathlib import Path
from typing import Optional
from reflex import constants
def asset(
filename: str,
subfolder: Optional[str] = None,
shared: Optional[bool] = None,
) -> str:
"""Add an asset to the app, either shared as a symlink or local.
Shared/External/Library assets:
Place the file next to your including python file.
Links the file to the app's external assets directory.
Local/Internal assets:
Place the file in the app's assets/ directory.
Example:
```python
rx.script(src=rx._x.asset("my_custom_javascript.js"))
rx.image(src=rx._x.asset("test_image.png","subfolder"))
```
Args:
filename: The relative filename of the asset.
subfolder: The directory to place the asset in.
shared: Whether to expose the asset to other apps. None means auto-detect.
Raises:
FileNotFoundError: If the file does not exist.
ValueError: If shared is not explicitly set and both shared and local assets exist.
Returns:
The relative URL to the asset.
"""
# Determine the file by which the asset is exposed.
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
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 {filename}. "
+ "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 not local_exists:
raise FileNotFoundError(f"File not found: {src_file_local}")
return f"/{filename}"
# Shared asset handling
if not shared_exists:
raise FileNotFoundError(f"File not found: {src_file_shared}")
# 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
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}"
return asset_url