Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: escape cell contents if bad Rich markup; improve tooltip formatting #119

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ jobs:
- name: Run analysis
run: |
source .venv/bin/activate
black . --check
ruff .
ruff format --check
ruff check .
mypy
11 changes: 4 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
repos:
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.1.4
rev: v0.5.1
hooks:
- id: ruff-format
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
rev: v1.10.1
hooks:
- id: mypy
additional_dependencies:
- textual>=0.48.1
- textual>=0.72.0
- pytest
- pyarrow-stubs
- pandas-stubs
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

- Fixes a crash when cell contents contained bad Rich Markdown ([tconbeer/harlequin#569](https://github.com/tconbeer/harlequin/issues/569)).
- Improves the appearance of data tooltips.

## [0.7.1] - 2024-02-09

- Adds a `backend.source_data` property to exposue the underlying Arrow table, before slicing.
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.PHONY: check
check:
black .
ruff format .
pytest
ruff . --fix
ruff check . --fix
mypy

.PHONY: lint
lint:
black .
ruff . --fix
ruff format .
ruff check . --fix
mypy

.PHONY: serve
Expand Down
722 changes: 325 additions & 397 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ numpy = { version="^1", python=">=3.9,<3.13" }
pyinstrument = "^4.6.0"

[tool.poetry.group.static.dependencies]
black = "^23.3.0"
ruff = ">=0.2,<0.5"
mypy = "^1.2.0"
ruff = "^0.5"
mypy = "^1.10.0"
pandas-stubs = { version="^2.1.1", python=">=3.9,<3.13" }

[tool.poetry.group.test.dependencies]
Expand Down
30 changes: 21 additions & 9 deletions src/textual_fastdatatable/data_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from __future__ import annotations

import functools
from datetime import date, datetime, time, timedelta
from decimal import Decimal
from itertools import chain, zip_longest
from typing import Any, ClassVar, Iterable, NamedTuple, Tuple, Union

Expand Down Expand Up @@ -557,7 +559,9 @@ def __init__(
backend
if backend is not None
else create_backend(
data, max_rows=max_rows, has_header=(column_labels is None) # type: ignore
data, # type: ignore[arg-type]
max_rows=max_rows,
has_header=(column_labels is None), # type: ignore
)
)
except (TypeError, OSError) as e:
Expand All @@ -584,9 +588,9 @@ def __init__(
"""Cache for individual cells."""
self._line_cache: LRUCache[LineCacheKey, Strip] = LRUCache(1000)
"""Cache for lines within rows."""
self._tooltip_cache: LRUCache[
TooltipCacheKey, RenderableType | None
] = LRUCache(1000)
self._tooltip_cache: LRUCache[TooltipCacheKey, RenderableType | None] = (
LRUCache(1000)
)
"""Cache for values for the tooltip"""
# self._offset_cache: LRUCache[int, list[tuple[RowKey, int]]] = LRUCache(1)
"""Cached y_offset - key is update_count - see y_offsets property for more
Expand Down Expand Up @@ -2424,7 +2428,9 @@ async def move_cursor_to_mouse_down(self, event: events.MouseDown) -> None:
# TODO: support row labels.
# row = self.ordered_rows[row_index]
row_message = DataTable.RowLabelSelected(
self, row_index, label=Text() # label=row.label
self,
row_index,
label=Text(), # label=row.label
)
self.post_message(row_message)
elif self.show_cursor and self.cursor_type != "none":
Expand Down Expand Up @@ -2499,9 +2505,10 @@ def set_hover_or_cursor_from_mouse(self, event: events.MouseMove) -> None:
def _set_tooltip_from_cell_at(self, coordinate: Coordinate) -> None:
# TODO: support row labels
cache_key = (coordinate.row, coordinate.column, self._update_count)
column = self.ordered_columns[coordinate.column]
if cache_key not in self._tooltip_cache:
if coordinate.row == -1: # hover over header
raw_value = self.ordered_columns[coordinate.column].label
raw_value = column.label
else:
raw_value = self.get_cell_at(coordinate)
if raw_value is None:
Expand All @@ -2510,11 +2517,16 @@ def _set_tooltip_from_cell_at(self, coordinate: Coordinate) -> None:
if (
self.max_column_content_width is not None
and measured_width > self.max_column_content_width
) or (
measured_width > self.ordered_columns[coordinate.column].render_width
):
) or (measured_width > column.render_width):
if isinstance(raw_value, Text):
self._tooltip_cache[cache_key] = raw_value
elif isinstance(
raw_value,
(str, float, Decimal, int, datetime, time, date, timedelta),
):
self._tooltip_cache[cache_key] = cell_formatter(
raw_value, null_rep=self.null_rep, col=column
)
else:
self._tooltip_cache[cache_key] = Pretty(raw_value)
else:
Expand Down
8 changes: 7 additions & 1 deletion src/textual_fastdatatable/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from rich.align import Align
from rich.console import Console, RenderableType
from rich.errors import MarkupError
from rich.markup import escape
from rich.protocol import is_renderable
from rich.text import Text

Expand All @@ -29,7 +31,11 @@ def cell_formatter(
if obj is None:
return Align(null_rep, align="center")
elif isinstance(obj, str):
return Text.from_markup(obj)
try:
rich_text: Text | str = Text.from_markup(obj)
except MarkupError:
rich_text = escape(obj)
return rich_text
elif isinstance(obj, bool):
return Align(
f"[dim]{'✓' if obj else 'X'}[/] {obj}{' ' if obj else ''}",
Expand Down
538 changes: 269 additions & 269 deletions tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Large diffs are not rendered by default.

Loading