Skip to content

Commit cce4605

Browse files
Refactor asset collection to new internal plugin (#186)
* Refactor asset collection using plugin system * update changelog * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 70ea87f commit cce4605

File tree

10 files changed

+107
-59
lines changed

10 files changed

+107
-59
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/
2121
### Added
2222

2323
- Added plugin system infrastructure using `pluggy`.
24+
- Added plugin hook for component asset collection.
25+
26+
### Changed
27+
28+
- **Internal**: Refactored asset collection to use plugin system instead of directly in main library.
29+
- **Internal**: Moved `Asset.from_path` functionality to the new file assets plugin.
2430

2531
## [0.14.2]
2632

pyproject.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,7 @@ classifiers = [
5555
"Programming Language :: Python :: 3.13",
5656
"Programming Language :: Python :: Implementation :: CPython"
5757
]
58-
dependencies = [
59-
"cachetools>=5.5.0",
60-
"django>=4.2",
61-
"pluggy>=1.5.0"
62-
]
58+
dependencies = ["cachetools>=5.5.0", "django>=4.2", "pluggy>=1.5.0"]
6359
description = "High-flying components for perfectionists with deadlines"
6460
dynamic = ["version"]
6561
keywords = []
@@ -78,6 +74,12 @@ exclude = ["**/__pycache__"]
7874
include = ["src"]
7975
reportAny = false
8076
reportExplicitAny = false
77+
reportUnusedCallResult = false
78+
79+
[[tool.basedpyright.executionEnvironments]]
80+
reportReturnType = false
81+
reportUnusedParameter = false
82+
root = "src/django_bird/plugins/hookspecs.py"
8183

8284
[[tool.basedpyright.executionEnvironments]]
8385
root = "src"
@@ -123,7 +125,6 @@ fail_under = 98
123125
[tool.coverage.run]
124126
omit = [
125127
"src/django_bird/migrations/*",
126-
"src/django_bird/plugins/*", # TODO: remove when plugin hooks implemented
127128
"src/django_bird/_typing.py",
128129
"src/django_bird/views.py", # TODO: remove when not empty
129130
"tests/*"
@@ -166,6 +167,10 @@ ignore_errors = true
166167
ignore_missing_imports = true
167168
module = ["*.migrations.*", "docs.*", "tests.*"]
168169

170+
[[tool.mypy.overrides]]
171+
disable_error_code = "empty-body"
172+
module = ["django_bird.plugins.hookspecs"]
173+
169174
[tool.mypy_django_plugin]
170175
ignore_missing_model_attributes = true
171176

src/django_bird/components.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
22

3+
import itertools
34
from collections import defaultdict
45
from collections.abc import Generator
6+
from collections.abc import Iterable
57
from dataclasses import dataclass
68
from dataclasses import field
79
from hashlib import md5
@@ -22,6 +24,7 @@
2224
from .params import Param
2325
from .params import Params
2426
from .params import Value
27+
from .plugins import pm
2528
from .staticfiles import Asset
2629
from .staticfiles import AssetType
2730
from .templates import gather_bird_tag_template_usage
@@ -82,17 +85,12 @@ def from_abs_path(cls, path: Path, root: Path) -> Component:
8285
def from_name(cls, name: str):
8386
template_names = get_template_names(name)
8487
template = select_template(template_names)
85-
assets: list[Asset] = [
86-
asset
87-
for asset_type in AssetType
88-
if (
89-
asset := Asset.from_path(
90-
Path(template.template.origin.name), asset_type
91-
)
92-
)
93-
is not None
94-
]
95-
return cls(name=name, template=template, assets=frozenset(assets))
88+
assets: list[Iterable[Asset]] = pm.hook.collect_component_assets(
89+
template_path=Path(template.template.origin.name)
90+
)
91+
return cls(
92+
name=name, template=template, assets=frozenset(itertools.chain(*assets))
93+
)
9694

9795

9896
class SequenceGenerator:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Iterable
4+
from pathlib import Path
5+
6+
from django_bird import hookimpl
7+
from django_bird.staticfiles import Asset
8+
from django_bird.staticfiles import AssetType
9+
10+
11+
def find_component_asset(path: Path, asset_type: AssetType) -> Asset | None:
12+
asset_path = path.with_suffix(asset_type.ext)
13+
if asset_path.exists():
14+
return Asset(path=asset_path, type=asset_type)
15+
return None
16+
17+
18+
@hookimpl
19+
def collect_component_assets(template_path: Path) -> Iterable[Asset]:
20+
assets: list[Asset] = []
21+
for asset_type in AssetType:
22+
if asset := find_component_asset(template_path, asset_type):
23+
assets.append(asset)
24+
return assets
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
from __future__ import annotations
22

3+
from collections.abc import Iterable
4+
from pathlib import Path
5+
36
from pluggy import HookspecMarker
47

8+
from django_bird.staticfiles import Asset
9+
510
hookspec = HookspecMarker("django_bird")
11+
12+
13+
@hookspec
14+
def collect_component_assets(template_path: Path) -> Iterable[Asset]:
15+
"""Collect all assets associated with a component."""

src/django_bird/plugins/manager.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
pm = pluggy.PluginManager("django_bird")
1010
pm.add_hookspecs(hookspecs)
1111

12-
DEFAULT_PLUGINS: list[str] = []
12+
DEFAULT_PLUGINS: list[str] = [
13+
"django_bird.plugins.file_assets",
14+
]
15+
1316

1417
for plugin in DEFAULT_PLUGINS:
1518
mod = importlib.import_module(plugin)

src/django_bird/staticfiles.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,6 @@ def url(self) -> str | None:
110110
static_relative_path = Path(static_path).relative_to(self.template_dir)
111111
return self.storage.url(str(static_relative_path))
112112

113-
@classmethod
114-
def from_path(cls, path: Path, asset_type: AssetType):
115-
asset_path = path.with_suffix(asset_type.ext)
116-
if asset_path.exists():
117-
return cls(path=asset_path, type=asset_type)
118-
return None
119-
120113

121114
@final
122115
class BirdAssetStorage(StaticFilesStorage):

tests/plugins/__init__.py

Whitespace-only changes.

tests/plugins/test_file_asset.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from __future__ import annotations
2+
3+
from django_bird.plugins.file_assets import find_component_asset
4+
from django_bird.staticfiles import Asset
5+
from django_bird.staticfiles import AssetType
6+
from tests.utils import TestAsset
7+
from tests.utils import TestComponent
8+
9+
10+
def test_find_component_asset(templates_dir):
11+
button = TestComponent(name="button", content="<button>Click me</button>").create(
12+
templates_dir
13+
)
14+
button_css = TestAsset(
15+
component=button,
16+
content=".button { color: blue; }",
17+
asset_type=AssetType.CSS,
18+
).create()
19+
button_js = TestAsset(
20+
component=button, content="console.log('button');", asset_type=AssetType.JS
21+
).create()
22+
23+
assert find_component_asset(button.file, button_css.asset_type) == Asset(
24+
button_css.file, button_css.asset_type
25+
)
26+
assert find_component_asset(button.file, button_js.asset_type) == Asset(
27+
button_js.file, button_js.asset_type
28+
)
29+
30+
31+
def test_from_template_nested(templates_dir):
32+
button = TestComponent(
33+
name="button", content="<button>Click me</button>", sub_dir="nested"
34+
).create(templates_dir)
35+
button_css = TestAsset(
36+
component=button,
37+
content=".button { color: blue; }",
38+
asset_type=AssetType.CSS,
39+
).create()
40+
41+
assert find_component_asset(button.file, button_css.asset_type) == Asset(
42+
button_css.file, button_css.asset_type
43+
)

tests/test_staticfiles.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -198,40 +198,6 @@ def test_url_nonexistent(self, templates_dir):
198198

199199
assert asset.url is None
200200

201-
def test_from_path(self, templates_dir):
202-
button = TestComponent(
203-
name="button", content="<button>Click me</button>"
204-
).create(templates_dir)
205-
button_css = TestAsset(
206-
component=button,
207-
content=".button { color: blue; }",
208-
asset_type=AssetType.CSS,
209-
).create()
210-
button_js = TestAsset(
211-
component=button, content="console.log('button');", asset_type=AssetType.JS
212-
).create()
213-
214-
assert Asset.from_path(button.file, button_css.asset_type) == Asset(
215-
button_css.file, button_css.asset_type
216-
)
217-
assert Asset.from_path(button.file, button_js.asset_type) == Asset(
218-
button_js.file, button_js.asset_type
219-
)
220-
221-
def test_from_template_nested(self, templates_dir):
222-
button = TestComponent(
223-
name="button", content="<button>Click me</button>", sub_dir="nested"
224-
).create(templates_dir)
225-
button_css = TestAsset(
226-
component=button,
227-
content=".button { color: blue; }",
228-
asset_type=AssetType.CSS,
229-
).create()
230-
231-
assert Asset.from_path(button.file, button_css.asset_type) == Asset(
232-
button_css.file, button_css.asset_type
233-
)
234-
235201

236202
class TestBirdAssetFinder:
237203
def test_check(self):

0 commit comments

Comments
 (0)