Skip to content

Commit 62fe262

Browse files
fsecada01claude
andcommitted
fix: idempotent registry re-registration and namespace static files (0.3.1b0)
Closes #8 — registry.register() now silently accepts re-registration of the exact same class under the same name (idempotent no-op). Genuine conflicts (different class claiming the same name) still raise ValueError with a clearer message. Prevents spurious errors in Django test discovery where the same module is imported under two dotted paths. Closes #9 — move component-client.js and component-client.d.ts from static/js/ into the Django-conventional static/component_framework/js/ subfolder. collectstatic now produces component_framework/js/component-client.js, matching the documented {% static 'component_framework/js/component-client.js' %} path and avoiding collisions with other installed apps. Bump version 0.3.0b0 → 0.3.1b0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fb57e91 commit 62fe262

6 files changed

Lines changed: 30 additions & 7 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "component-framework"
3-
version = "0.3.0b0"
3+
version = "0.3.1b0"
44
description = "Framework-agnostic server components with LiveView-style interactivity"
55
readme = "README.md"
66
requires-python = ">=3.11"

src/component_framework/core/registry.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ class Counter(Component):
2121

2222
def decorator(cls: type[Component]):
2323
if name in self._registry:
24-
raise ValueError(f"Component '{name}' already registered")
24+
if self._registry[name] is cls:
25+
return cls # idempotent: same class re-imported, no-op
26+
raise ValueError(
27+
f"Component '{name}' already registered by a different class "
28+
f"({self._registry[name].__qualname__!r})"
29+
)
2530
self._registry[name] = cls
2631
return cls
2732

src/component_framework/static/js/component-client.d.ts renamed to src/component_framework/static/component_framework/js/component-client.d.ts

File renamed without changes.

src/component_framework/static/js/component-client.js renamed to src/component_framework/static/component_framework/js/component-client.js

File renamed without changes.

tests/test_registry.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,21 @@ def test_register_duplicate_raises(self):
2222
class First(Component):
2323
pass
2424

25-
with pytest.raises(ValueError, match="Component 'dup' already registered"):
25+
with pytest.raises(ValueError, match="already registered by a different class"):
2626

2727
@self.reg.register("dup")
2828
class Second(Component):
2929
pass
3030

31+
def test_register_same_class_idempotent(self):
32+
@self.reg.register("idempotent")
33+
class MyComp(Component):
34+
pass
35+
36+
# Re-registering the exact same class must not raise
37+
self.reg.register("idempotent")(MyComp)
38+
assert self.reg.get("idempotent") is MyComp
39+
3140
def test_get_missing_returns_none(self):
3241
assert self.reg.get("nonexistent") is None
3342

tests/test_testing_utils.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,22 @@ def test_retrievable_by_name(self) -> None:
186186
register_test_component(reg, "btn", cls)
187187
assert reg.get("btn") is cls
188188

189-
def test_duplicate_name_raises(self) -> None:
190-
"""Registering the same name twice raises ValueError."""
189+
def test_duplicate_name_different_class_raises(self) -> None:
190+
"""Registering a different class under the same name raises ValueError."""
191+
reg = ComponentRegistry()
192+
cls1 = make_component("dup")
193+
cls2 = make_component("dup")
194+
register_test_component(reg, "dup", cls1)
195+
with pytest.raises(ValueError, match="already registered by a different class"):
196+
register_test_component(reg, "dup", cls2)
197+
198+
def test_duplicate_name_same_class_idempotent(self) -> None:
199+
"""Registering the same class under the same name is a no-op."""
191200
reg = ComponentRegistry()
192201
cls = make_component("dup")
193202
register_test_component(reg, "dup", cls)
194-
with pytest.raises(ValueError, match="already registered"):
195-
register_test_component(reg, "dup", cls)
203+
register_test_component(reg, "dup", cls) # must not raise
204+
assert reg.get("dup") is cls
196205

197206

198207
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)