From 382b3083932a0f9068d0327899b98a6209d30184 Mon Sep 17 00:00:00 2001 From: Moritz Neeb Date: Sat, 22 Jul 2023 11:48:48 +0200 Subject: [PATCH 1/3] allow empty css variables fixes #1849 Co-authored-by: @eliasdorneles --- src/textual/css/parse.py | 2 ++ tests/css/test_parse.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py index 13aeea488..a604fed01 100644 --- a/src/textual/css/parse.py +++ b/src/textual/css/parse.py @@ -287,6 +287,8 @@ def substitute_references( variables.setdefault(variable_name, []).append(token) yield token elif token.name == "variable_value_end": + # in case of an empty variable, avoid not referenced error + variables.setdefault(variable_name, []) yield token break # For variables referring to other variables diff --git a/tests/css/test_parse.py b/tests/css/test_parse.py index fea7b3dad..cb6e6aad0 100644 --- a/tests/css/test_parse.py +++ b/tests/css/test_parse.py @@ -220,6 +220,22 @@ class TestVariableReferenceSubstitution: with pytest.raises(UnresolvedVariableError): list(substitute_references(tokenize(css, ""))) + def test_empty_variable(self): + css = "$x:\n* { background:$x; }" + result = list(substitute_references(tokenize(css, ""))) + assert [(t.name, t.value) for t in result] == [ + ("variable_name", "$x:"), + ("variable_value_end", "\n"), + ("selector_start_universal", "*"), + ("whitespace", " "), + ("declaration_set_start", "{"), + ("whitespace", " "), + ("declaration_name", "background:"), + ("declaration_end", ";"), + ("whitespace", " "), + ("declaration_set_end", "}"), + ] + def test_transitive_reference(self): css = "$x: 1\n$y: $x\n.thing { border: $y }" assert list(substitute_references(tokenize(css, ""))) == [ From 2ab6ca364f3401aacd340fb7409e88954745d291 Mon Sep 17 00:00:00 2001 From: Moritz Neeb Date: Sat, 22 Jul 2023 11:54:21 +0200 Subject: [PATCH 2/3] explain change on changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9a3b79b..ec1c24b51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash when a `SelectionList` had a prompt wider than itself https://github.com/Textualize/textual/issues/2900 - Fixed a bug where `Click` events were bubbling up from `Switch` widgets https://github.com/Textualize/textual/issues/2366 +- Fixed a crash when using empty CSS variables https://github.com/Textualize/textual/issues/1849 ## [0.30.0] - 2023-07-17 From 7770f8de82b2fc20a31e97f00ef45ce6e3cab682 Mon Sep 17 00:00:00 2001 From: Moritz Neeb Date: Sat, 22 Jul 2023 12:24:03 +0200 Subject: [PATCH 3/3] refactor: simplify css variable table creation --- src/textual/css/parse.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py index a604fed01..1b31e8b66 100644 --- a/src/textual/css/parse.py +++ b/src/textual/css/parse.py @@ -269,6 +269,7 @@ def substitute_references( break if token.name == "variable_name": variable_name = token.value[1:-1] # Trim the $ and the :, i.e. "$x:" -> "x" + variable_tokens = variables.setdefault(variable_name, []) yield token while True: @@ -284,18 +285,15 @@ def substitute_references( if not token: break elif token.name == "whitespace": - variables.setdefault(variable_name, []).append(token) + variable_tokens.append(token) yield token elif token.name == "variable_value_end": - # in case of an empty variable, avoid not referenced error - variables.setdefault(variable_name, []) yield token break # For variables referring to other variables elif token.name == "variable_ref": ref_name = token.value[1:] if ref_name in variables: - variable_tokens = variables.setdefault(variable_name, []) reference_tokens = variables[ref_name] variable_tokens.extend(reference_tokens) ref_location = token.location @@ -309,7 +307,7 @@ def substitute_references( else: _unresolved(ref_name, variables.keys(), token) else: - variables.setdefault(variable_name, []).append(token) + variable_tokens.append(token) yield token token = next(iter_tokens, None) elif token.name == "variable_ref":