mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Exception on duplicate row and column keys
This commit is contained in:
@@ -45,6 +45,11 @@ class CellDoesNotExist(Exception):
|
|||||||
do not exist in the DataTable."""
|
do not exist in the DataTable."""
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateKey(Exception):
|
||||||
|
"""Raised when the RowKey or ColumnKey provided already refers to
|
||||||
|
an existing row or column in the DataTable. Keys must be unique."""
|
||||||
|
|
||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
class StringKey:
|
class StringKey:
|
||||||
"""An object used as a key in a mapping. It can optionally wrap a string,
|
"""An object used as a key in a mapping. It can optionally wrap a string,
|
||||||
@@ -456,7 +461,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
"""Cached y_offset - key is update_count - see y_offsets property for more
|
"""Cached y_offset - key is update_count - see y_offsets property for more
|
||||||
information """
|
information """
|
||||||
self._ordered_row_cache: LRUCache[tuple[int, int], list[Row]] = LRUCache(1)
|
self._ordered_row_cache: LRUCache[tuple[int, int], list[Row]] = LRUCache(1)
|
||||||
"""Caches row ordering - key is update_count."""
|
"""Caches row ordering - key is (num_rows, update_count)."""
|
||||||
|
|
||||||
self._require_update_dimensions: bool = False
|
self._require_update_dimensions: bool = False
|
||||||
"""Set to re-calculate dimensions on idle."""
|
"""Set to re-calculate dimensions on idle."""
|
||||||
@@ -917,6 +922,8 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
after being added due to sorting/insertion/deletion of other columns).
|
after being added due to sorting/insertion/deletion of other columns).
|
||||||
"""
|
"""
|
||||||
column_key = ColumnKey(key)
|
column_key = ColumnKey(key)
|
||||||
|
if column_key in self._column_locations:
|
||||||
|
raise DuplicateKey(f"The column key {key!r} already exists.")
|
||||||
column_index = len(self.columns)
|
column_index = len(self.columns)
|
||||||
label = Text.from_markup(label) if isinstance(label, str) else label
|
label = Text.from_markup(label) if isinstance(label, str) else label
|
||||||
content_width = measure(self.app.console, label, 1)
|
content_width = measure(self.app.console, label, 1)
|
||||||
@@ -959,13 +966,15 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
|||||||
of its current location in the DataTable (it could have moved after
|
of its current location in the DataTable (it could have moved after
|
||||||
being added due to sorting or insertion/deletion of other rows).
|
being added due to sorting or insertion/deletion of other rows).
|
||||||
"""
|
"""
|
||||||
row_index = self.row_count
|
|
||||||
row_key = RowKey(key)
|
row_key = RowKey(key)
|
||||||
|
if row_key in self._row_locations:
|
||||||
|
raise DuplicateKey(f"The row key {row_key!r} already exists.")
|
||||||
|
|
||||||
# TODO: If there are no columns: do we generate them here?
|
# TODO: If there are no columns: do we generate them here?
|
||||||
# If we don't do this, users will be required to call add_column(s)
|
# If we don't do this, users will be required to call add_column(s)
|
||||||
# Before they call add_row.
|
# Before they call add_row.
|
||||||
|
|
||||||
|
row_index = self.row_count
|
||||||
# Map the key of this row to its current index
|
# Map the key of this row to its current index
|
||||||
self._row_locations[row_key] = row_index
|
self._row_locations[row_key] = row_index
|
||||||
self.data[row_key] = {
|
self.data[row_key] = {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
"""Make non-widget DataTable support classes available."""
|
"""Make non-widget DataTable support classes available."""
|
||||||
|
|
||||||
from ._data_table import (
|
from ._data_table import (
|
||||||
|
CellDoesNotExist,
|
||||||
|
CellKey,
|
||||||
|
CellType,
|
||||||
Column,
|
Column,
|
||||||
|
ColumnKey,
|
||||||
|
CursorType,
|
||||||
|
DuplicateKey,
|
||||||
Row,
|
Row,
|
||||||
RowKey,
|
RowKey,
|
||||||
ColumnKey,
|
|
||||||
CellKey,
|
|
||||||
CursorType,
|
|
||||||
CellType,
|
|
||||||
CellDoesNotExist,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -20,4 +21,5 @@ __all__ = [
|
|||||||
"CursorType",
|
"CursorType",
|
||||||
"CellType",
|
"CellType",
|
||||||
"CellDoesNotExist",
|
"CellDoesNotExist",
|
||||||
|
"DuplicateKey",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from textual.events import Click, MouseMove
|
|||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
from textual.message_pump import MessagePump
|
from textual.message_pump import MessagePump
|
||||||
from textual.widgets import DataTable
|
from textual.widgets import DataTable
|
||||||
|
from textual.widgets._data_table import DuplicateKey
|
||||||
from textual.widgets.data_table import CellDoesNotExist, CellKey, ColumnKey, Row, RowKey
|
from textual.widgets.data_table import CellDoesNotExist, CellKey, ColumnKey, Row, RowKey
|
||||||
|
|
||||||
ROWS = [["0/0", "0/1"], ["1/0", "1/1"], ["2/0", "2/1"]]
|
ROWS = [["0/0", "0/1"], ["1/0", "1/1"], ["2/0", "2/1"]]
|
||||||
@@ -192,6 +193,25 @@ async def test_add_rows_user_defined_keys():
|
|||||||
assert table.rows["algernon"] == first_row
|
assert table.rows["algernon"] == first_row
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_row_duplicate_key():
|
||||||
|
app = DataTableApp()
|
||||||
|
async with app.run_test():
|
||||||
|
table = app.query_one(DataTable)
|
||||||
|
table.add_column("A")
|
||||||
|
table.add_row("1", key="1")
|
||||||
|
with pytest.raises(DuplicateKey):
|
||||||
|
table.add_row("2", key="1") # Duplicate row key
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_column_duplicate_key():
|
||||||
|
app = DataTableApp()
|
||||||
|
async with app.run_test():
|
||||||
|
table = app.query_one(DataTable)
|
||||||
|
table.add_column("A", key="A")
|
||||||
|
with pytest.raises(DuplicateKey):
|
||||||
|
table.add_column("B", key="A") # Duplicate column key
|
||||||
|
|
||||||
|
|
||||||
async def test_add_column_with_width():
|
async def test_add_column_with_width():
|
||||||
app = DataTableApp()
|
app = DataTableApp()
|
||||||
async with app.run_test():
|
async with app.run_test():
|
||||||
|
|||||||
Reference in New Issue
Block a user