Skip to content

Commit d444f66

Browse files
Speed up mount path: cheap arity check, reuse first(), leaner emit
- weak(): read __code__.co_argcount instead of building a full inspect.Signature for every wrapped watcher callback; this runs for every dynamic bind during create/mount. - ComponentFragment.mount(): use the existing first() helper to resolve the component root element instead of a hand-rolled BFS with a per-mount deque import. Adds a regression test for a component whose root element sits behind a v-if wrapper. - Component.emit(): avoid creating defaultdict entries for events that have no handlers and skip the set copy when empty. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c70f5b4 commit d444f66

4 files changed

Lines changed: 38 additions & 22 deletions

File tree

collagraph/component.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ def inject(self, key, default=None):
136136
def emit(self, event, *args, **kwargs):
137137
"""Call event handlers for the given event. Any args and kwargs will be passed
138138
on to the registered handlers."""
139-
for handler in self._event_handlers[event].copy():
140-
handler(*args, **kwargs)
139+
if handlers := self._event_handlers.get(event):
140+
for handler in tuple(handlers):
141+
handler(*args, **kwargs)
141142

142143
def add_event_handler(self, event, handler):
143144
"""Adds an event handler for the given event."""

collagraph/fragment.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -809,20 +809,7 @@ def mount(self, target: Any, anchor: Any | None = None):
809809
child.mount(target, anchor)
810810

811811
if self.component:
812-
from collections import deque
813-
814-
# Use fifo data structure (double-ended queue)
815-
lookup = deque(self.children)
816-
try:
817-
while True:
818-
child = lookup.popleft()
819-
if element := child.element:
820-
self.component._element = element
821-
lookup.clear()
822-
break
823-
lookup.extend(child.children)
824-
except IndexError:
825-
pass
812+
self.component._element = self.first()
826813

827814
# Register static component ref after component is mounted
828815
# (dynamic refs are handled by the watcher created in create())

collagraph/weak.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from functools import wraps
2-
from inspect import signature
32
from weakref import ref
43

54

@@ -29,11 +28,7 @@ def callback(self):
2928
weak_obj = ref(obj)
3029

3130
def wrapper(method):
32-
sig = signature(method)
33-
non_default_parameters = [
34-
par for par in sig.parameters.values() if par.default is par.empty
35-
]
36-
nr_arguments = len(non_default_parameters)
31+
nr_arguments = method.__code__.co_argcount - len(method.__defaults__ or ())
3732

3833
if nr_arguments == 0:
3934
raise TypeError(

tests/test_component.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,36 @@ def mounted(self):
169169
assert Example.component is not None
170170
assert isinstance(Example.component, Example)
171171
assert Example.component.element is container["children"][0]
172+
173+
174+
def test_component_element_behind_control_flow_wrapper(parse_source):
175+
"""
176+
A component whose render root is itself a wrapper (e.g. v-if) around a
177+
single element should still resolve `component.element` to that nested
178+
element (regression test for ComponentFragment.mount() using
179+
`self.first()` instead of a manual BFS over `self.children`).
180+
"""
181+
Example, _ = parse_source(
182+
"""
183+
<el v-if="show" />
184+
185+
<script>
186+
import collagraph as cg
187+
188+
class Example(cg.Component):
189+
component = None
190+
191+
def mounted(self):
192+
Example.component = self
193+
</script>
194+
"""
195+
)
196+
197+
gui = cg.Collagraph(cg.DictRenderer(), event_loop_type=cg.EventLoopType.SYNC)
198+
container = {"type": "root"}
199+
state = reactive({"show": True})
200+
gui.render(Example, container, state=state)
201+
202+
assert Example.component is not None
203+
assert isinstance(Example.component, Example)
204+
assert Example.component.element is container["children"][0]

0 commit comments

Comments
 (0)