-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
TLDR; As an app developer, I'd like to pass additional information into an App instance to allow for some more more complex configuration. This originally came up when I was working on writing a CLI that embeds different textual applications under different commands and argv became complicated. I'm thinking about the implementation as an additional parameter, like config_object, that sets an instance attribute to be used downstream.
Let's say we have this nice app, simple_app.py. We can see that it prints a message depending on what arguments are passed to the python script.
from sys import argv
from textual.app import App, ComposeResult
from textual.widgets import Static
class SimplestApp(App):
default_response = "Hey, I'm a default response since nothing was passed"
def compose(self) -> ComposeResult:
response = self.default_response if len(argv) == 1 else " ".join(argv[1:])
yield Static(response)
if __name__ == "__main__":
app = SimplestApp()
app.run()$ python simple_app.py
>> TUI["Hey, I'm a default response since nothing was passed"]$ python simple_app.py Print Something Else
>> TUI["Print Something Else"]But now, I want to import this app into another file and nest it under a command line application, command_line.py:
from typing import Tuple
import click
from simple_app import SimplestApp
@click.group()
def cli():
pass
@cli.command("textual-app")
def textual_app(args: Tuple[str]):
SimplestApp().run()
if __name__ == "__main__":
cli()$ python command_line.py textual-app
>> TUI["textual-app"]$ python command_line.py textual-app Print Something Entirely Different
>> TUI["textual-app Print Something Entirely Different"]argv becomes a little complicated to manage when being nested on other CLIs. Instead, it would be awesome to do something like this to pass in a more complex configuration object:
@cli.command("textual-app")
@click.argument("args", nargs=-1, required=False)
def textual_app(args: List[str]):
SimplestApp(config_object={"args": args}).run()and subsequently be able to grab that config like this:
def compose(self) -> ComposeResult:
assert isinstance(self.config_object, dict)
assert "args" in self.config_object
response = (
" ".join(self.config_object["args"])
if self.config_object["args"]
else self.default_response
)
yield Static(response)Here's how I've implemented this myself using a subclass of App - let me know your thoughts. I'd be more than happy to contribute.
from __future__ import annotations
from sys import argv
from typing import Type, Any
from textual.app import App, ComposeResult, CSSPathType
from textual.driver import Driver
from textual.widgets import Static
class AppWithConfig(App):
def __init__(
self,
driver_class: Type[Driver] | None = None,
css_path: CSSPathType = None,
watch_css: bool = False,
config_object: Any = None,
):
"""
Like the textual.app.App class, but with an extra config_object property
Parameters
----------
driver_class: Type[Driver]
css_path: CSSPathType
watch_css: bool
config_object: Any
A configuration object. This is an optional python object,
like a dictionary to pass into an application
"""
self.config_object = config_object
super().__init__(
driver_class=driver_class, css_path=css_path, watch_css=watch_css
)
class SimplestApp(AppWithConfig):
default_response = "Hey, I'm a default response since nothing was passed"
def compose(self) -> ComposeResult:
assert isinstance(self.config_object, dict)
assert "args" in self.config_object
response = (
" ".join(self.config_object["args"])
if self.config_object["args"]
else self.default_response
)
yield Static(response)
if __name__ == "__main__":
args = argv[1:]
app = SimplestApp(config_object={"args": args})
app.run()... and now:
$ python command_line.py textual-app
>> TUI["Hey, I'm a default response since nothing was passed"]$ python command_line.py textual-app Print Something Entirely Different
>> TUI["Print Something Entirely Different"]$ python simple_app.py
>> TUI["Hey, I'm a default response since nothing was passed"]$ python simple_app.py Print Something Else
>> TUI["Print Something Else"]