From 457340777bff4f4865d89c7739c7057ca5b79cfd Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 31 Oct 2022 15:25:22 +0000 Subject: [PATCH] Change DOM queries to be case-sensitive for classes and IDs See #1041 and #1047. --- CHANGELOG.md | 1 + src/textual/css/model.py | 8 +++----- src/textual/dom.py | 4 ++-- tests/test_query.py | 2 ++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 876bcb4310..a153fac781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Widgets are now closed in reversed DOM order - Input widget justify hardcoded to left to prevent text-align interference - Changed `textual run` so that it patches `argv` in more situations +- DOM classes and IDs are now always treated fully case-sensitive https://github.com/Textualize/textual/issues/1047 ### Added diff --git a/src/textual/css/model.py b/src/textual/css/model.py index c5efebc153..4cfa24a0c2 100644 --- a/src/textual/css/model.py +++ b/src/textual/css/model.py @@ -44,7 +44,6 @@ class Selector: type: SelectorType = SelectorType.TYPE pseudo_classes: list[str] = field(default_factory=list) specificity: Specificity3 = field(default_factory=lambda: (0, 0, 0)) - _name_lower: str = field(default="", repr=False) advance: int = 1 @property @@ -61,7 +60,6 @@ def css(self) -> str: return f"#{self.name}{pseudo_suffix}" def __post_init__(self) -> None: - self._name_lower = self.name.lower() self._checks = { SelectorType.UNIVERSAL: self._check_universal, SelectorType.TYPE: self._check_type, @@ -94,21 +92,21 @@ def _check_universal(self, node: DOMNode) -> bool: return node.has_pseudo_class(*self.pseudo_classes) def _check_type(self, node: DOMNode) -> bool: - if self._name_lower not in node._css_type_names: + if self.name not in node._css_type_names: return False if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes): return False return True def _check_class(self, node: DOMNode) -> bool: - if not node.has_class(self._name_lower): + if not node.has_class(self.name): return False if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes): return False return True def _check_id(self, node: DOMNode) -> bool: - if not node.id == self._name_lower: + if not node.id == self.name: return False if self.pseudo_classes and not node.has_pseudo_class(*self.pseudo_classes): return False diff --git a/src/textual/dom.py b/src/textual/dom.py index 1a35b07176..89cd3b95b3 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -108,7 +108,7 @@ class DOMNode(MessagePump): # True if this node inherits the CSS from the base class. _inherit_css: ClassVar[bool] = True - # List of names of base class (lower cased) that inherit CSS + # List of names of base classes that inherit CSS _css_type_names: ClassVar[frozenset[str]] = frozenset() def __init__( @@ -168,7 +168,7 @@ def __init_subclass__(cls, inherit_css: bool = True) -> None: cls._inherit_css = inherit_css css_type_names: set[str] = set() for base in cls._css_bases(cls): - css_type_names.add(base.__name__.lower()) + css_type_names.add(base.__name__) cls._css_type_names = frozenset(css_type_names) def get_component_styles(self, name: str) -> RenderStyles: diff --git a/tests/test_query.py b/tests/test_query.py index 13d5bc5b4e..c48587ea19 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -54,7 +54,9 @@ class App(Widget): assert list(app.query("#main")) == [main_view] assert list(app.query("View#main")) == [main_view] assert list(app.query("#widget1")) == [widget1] + assert list(app.query("#Widget1")) == [] # Note case. assert list(app.query("#widget2")) == [widget2] + assert list(app.query("#Widget2")) == [] # Note case. assert list(app.query("Widget.float")) == [sidebar, tooltip, helpbar] assert list(app.query(Widget).filter(".float")) == [sidebar, tooltip, helpbar]