Merge pull request #985 from Textualize/query-parse

Add sensible error to query
This commit is contained in:
Will McGugan
2022-10-25 11:28:37 +01:00
committed by GitHub
4 changed files with 42 additions and 6 deletions

View File

@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.2] - Unreleased
### Changed
- DOMQuery now raises InvalidQueryFormat in response to invalid query strings, rather than cryptic CSS error
## [0.2.1] - 2022-10-23 ## [0.2.1] - 2022-10-23
### Changed ### Changed

View File

@@ -28,7 +28,8 @@ class CalculatorApp(App):
"plus_minus_sign": "plus-minus", "plus_minus_sign": "plus-minus",
"percent_sign": "percent", "percent_sign": "percent",
"equals_sign": "equals", "equals_sign": "equals",
"enter": "equals", "minus": "minus",
"plus": "plus",
} }
def watch_numbers(self, value: str) -> None: def watch_numbers(self, value: str) -> None:
@@ -80,7 +81,6 @@ class CalculatorApp(App):
self.query_one(f"#{button_id}", Button).press() self.query_one(f"#{button_id}", Button).press()
except NoMatches: except NoMatches:
pass pass
self.set_focus(None)
key = event.key key = event.key
if key.isdecimal(): if key.isdecimal():
@@ -89,6 +89,8 @@ class CalculatorApp(App):
press("c") press("c")
press("ac") press("ac")
else: else:
button_id = self.NAME_MAP.get(key)
if button_id is not None:
press(self.NAME_MAP.get(key, key)) press(self.NAME_MAP.get(key, key))
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:

View File

@@ -20,7 +20,7 @@ from typing import cast, Generic, TYPE_CHECKING, Iterator, TypeVar, overload
import rich.repr import rich.repr
from .errors import DeclarationError from .errors import DeclarationError, TokenError
from .match import match from .match import match
from .model import SelectorSet from .model import SelectorSet
from .parse import parse_declarations, parse_selectors from .parse import parse_declarations, parse_selectors
@@ -34,6 +34,10 @@ class QueryError(Exception):
"""Base class for a query related error.""" """Base class for a query related error."""
class InvalidQueryFormat(QueryError):
"""Query did not parse correctly."""
class NoMatches(QueryError): class NoMatches(QueryError):
"""No nodes matched the query.""" """No nodes matched the query."""
@@ -72,9 +76,17 @@ class DOMQuery(Generic[QueryType]):
parent._excludes.copy() if parent else [] parent._excludes.copy() if parent else []
) )
if filter is not None: if filter is not None:
try:
self._filters.append(parse_selectors(filter)) 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: if exclude is not None:
try:
self._excludes.append(parse_selectors(exclude)) self._excludes.append(parse_selectors(exclude))
except TokenError:
raise InvalidQueryFormat(f"Unable to parse filter {filter!r} as query")
@property @property
def node(self) -> DOMNode: def node(self) -> DOMNode:

View File

@@ -1,4 +1,7 @@
import pytest
from textual.widget import Widget from textual.widget import Widget
from textual.css.query import InvalidQueryFormat
def test_query(): 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")) == [widget1, widget2] assert list(app.query("#widget1 , #widget2")) == [widget1, widget2]
assert list(app.query("#widget1, #widget2, App")) == [app, 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")