From 8daf832c4cbe014582557083b82cd73aed0053c6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 24 May 2022 10:26:20 +0100 Subject: [PATCH] more general app --- sandbox/basic.py | 4 ++-- sandbox/nest.py | 8 +------- src/textual/app.py | 9 +++++++-- src/textual/cli/cli.py | 42 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/sandbox/basic.py b/sandbox/basic.py index 8dfc6a162..a52c05aa5 100644 --- a/sandbox/basic.py +++ b/sandbox/basic.py @@ -91,7 +91,7 @@ class Success(Widget): return Text("This is a success message", justify="center") -class BasicApp(App): +class BasicApp(App, css_path="basic.css"): """A basic app demonstrating CSS""" def on_load(self): @@ -158,7 +158,7 @@ class BasicApp(App): tweet_body.refresh(layout=True) -app = BasicApp(css_path="basic.css") +app = BasicApp() if __name__ == "__main__": app.run() diff --git a/sandbox/nest.py b/sandbox/nest.py index 263d7c2da..a02c7fa93 100644 --- a/sandbox/nest.py +++ b/sandbox/nest.py @@ -16,7 +16,7 @@ class TextWidget(Widget): return TEXT -class AutoApp(App): +class AutoApp(App, css_path="nest.css"): def on_mount(self) -> None: self.bind("t", "tree") @@ -30,9 +30,3 @@ class AutoApp(App): def action_tree(self): self.log(self.screen.tree) - - -app = AutoApp(css_path="nest.css") - -if __name__ == "__main__": - app.run() diff --git a/src/textual/app.py b/src/textual/app.py index 20f448258..83e260ccb 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -109,6 +109,8 @@ class App(Generic[ReturnType], DOMNode): """ + CSS_PATH: str | None = None + def __init__( self, driver_class: Type[Driver] | None = None, @@ -188,8 +190,7 @@ class App(Generic[ReturnType], DOMNode): self.stylesheet = Stylesheet(variables=self.get_css_variables()) self._require_styles_update = False - - self.css_path = css_path + self.css_path = css_path or self.CSS_PATH self.registry: set[MessagePump] = set() self.devtools = DevtoolsClient() @@ -203,6 +204,10 @@ class App(Generic[ReturnType], DOMNode): super().__init__() + def __init_subclass__(cls, css_path: str | None = None) -> None: + super().__init_subclass__() + cls.CSS_PATH = css_path + title: Reactive[str] = Reactive("Textual") sub_title: Reactive[str] = Reactive("") background: Reactive[str] = Reactive("black") diff --git a/src/textual/cli/cli.py b/src/textual/cli/cli.py index 33c3c68c6..66a7dd177 100644 --- a/src/textual/cli/cli.py +++ b/src/textual/cli/cli.py @@ -47,7 +47,6 @@ def import_app(import_name: str) -> App: from textual.app import App lib, _colon, name = import_name.partition(":") - name = name or "app" if lib.endswith(".py"): # We're assuming the user wants to load a .py file @@ -60,10 +59,41 @@ def import_app(import_name: str) -> App: global_vars: dict[str, object] = {} exec(py_code, global_vars) - try: - app = global_vars[name] - except KeyError: - raise AppFail(f"App {name!r} not found in {lib!r}") + if name: + # User has give a name, use that + try: + app = global_vars[name] + except KeyError: + raise AppFail(f"App {name!r} not found in {lib!r}") + else: + # User has not given a name + if "app" in global_vars: + # App exists, lets use that + try: + app = global_vars[name] + except KeyError: + raise AppFail(f"App {name!r} not found in {lib!r}") + else: + # Find a App class or instance that is *not* the base class + apps = [ + value + for key, value in global_vars.items() + if ( + isinstance(value, App) + or (inspect.isclass(value) and issubclass(value, App)) + and value is not App + ) + ] + if not apps: + raise AppFail( + f'Unable to find app in {lib!r}, try specifying app with "foo.py:app"' + ) + if len(apps) > 1: + raise AppFail( + f'Multiple apps found {lib!r}, try specifying app with "foo.py:app"' + ) + app = apps[0] + else: # Assuming the user wants to import the file sys.path.append("") @@ -78,7 +108,7 @@ def import_app(import_name: str) -> App: raise AppFail(f"Unable to find {name!r} in {module!r}") if inspect.isclass(app) and issubclass(app, App): - app = App() + app = app() return cast(App, app)