diff --git a/examples/calculator.py b/examples/calculator.py index 60d982f96..e42273fe0 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -28,7 +28,8 @@ class CalculatorApp(App): "plus_minus_sign": "plus-minus", "percent_sign": "percent", "equals_sign": "equals", - "enter": "equals", + "minus": "minus", + "plus": "plus", } def watch_numbers(self, value: str) -> None: @@ -80,7 +81,6 @@ class CalculatorApp(App): self.query_one(f"#{button_id}", Button).press() except NoMatches: pass - self.set_focus(None) key = event.key if key.isdecimal(): @@ -89,7 +89,9 @@ class CalculatorApp(App): press("c") press("ac") else: - press(self.NAME_MAP.get(key, key)) + button_id = self.NAME_MAP.get(key) + if button_id is not None: + press(self.NAME_MAP.get(key, key)) def on_button_pressed(self, event: Button.Pressed) -> None: """Called when a button is pressed.""" diff --git a/src/textual/css/query.py b/src/textual/css/query.py index 93addb6fb..e0d588204 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -20,7 +20,7 @@ from typing import cast, Generic, TYPE_CHECKING, Iterator, TypeVar, overload import rich.repr -from .errors import DeclarationError +from .errors import DeclarationError, TokenError from .match import match from .model import SelectorSet from .parse import parse_declarations, parse_selectors @@ -34,6 +34,10 @@ class QueryError(Exception): """Base class for a query related error.""" +class InvalidQueryFormat(QueryError): + """Query did not parse correctly.""" + + class NoMatches(QueryError): """No nodes matched the query.""" @@ -72,9 +76,17 @@ class DOMQuery(Generic[QueryType]): parent._excludes.copy() if parent else [] ) if filter is not None: - self._filters.append(parse_selectors(filter)) + try: + self._filters.append(parse_selectors(filter)) + except TokenError: + # TODO: More helpful errors + raise InvalidQueryFormat(f"Unable to parse filter {filter!r} as query") + if exclude is not None: - self._excludes.append(parse_selectors(exclude)) + try: + self._excludes.append(parse_selectors(exclude)) + except TokenError: + raise InvalidQueryFormat(f"Unable to parse filter {filter!r} as query") @property def node(self) -> DOMNode: diff --git a/tests/test_query.py b/tests/test_query.py index be6108046..373d8d0ee 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -1,4 +1,7 @@ +import pytest + from textual.widget import Widget +from textual.css.query import InvalidQueryFormat def test_query(): @@ -78,3 +81,16 @@ def test_query(): assert list(app.query("#widget1, #widget2")) == [widget1, widget2] assert list(app.query("#widget1 , #widget2")) == [widget1, widget2] assert list(app.query("#widget1, #widget2, App")) == [app, widget1, widget2] + + +def test_invalid_query(): + class App(Widget): + pass + + app = App() + + with pytest.raises(InvalidQueryFormat): + app.query("#3") + + with pytest.raises(InvalidQueryFormat): + app.query("#foo").exclude("#2")