Skip to content

Commit 627df92

Browse files
committed
Added output index tracker
1 parent 237d4a1 commit 627df92

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import sys
2+
from typing import Dict, Set, Optional
3+
4+
class OutputIndexTracker:
5+
__slots__ = [
6+
'_last_output_index',
7+
'_display_id_to_output_index',
8+
'_cell_display_ids'
9+
]
10+
11+
def __init__(self):
12+
# Dictionaries to track indices and display IDs
13+
self._last_output_index: Dict[str, int] = {}
14+
self._display_id_to_output_index: Dict[str, int] = {}
15+
self._cell_display_ids: Dict[str, Set[str]] = {}
16+
17+
def _intern_key(self, key: str) -> str:
18+
"""
19+
Intern string keys to reduce memory usage.
20+
Only use for keys that are likely to be repeated.
21+
"""
22+
return sys.intern(key) if key else key
23+
24+
def allocate_output_index(
25+
self,
26+
cell_id: str,
27+
display_id: Optional[str] = None
28+
) -> int:
29+
# Intern keys to reduce memory
30+
cell_id = self._intern_key(cell_id)
31+
display_id = self._intern_key(display_id) if display_id else None
32+
33+
# Retrieve the last index for this cell, defaulting to -1 if not found
34+
last_index = self._last_output_index.get(cell_id, -1)
35+
36+
# If a display_id is provided, check for existing index
37+
if display_id:
38+
# If display_id already has an index, return that
39+
if display_id in self._display_id_to_output_index:
40+
return self._display_id_to_output_index[display_id]
41+
42+
# Allocate a new index for this display_id
43+
new_index = last_index + 1
44+
self._display_id_to_output_index[display_id] = new_index
45+
46+
# Track display_id for this cell
47+
if cell_id not in self._cell_display_ids:
48+
self._cell_display_ids[cell_id] = set()
49+
self._cell_display_ids[cell_id].add(display_id)
50+
else:
51+
# For non-display specific outputs, simply increment the last index
52+
new_index = last_index + 1
53+
54+
# Update the last output index for the cell
55+
self._last_output_index[cell_id] = new_index
56+
57+
return new_index
58+
59+
def get_output_index(self, display_id: str) -> Optional[int]:
60+
"""
61+
Retrieve the output index for a given display ID.
62+
63+
Args:
64+
display_id (str): The display identifier.
65+
66+
Returns:
67+
Optional[int]: The output index if found, None otherwise.
68+
"""
69+
return self._display_id_to_output_index.get(display_id)
70+
71+
def clear_cell_indices(self, cell_id: str) -> None:
72+
"""
73+
Clear indices associated with a specific cell.
74+
75+
Args:
76+
cell_id (str): The identifier of the cell to clear.
77+
"""
78+
# Remove the last output index for this cell
79+
self._last_output_index.pop(cell_id, None)
80+
81+
# Remove and clean up associated display IDs
82+
if cell_id in self._cell_display_ids:
83+
# Remove all display IDs associated with this cell from the output index mapping
84+
for display_id in self._cell_display_ids[cell_id]:
85+
self._display_id_to_output_index.pop(display_id, None)
86+
87+
# Remove the cell's display ID tracking
88+
del self._cell_display_ids[cell_id]
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import pytest
2+
import sys
3+
from jupyter_server_documents.outputs.output_index_tracker import OutputIndexTracker
4+
5+
def test_basic_output_index_allocation():
6+
"""
7+
Test basic output index allocation for a cell without display ID
8+
"""
9+
tracker = OutputIndexTracker()
10+
11+
# First output for a cell should be 0
12+
assert tracker.allocate_output_index('cell1') == 0
13+
assert tracker.allocate_output_index('cell1') == 1
14+
assert tracker.allocate_output_index('cell1') == 2
15+
16+
def test_output_index_with_display_id():
17+
"""
18+
Test output index allocation with display IDs
19+
"""
20+
tracker = OutputIndexTracker()
21+
22+
# First output for a cell with display ID
23+
assert tracker.allocate_output_index('cell1', 'display1') == 0
24+
25+
# Subsequent calls with same display ID should return the same index
26+
assert tracker.allocate_output_index('cell1', 'display1') == 0
27+
28+
# Different display ID should get a new index
29+
assert tracker.allocate_output_index('cell1', 'display2') == 1
30+
31+
def test_multiple_cells_output_indices():
32+
"""
33+
Test output index allocation across multiple cells
34+
"""
35+
tracker = OutputIndexTracker()
36+
37+
assert tracker.allocate_output_index('cell1') == 0
38+
assert tracker.allocate_output_index('cell1') == 1
39+
assert tracker.allocate_output_index('cell2') == 0
40+
assert tracker.allocate_output_index('cell2') == 1
41+
42+
def test_display_id_index_retrieval():
43+
"""
44+
Test retrieving output index for a display ID
45+
"""
46+
tracker = OutputIndexTracker()
47+
48+
tracker.allocate_output_index('cell1', 'display1')
49+
50+
assert tracker.get_output_index('display1') == 0
51+
assert tracker.get_output_index('non_existent_display') is None
52+
53+
def test_clear_cell_indices():
54+
"""
55+
Test clearing indices for a specific cell
56+
"""
57+
tracker = OutputIndexTracker()
58+
59+
# Allocate some indices
60+
tracker.allocate_output_index('cell1', 'display1')
61+
tracker.allocate_output_index('cell1', 'display2')
62+
tracker.allocate_output_index('cell2')
63+
64+
# Clear cell1 indices
65+
tracker.clear_cell_indices('cell1')
66+
67+
# Verify cell1 indices are cleared
68+
assert 'cell1' not in tracker._last_output_index
69+
assert 'display1' not in tracker._display_id_to_output_index
70+
assert 'display2' not in tracker._display_id_to_output_index
71+
72+
# Verify cell2 indices remain
73+
assert 'cell2' in tracker._last_output_index
74+
75+
def test_memory_efficiency():
76+
"""
77+
Basic test to ensure __slots__ is working
78+
"""
79+
tracker = OutputIndexTracker()
80+
81+
# Attempt to add a new attribute should raise an AttributeError
82+
with pytest.raises(AttributeError):
83+
tracker.new_attribute = "test"
84+
85+
def test_key_interning():
86+
"""
87+
Test that key interning works for repeated strings
88+
"""
89+
tracker = OutputIndexTracker()
90+
91+
# Use the same string multiple times
92+
cell_id = "repeated_cell"
93+
display_id = "repeated_display"
94+
95+
# Allocate indices multiple times
96+
for _ in range(5):
97+
index1 = tracker.allocate_output_index(cell_id, display_id)
98+
index2 = tracker.allocate_output_index(cell_id, display_id)
99+
assert index1 == index2
100+
101+
def test_cell_display_ids_tracking():
102+
"""
103+
Test tracking of display IDs for a cell
104+
"""
105+
tracker = OutputIndexTracker()
106+
107+
# Allocate multiple display IDs for a cell
108+
tracker.allocate_output_index('cell1', 'display1')
109+
tracker.allocate_output_index('cell1', 'display2')
110+
111+
# Verify display IDs are tracked
112+
assert 'cell1' in tracker._cell_display_ids
113+
assert set(tracker._cell_display_ids['cell1']) == {'display1', 'display2'}
114+
115+
# Clear cell indices
116+
tracker.clear_cell_indices('cell1')
117+
118+
# Verify display IDs are cleared
119+
assert 'cell1' not in tracker._cell_display_ids

0 commit comments

Comments
 (0)