Updated docs for app.run (#2414)

* fix run app from python

* updated run

* remove function

* update help

* doc update
This commit is contained in:
Will McGugan
2023-04-28 10:47:24 +01:00
committed by GitHub
parent 730f78757a
commit 7c696ce378
5 changed files with 48 additions and 35 deletions

View File

@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.22.1] - 2023-04-28
### Fixed
- Fixed `textual run` issue https://github.com/Textualize/textual/issues/2391
## [0.22.0] - 2023-04-27 ## [0.22.0] - 2023-04-27
### Fixed ### Fixed

View File

@@ -8,7 +8,7 @@
Textual comes with a command line application of the same name. The `textual` command is a super useful tool that will help you to build apps. Textual comes with a command line application of the same name. The `textual` command is a super useful tool that will help you to build apps.
Take a moment to look through the available sub-commands. There will be even more helpful tools here in the future. Take a moment to look through the available subcommands. There will be even more helpful tools here in the future.
```bash ```bash
textual --help textual --help
@@ -17,25 +17,48 @@ textual --help
## Run ## Run
You can run Textual apps with the `run` subcommand. If you supply a path to a Python file it will load and run the application. The `run` sub-command runs Textual apps. If you supply a path to a Python file it will load and run the app.
```bash ```bash
textual run my_app.py textual run my_app.py
``` ```
The `run` sub-command will first look for a `App` instance called `app` in the global scope of your Python file. If there is no `app`, it will create an instance of the first `App` class it finds and run that. This is equivalent to running `python my_app.py` from the command prompt, but will allow you to set various switches which can help you debug, such as `--dev` which enable the [Console](#console).
Alternatively, you can add the name of an `App` instance or class after a colon to run a specific app in the Python file. Here's an example: See the `run` subcommand's help for details:
```bash ```bash
textual run my_app.py:alternative_app textual run --help
```
You can also run Textual apps from a python import.
The following command would import `music.play` and run a Textual app in that module:
```bash
textual run music.play
```
This assumes you have a Textual app instance called `app` in `music.play`.
If your app has a different name, you can append it after a colon:
```bash
textual run music.play:MusicPlayerApp
``` ```
!!! note !!! note
If the Python file contains a call to app.run() then you can launch the file as you normally would any other Python program. Running your app via `textual run` will give you access to a few Textual features such as live editing of CSS files. This works for both Textual app *instances* and *classes*.
### Running from commands
If your app is installed as a command line script, you can use the `-c` switch to run it.
For instance, the following will run the `textual colors` command:
```bash
textual run -c textual colors
```
## Live editing ## Live editing
If you combine the `run` command with the `--dev` switch your app will run in *development mode*. If you combine the `run` command with the `--dev` switch your app will run in *development mode*.

View File

@@ -17,7 +17,11 @@ from typing import NoReturn, Sequence
EXEC_SCRIPT = Template( EXEC_SCRIPT = Template(
"""\ """\
from textual.app import App from textual.app import App
from $MODULE import $APP as app; try:
from $MODULE import $APP as app;
except ImportError:
raise SystemExit("Unable to import '$APP' from module '$MODULE'") from None
if isinstance(app, App): if isinstance(app, App):
# If we imported an app, run it # If we imported an app, run it
app.run() app.run()
@@ -111,25 +115,6 @@ def exec_command(
os.execvpe(command, [command, *args], environment) os.execvpe(command, [command, *args], environment)
def check_import(module_name: str, app_name: str) -> bool:
"""Check if a symbol can be imported.
Args:
module_name: Name of the module
app_name: Name of the app.
Returns:
True if the app may be imported from the module.
"""
try:
sys.path.insert(0, "")
module = importlib.import_module(module_name)
except ImportError as error:
return False
return hasattr(module, app_name)
def exec_import( def exec_import(
import_name: str, args: Sequence[str], environment: dict[str, str] import_name: str, args: Sequence[str], environment: dict[str, str]
) -> NoReturn: ) -> NoReturn:
@@ -147,9 +132,6 @@ def exec_import(
module, _colon, app = import_name.partition(":") module, _colon, app = import_name.partition(":")
app = app or "app" app = app or "app"
if not check_import(module, app):
raise ExecImportError(f"Unable to import {app!r} from {import_name!r}")
script = EXEC_SCRIPT.substitute(MODULE=module, APP=app) script = EXEC_SCRIPT.substitute(MODULE=module, APP=app)
# Compiling the script will raise a SyntaxError if there are any invalid symbols # Compiling the script will raise a SyntaxError if there are any invalid symbols
compile(script, "textual-exec", "exec") compile(script, "textual-exec", "exec")

View File

@@ -147,16 +147,16 @@ def _run_app(
) -> None: ) -> None:
"""Run a Textual app. """Run a Textual app.
The code to run may be given as a path (ending with .py) or as a Python The app to run may be given as a path (ending with .py) which will be equivalent to running the
import, which will load the code and run an app called "app". You may optionally script with python, or as a Python import which will import and run an app called "app".
add a colon plus the class or class instance you want to run.
In the case of an import, you can import and run an alternative app by appending a colon followed
by the name of the app instance or class.
Here are some examples: Here are some examples:
textual run foo.py textual run foo.py
textual run foo.py:MyApp
textual run module.foo textual run module.foo
textual run module.foo:MyApp textual run module.foo:MyApp

View File

@@ -404,7 +404,9 @@ class Tabs(Widget, can_focus=True):
active_tab = self.query_one(f"#tabs-list > #{active}", Tab) active_tab = self.query_one(f"#tabs-list > #{active}", Tab)
self.query("#tabs-list > Tab.-active").remove_class("-active") self.query("#tabs-list > Tab.-active").remove_class("-active")
active_tab.add_class("-active") active_tab.add_class("-active")
self._highlight_active(animate=previously_active != "") self.call_after_refresh(
self._highlight_active, animate=previously_active != ""
)
self.post_message(self.TabActivated(self, active_tab)) self.post_message(self.TabActivated(self, active_tab))
else: else:
underline = self.query_one(Underline) underline = self.query_one(Underline)