Skip to content

Commit ee68f65

Browse files
committed
AbstractElement.id is not needed
1 parent 547acde commit ee68f65

File tree

4 files changed

+40
-40
lines changed

4 files changed

+40
-40
lines changed

idom/core/element.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import abc
22
import inspect
3-
from uuid import uuid4
43
from functools import wraps
54
from typing import TYPE_CHECKING, Dict, Callable, Any, Tuple, Union
65

@@ -30,20 +29,7 @@ def constructor(*args: Any, **kwargs: Any) -> Element:
3029

3130
class AbstractElement(abc.ABC):
3231

33-
__slots__ = ["_id"]
34-
35-
if not hasattr(abc.ABC, "__weakref__"): # pragma: no cover
36-
__slots__.append("__weakref__")
37-
38-
def __init__(self) -> None:
39-
# we can't use `id(self)` because IDs are regularly re-used and the layout
40-
# relies on unique IDs to determine which elements have been deleted
41-
self._id = uuid4().hex
42-
43-
@property
44-
def id(self) -> str:
45-
"""The unique ID of the element."""
46-
return self._id
32+
__slots__ = [] if hasattr(abc.ABC, "__weakref__") else ["__weakref__"]
4733

4834
@abc.abstractmethod
4935
def render(self) -> "VdomDict":
@@ -81,7 +67,6 @@ def __init__(
8167
args: Tuple[Any, ...],
8268
kwargs: Dict[str, Any],
8369
) -> None:
84-
super().__init__()
8570
self._function = function
8671
self._args = args
8772
self._kwargs = kwargs
@@ -94,6 +79,6 @@ def __repr__(self) -> str:
9479
args = sig.bind(*self._args, **self._kwargs).arguments
9580
items = ", ".join(f"{k}={v!r}" for k, v in args.items())
9681
if items:
97-
return f"{self._function.__name__}({self.id}, {items})"
82+
return f"{self._function.__name__}:{id(self)}({items})"
9883
else:
99-
return f"{self._function.__name__}({self.id})"
84+
return f"{self._function.__name__}:{id(self)}()"

idom/core/layout.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ class LayoutEvent(NamedTuple):
4040
class ElementState(NamedTuple):
4141
model: Dict[str, Any]
4242
path: str
43+
element_id: int
4344
element_obj: AbstractElement
4445
event_handler_ids: Set[str]
45-
child_elements_ids: List[str]
46+
child_elements_ids: List[int]
4647
life_cycle_hook: LifeCycleHook
4748

4849

@@ -80,7 +81,7 @@ async def dispatch(self, event: LayoutEvent) -> None:
8081
async def render(self) -> LayoutUpdate:
8182
while True:
8283
element = await self._rendering_queue.get()
83-
if element.id in self._element_states:
84+
if self._has_element_state(element):
8485
return self._create_layout_update(element)
8586

8687
@async_resource
@@ -90,15 +91,15 @@ async def _rendering_queue(self) -> AsyncIterator["_ElementQueue"]:
9091
yield queue
9192

9293
@async_resource
93-
async def _element_states(self) -> AsyncIterator[Dict[str, ElementState]]:
94-
root_element_state = self._create_element_state(self.root, "")
94+
async def _element_states(self) -> AsyncIterator[Dict[int, ElementState]]:
95+
root_element_state = self._create_element_state(self.root, "", save=False)
9596
try:
96-
yield {self.root.id: root_element_state}
97+
yield {root_element_state.element_id: root_element_state}
9798
finally:
9899
self._delete_element_state(root_element_state)
99100

100101
def _create_layout_update(self, element: AbstractElement) -> LayoutUpdate:
101-
element_state = self._element_states[element.id]
102+
element_state = self._get_element_state(element)
102103

103104
element_state.life_cycle_hook.element_will_render()
104105

@@ -169,10 +170,9 @@ def _render_model_children(
169170
resolved_children.append(self._render_model(element_state, child))
170171
elif isinstance(child, AbstractElement):
171172
child_path = f"{element_state.path}/children/{index}"
172-
child_state = self._create_element_state(child, child_path)
173-
self._element_states[child.id] = child_state
173+
child_state = self._create_element_state(child, child_path, save=True)
174174
resolved_children.append(self._render_element(child_state))
175-
element_state.child_elements_ids.append(child.id)
175+
element_state.child_elements_ids.append(id(child))
176176
else:
177177
resolved_children.append(str(child))
178178
return resolved_children
@@ -201,22 +201,36 @@ def _render_model_event_targets(
201201

202202
return {e: h.serialize() for e, h in handlers.items()}
203203

204+
def _get_element_state(self, element: AbstractElement) -> ElementState:
205+
return self._element_states[id(element)]
206+
207+
def _has_element_state(self, element: AbstractElement) -> bool:
208+
return id(element) in self._element_states
209+
204210
def _create_element_state(
205-
self, element: AbstractElement, path: str
211+
self,
212+
element: AbstractElement,
213+
path: str,
214+
save: bool,
206215
) -> ElementState:
207-
return ElementState(
216+
element_id = id(element)
217+
state = ElementState(
208218
model={},
209219
path=path,
220+
element_id=element_id,
210221
element_obj=element,
211222
event_handler_ids=set(),
212223
child_elements_ids=[],
213224
life_cycle_hook=LifeCycleHook(element, self.update),
214225
)
226+
if save:
227+
self._element_states[element_id] = state
228+
return state
215229

216230
def _delete_element_state(self, element_state: ElementState) -> None:
217231
self._clear_element_state_event_handlers(element_state)
218232
self._delete_element_state_children(element_state)
219-
del self._element_states[element_state.element_obj.id]
233+
del self._element_states[element_state.element_id]
220234

221235
def _clear_element_state_event_handlers(self, element_state: ElementState) -> None:
222236
for handler_id in element_state.event_handler_ids:
@@ -258,15 +272,16 @@ class _ElementQueue:
258272

259273
def __init__(self) -> None:
260274
self._queue: "asyncio.Queue[AbstractElement]" = asyncio.Queue()
261-
self._pending: Set[str] = set()
275+
self._pending: Set[int] = set()
262276

263277
def put(self, element: AbstractElement) -> None:
264-
if element.id not in self._pending:
265-
self._pending.add(element.id)
278+
element_id = id(element)
279+
if element_id not in self._pending:
280+
self._pending.add(element_id)
266281
self._queue.put_nowait(element)
267282
return None
268283

269284
async def get(self) -> AbstractElement:
270285
element = await self._queue.get()
271-
self._pending.remove(element.id)
286+
self._pending.remove(id(element))
272287
return element

tests/test_core/test_element.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def MyElement(a, *b, **c):
88

99
m_e = MyElement(1, 2, 3, x=4, y=5)
1010

11-
expected = f"MyElement({m_e.id}, a=1, b=(2, 3), c={{'x': 4, 'y': 5}})"
11+
expected = f"MyElement:{id(m_e)}(a=1, b=(2, 3), c={{'x': 4, 'y': 5}})"
1212
assert repr(m_e) == expected
1313

1414

tests/test_core/test_layout.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def MyElement():
1717

1818
my_element = MyElement()
1919
layout = idom.Layout(my_element)
20-
assert str(layout) == f"Layout(MyElement({my_element.id}))"
20+
assert str(layout) == f"Layout(MyElement:{id(my_element)}())"
2121

2222

2323
def test_layout_expects_abstract_element():
@@ -171,8 +171,8 @@ async def test_elements_are_garbage_collected():
171171
@outer_element_hook.capture
172172
def Outer():
173173
element = idom.hooks.current_hook().element
174-
live_elements.add(element.id)
175-
finalize(element, live_elements.remove, element.id)
174+
live_elements.add(id(element))
175+
finalize(element, live_elements.remove, id(element))
176176

177177
hook = idom.hooks.current_hook()
178178

@@ -185,8 +185,8 @@ async def force_update():
185185
@idom.element
186186
def Inner():
187187
element = idom.hooks.current_hook().element
188-
live_elements.add(element.id)
189-
finalize(element, live_elements.remove, element.id)
188+
live_elements.add(id(element))
189+
finalize(element, live_elements.remove, id(element))
190190
return idom.html.div()
191191

192192
async with idom.Layout(Outer()) as layout:

0 commit comments

Comments
 (0)