v0.142.0
 1from __future__ import annotations
 2
 3import os
 4from collections.abc import Iterator
 5from pathlib import Path
 6
 7from plain.packages import packages_registry
 8from plain.runtime import APP_PATH
 9
10_APP_ASSETS_DIR = APP_PATH / "assets"
11
12_SKIP_ASSETS = (".DS_Store", ".gitignore")
13
14
15class Asset:
16    def __init__(self, *, url_path: str, absolute_path: str):
17        self.url_path = url_path
18        self.absolute_path = absolute_path
19
20    def __str__(self) -> str:
21        return self.url_path
22
23
24def _iter_assets() -> Iterator[Asset]:
25    """
26    Iterate all valid asset files found in the installed
27    packages and the app itself.
28    """
29
30    def __iter_assets_dir(path: str | Path) -> Iterator[tuple[str, str]]:
31        for root, _, files in os.walk(path):
32            for f in files:
33                if f in _SKIP_ASSETS:
34                    continue
35                abs_path = os.path.join(root, f)
36                url_path = os.path.relpath(abs_path, path)
37                yield url_path, abs_path
38
39    for asset_dir in _iter_asset_dirs():
40        for url_path, abs_path in __iter_assets_dir(asset_dir):
41            yield Asset(url_path=url_path, absolute_path=abs_path)
42
43
44def _iter_asset_dirs() -> Iterator[str | Path]:
45    """
46    Iterate all directories containing assets, from installed
47    packages and from app/assets.
48    """
49    # Iterate the installed package assets, in order
50    for pkg in packages_registry.get_package_configs():
51        asset_dir = os.path.join(pkg.path, "assets")
52        if os.path.exists(asset_dir):
53            yield asset_dir
54
55    # The app/assets take priority over everything
56    if _APP_ASSETS_DIR.exists():
57        yield _APP_ASSETS_DIR