From d04c66291b3a1e90226b8717f35a83b57bf751b3 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Fri, 21 Jan 2022 14:14:40 +0000 Subject: [PATCH] Improve error message when layout doesnt exist, add tests for parsing layout from CSS --- src/textual/css/_styles_builder.py | 25 +++++++++++++++++-------- src/textual/layouts/factory.py | 2 +- tests/test_css_parse.py | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index ef3a9dba7..8a826507a 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -6,19 +6,19 @@ import rich.repr from rich.color import Color from rich.style import Style +from ._error_tools import friendly_list from .constants import VALID_BORDER, VALID_EDGE, VALID_DISPLAY, VALID_VISIBILITY from .errors import DeclarationError -from ._error_tools import friendly_list -from .._duration import _duration_as_seconds -from .._easing import EASING -from ..geometry import Spacing, SpacingDimensions from .model import Declaration from .scalar import Scalar, ScalarOffset, Unit, ScalarError from .styles import DockGroup, Styles -from .types import Edge, Display, Visibility from .tokenize import Token from .transition import Transition -from ..layouts.factory import get_layout +from .types import Edge, Display, Visibility +from .._duration import _duration_as_seconds +from .._easing import EASING +from ..geometry import Spacing, SpacingDimensions +from ..layouts.factory import get_layout, LayoutName, MissingLayout, LAYOUT_MAP class StylesBuilder: @@ -60,7 +60,7 @@ class StylesBuilder: self.styles.important.add(rule_name) try: process_method(declaration.name, tokens, important) - except DeclarationError as error: + except DeclarationError: raise except Exception as error: self.error(declaration.name, declaration.token, str(error)) @@ -289,7 +289,16 @@ class StylesBuilder: if len(tokens) != 1: self.error(name, tokens[0], "unexpected tokens in declaration") else: - self.styles._rule_layout = get_layout(tokens[0].value) + value = tokens[0].value + layout_name = cast(LayoutName, value) + try: + self.styles._rule_layout = get_layout(layout_name) + except MissingLayout: + self.error( + name, + tokens[0], + f"invalid value for layout (received {value!r}, expected {friendly_list(LAYOUT_MAP.keys())})", + ) def process_text(self, name: str, tokens: list[Token], important: bool) -> None: style_definition = " ".join(token.value for token in tokens) diff --git a/src/textual/layouts/factory.py b/src/textual/layouts/factory.py index 5d74a5f9f..03776cdee 100644 --- a/src/textual/layouts/factory.py +++ b/src/textual/layouts/factory.py @@ -28,5 +28,5 @@ def get_layout(name: LayoutName) -> Layout: layout_class = LAYOUT_MAP.get(name) if layout_class is None: - raise MissingLayout("no layout called {name!r}") + raise MissingLayout(f"no layout called {name!r}, valid layouts") return layout_class() diff --git a/tests/test_css_parse.py b/tests/test_css_parse.py index f17a0c3e9..ad03fb34d 100644 --- a/tests/test_css_parse.py +++ b/tests/test_css_parse.py @@ -4,6 +4,27 @@ from rich.color import Color, ColorType from textual.css.scalar import Scalar, Unit from textual.css.stylesheet import Stylesheet, StylesheetParseError from textual.css.transition import Transition +from textual.layouts.dock import DockLayout + + +class TestParseLayout: + def test_valid_layout_name(self): + css = "#some-widget { layout: dock; }" + + stylesheet = Stylesheet() + stylesheet.parse(css) + + styles = stylesheet.rules[0].styles + assert isinstance(styles.layout, DockLayout) + + def test_invalid_layout_name(self): + css = "#some-widget { layout: invalidlayout; }" + + stylesheet = Stylesheet() + with pytest.raises(StylesheetParseError) as ex: + stylesheet.parse(css) + + assert ex.value.errors is not None class TestParseText: