From b804b8e85195a929285471b7368f712062cebd15 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 22 Jul 2024 12:48:47 +0100 Subject: [PATCH 1/2] simplify and optimize --- src/textual/css/query.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/textual/css/query.py b/src/textual/css/query.py index 23233f8d2c..8694ee2d3d 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -279,21 +279,9 @@ def only_one( the_one: ExpectType | QueryType = ( self.first(expect_type) if expect_type is not None else self.first() ) - try: - # Now see if we can access a subsequent item in the nodes. There - # should *not* be anything there, so we *should* get an - # IndexError. We *could* have just checked the length of the - # query, but the idea here is to do the check as cheaply as - # possible. "There can be only one!" -- Kurgan et al. - _ = self.nodes[1] - raise TooManyMatches( - "Call to only_one resulted in more than one matched node" - ) - except IndexError: - # The IndexError was got, that's a good thing in this case. So - # we return what we found. - pass - return the_one + if len(self.nodes) == 1: + return the_one + raise TooManyMatches("Call to only_one resulted in more than one matched node") if TYPE_CHECKING: From 144f4160a5521e59bcdfffcf3c2b8558963a6323 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 22 Jul 2024 14:42:58 +0100 Subject: [PATCH 2/2] wip --- src/textual/app.py | 6 ++++++ src/textual/config.py | 50 +++++++++++++++++++++++++++++++++++++++++++ src/textual/widget.py | 2 +- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/textual/config.py diff --git a/src/textual/app.py b/src/textual/app.py index 9f38dcb3af..28507e685c 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -278,6 +278,12 @@ def fileno(self) -> int: class App(Generic[ReturnType], DOMNode): """The base class for Textual Applications.""" + CONFIG_PATH: str | None = None + """Path to app config, or `None` for no app config.""" + + APP_ID: str | None = None + """A unique identifier used in the config system, or `None` to use the name of the App sub-class.""" + CSS: ClassVar[str] = "" """Inline CSS, useful for quick scripts. This is loaded after CSS_PATH, and therefore takes priority in the event of a specificity clash.""" diff --git a/src/textual/config.py b/src/textual/config.py new file mode 100644 index 0000000000..4b1b007211 --- /dev/null +++ b/src/textual/config.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import tomllib +from os import PathLike + + +class Config: + def __init__(self, *paths: PathLike) -> None: + self.paths = paths + self._data: dict | None = None + self._attempted_read = False + + def _read(self) -> dict: + configs: list[dict] = [] + for path in self.paths: + try: + with open(path, "rb") as config_file: + configs.append(tomllib.load(config_file)) + except IOError: + pass + config = configs[0] + for overlay_config in configs[1:]: + if isinstance(overlay_config, dict): + for key, value in overlay_config.items(): + config[key] = value + return config + + @property + def data(self) -> dict: + if not self._attempted_read: + self._attempted_read = True + self._data = self._read() + if self._data is None: + return {} + return self._data + + def get(self, *keys: str, default: object = None) -> object: + data = self.data + for key in keys: + if key not in data: + return default + data = data[key] + return data + + +if __name__ == "__main__": + config = Config("config.toml") + from rich import print + + print(config.data) diff --git a/src/textual/widget.py b/src/textual/widget.py index 8c4f1598d5..73bdcef143 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -834,7 +834,7 @@ def render_str(self, text_content: str | Text) -> Text: A text object. """ text = ( - Text.from_markup(text_content) + Text.from_markup(text_content, end="") if isinstance(text_content, str) else text_content )