Skip to content

Commit ed395e3

Browse files
authored
Merge pull request #985 from Textualize/query-parse
Add sensible error to query
2 parents 2a3eed1 + 745a6f3 commit ed395e3

File tree

4 files changed

+42
-6
lines changed

4 files changed

+42
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## [0.2.2] - Unreleased
9+
10+
### Changed
11+
12+
- DOMQuery now raises InvalidQueryFormat in response to invalid query strings, rather than cryptic CSS error
13+
814
## [0.2.1] - 2022-10-23
915

1016
### Changed

examples/calculator.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class CalculatorApp(App):
2828
"plus_minus_sign": "plus-minus",
2929
"percent_sign": "percent",
3030
"equals_sign": "equals",
31-
"enter": "equals",
31+
"minus": "minus",
32+
"plus": "plus",
3233
}
3334

3435
def watch_numbers(self, value: str) -> None:
@@ -80,7 +81,6 @@ def press(button_id: str) -> None:
8081
self.query_one(f"#{button_id}", Button).press()
8182
except NoMatches:
8283
pass
83-
self.set_focus(None)
8484

8585
key = event.key
8686
if key.isdecimal():
@@ -89,7 +89,9 @@ def press(button_id: str) -> None:
8989
press("c")
9090
press("ac")
9191
else:
92-
press(self.NAME_MAP.get(key, key))
92+
button_id = self.NAME_MAP.get(key)
93+
if button_id is not None:
94+
press(self.NAME_MAP.get(key, key))
9395

9496
def on_button_pressed(self, event: Button.Pressed) -> None:
9597
"""Called when a button is pressed."""

src/textual/css/query.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import rich.repr
2222

23-
from .errors import DeclarationError
23+
from .errors import DeclarationError, TokenError
2424
from .match import match
2525
from .model import SelectorSet
2626
from .parse import parse_declarations, parse_selectors
@@ -34,6 +34,10 @@ class QueryError(Exception):
3434
"""Base class for a query related error."""
3535

3636

37+
class InvalidQueryFormat(QueryError):
38+
"""Query did not parse correctly."""
39+
40+
3741
class NoMatches(QueryError):
3842
"""No nodes matched the query."""
3943

@@ -72,9 +76,17 @@ def __init__(
7276
parent._excludes.copy() if parent else []
7377
)
7478
if filter is not None:
75-
self._filters.append(parse_selectors(filter))
79+
try:
80+
self._filters.append(parse_selectors(filter))
81+
except TokenError:
82+
# TODO: More helpful errors
83+
raise InvalidQueryFormat(f"Unable to parse filter {filter!r} as query")
84+
7685
if exclude is not None:
77-
self._excludes.append(parse_selectors(exclude))
86+
try:
87+
self._excludes.append(parse_selectors(exclude))
88+
except TokenError:
89+
raise InvalidQueryFormat(f"Unable to parse filter {filter!r} as query")
7890

7991
@property
8092
def node(self) -> DOMNode:

tests/test_query.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import pytest
2+
13
from textual.widget import Widget
4+
from textual.css.query import InvalidQueryFormat
25

36

47
def test_query():
@@ -78,3 +81,16 @@ class App(Widget):
7881
assert list(app.query("#widget1, #widget2")) == [widget1, widget2]
7982
assert list(app.query("#widget1 , #widget2")) == [widget1, widget2]
8083
assert list(app.query("#widget1, #widget2, App")) == [app, widget1, widget2]
84+
85+
86+
def test_invalid_query():
87+
class App(Widget):
88+
pass
89+
90+
app = App()
91+
92+
with pytest.raises(InvalidQueryFormat):
93+
app.query("#3")
94+
95+
with pytest.raises(InvalidQueryFormat):
96+
app.query("#foo").exclude("#2")

0 commit comments

Comments
 (0)