mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add --port option to textual console. (#2258)
* Add --port option to textual console. * Changelog. * Address review feedback. * Mark unpredictable test as xfail. This test gets an xfail mark until #2254 is open. * Make DEVTOOLS_PORT a constant. Related review: https://github.com/Textualize/textual/pull/2258\#discussion_r1165210395 * Factor logic into function. Related review: https://github.com/Textualize/textual/pull/2258\#discussion_r1165298259 * Remove dead import.
This commit is contained in:
committed by
GitHub
parent
e32cdbb390
commit
bb2c31ba35
@@ -16,14 +16,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Added
|
||||
|
||||
- Added `DataTable.remove_row` method https://github.com/Textualize/textual/pull/2253
|
||||
- option `--port` to the command `textual console` to specify which port the console should connect to https://github.com/Textualize/textual/pull/2258
|
||||
- `Widget.scroll_to_center` method to scroll children to the center of container widget https://github.com/Textualize/textual/pull/2255 and https://github.com/Textualize/textual/pull/2276
|
||||
- Added `TabActivated` message to `TabbedContent` https://github.com/Textualize/textual/pull/2260
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed order styles are applied in DataTable - allows combining of renderable styles and component classes https://github.com/Textualize/textual/pull/2272
|
||||
|
||||
|
||||
## [0.19.1] - 2023-04-10
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -90,6 +90,20 @@ Multiple groups may be excluded, for example to exclude everything except warnin
|
||||
textual console -x SYSTEM -x EVENT -x DEBUG -x INFO
|
||||
```
|
||||
|
||||
### Custom port
|
||||
|
||||
You can use the option `--port` to specify a custom port to run the console on, which comes in handy if you have other software running on the port that Textual uses by default:
|
||||
|
||||
```bash
|
||||
textual console --port 7342
|
||||
```
|
||||
|
||||
Then, use the command `run` with the same `--port` option:
|
||||
|
||||
```bash
|
||||
textual run --dev --port 7342 my_app.py
|
||||
```
|
||||
|
||||
|
||||
## Textual log
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from ..constants import DEFAULT_DEVTOOLS_PORT, DEVTOOLS_PORT_ENVIRON_VARIABLE
|
||||
|
||||
try:
|
||||
import click
|
||||
except ImportError:
|
||||
@@ -21,14 +23,27 @@ def run():
|
||||
|
||||
|
||||
@run.command(help="Run the Textual Devtools console.")
|
||||
@click.option(
|
||||
"--port",
|
||||
"port",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="PORT",
|
||||
help=f"Port to use for the development mode console. Defaults to {DEFAULT_DEVTOOLS_PORT}.",
|
||||
)
|
||||
@click.option("-v", "verbose", help="Enable verbose logs.", is_flag=True)
|
||||
@click.option("-x", "--exclude", "exclude", help="Exclude log group(s)", multiple=True)
|
||||
def console(verbose: bool, exclude: list[str]) -> None:
|
||||
def console(port: int | None, verbose: bool, exclude: list[str]) -> None:
|
||||
"""Launch the textual console."""
|
||||
import os
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
from textual.devtools.server import _run_devtools
|
||||
|
||||
if port is not None:
|
||||
os.environ[DEVTOOLS_PORT_ENVIRON_VARIABLE] = str(port)
|
||||
|
||||
console = Console()
|
||||
console.clear()
|
||||
console.show_cursor(False)
|
||||
@@ -78,6 +93,14 @@ def _post_run_warnings() -> None:
|
||||
)
|
||||
@click.argument("import_name", metavar="FILE or FILE:APP")
|
||||
@click.option("--dev", "dev", help="Enable development mode", is_flag=True)
|
||||
@click.option(
|
||||
"--port",
|
||||
"port",
|
||||
type=int,
|
||||
default=None,
|
||||
metavar="PORT",
|
||||
help=f"Port to use for the development mode console. Defaults to {DEFAULT_DEVTOOLS_PORT}.",
|
||||
)
|
||||
@click.option("--press", "press", help="Comma separated keys to simulate press")
|
||||
@click.option(
|
||||
"--screenshot",
|
||||
@@ -86,7 +109,9 @@ def _post_run_warnings() -> None:
|
||||
metavar="DELAY",
|
||||
help="Take screenshot after DELAY seconds",
|
||||
)
|
||||
def run_app(import_name: str, dev: bool, press: str, screenshot: int | None) -> None:
|
||||
def run_app(
|
||||
import_name: str, dev: bool, port: int | None, press: str, screenshot: int | None
|
||||
) -> None:
|
||||
"""Run a Textual app.
|
||||
|
||||
The code to run may be given as a path (ending with .py) or as a Python
|
||||
@@ -107,7 +132,6 @@ def run_app(import_name: str, dev: bool, press: str, screenshot: int | None) ->
|
||||
in quotes:
|
||||
|
||||
textual run "foo.py arg --option"
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -116,6 +140,9 @@ def run_app(import_name: str, dev: bool, press: str, screenshot: int | None) ->
|
||||
|
||||
from textual.features import parse_features
|
||||
|
||||
if port is not None:
|
||||
os.environ[DEVTOOLS_PORT_ENVIRON_VARIABLE] = str(port)
|
||||
|
||||
features = set(parse_features(os.environ.get("TEXTUAL", "")))
|
||||
if dev:
|
||||
features.add("debug")
|
||||
|
||||
@@ -25,10 +25,28 @@ def get_environ_bool(name: str) -> bool:
|
||||
Returns:
|
||||
`True` if the env var is "1", otherwise `False`.
|
||||
"""
|
||||
has_environ = os.environ.get(name) == "1"
|
||||
has_environ = get_environ(name) == "1"
|
||||
return has_environ
|
||||
|
||||
|
||||
def get_environ_int(name: str, default: int) -> int:
|
||||
"""Retrieves an integer environment variable.
|
||||
|
||||
Args:
|
||||
name: Name of environment variable.
|
||||
default: The value to use if the value is not set, or set to something other
|
||||
than a valid integer.
|
||||
|
||||
Returns:
|
||||
The integer associated with the environment variable if it's set to a valid int
|
||||
or the default value otherwise.
|
||||
"""
|
||||
try:
|
||||
return int(get_environ(name, default))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
|
||||
BORDERS = list(BORDER_CHARS)
|
||||
|
||||
DEBUG: Final[bool] = get_environ_bool("TEXTUAL_DEBUG")
|
||||
@@ -37,3 +55,13 @@ DRIVER: Final[str | None] = get_environ("TEXTUAL_DRIVER", None)
|
||||
|
||||
LOG_FILE: Final[str | None] = get_environ("TEXTUAL_LOG", None)
|
||||
"""A last resort log file that appends all logs, when devtools isn't working."""
|
||||
|
||||
|
||||
DEVTOOLS_PORT_ENVIRON_VARIABLE: Final[str] = "TEXTUAL_CONSOLE_PORT"
|
||||
"""The name of the environment variable that sets the port for the devtools."""
|
||||
DEFAULT_DEVTOOLS_PORT: Final[int] = 8081
|
||||
"""The default port to use for the devtools."""
|
||||
DEVTOOLS_PORT: Final[int] = get_environ_int(
|
||||
DEVTOOLS_PORT_ENVIRON_VARIABLE, DEFAULT_DEVTOOLS_PORT
|
||||
)
|
||||
"""Constant with the port that the devtools will connect to."""
|
||||
|
||||
@@ -16,8 +16,8 @@ from rich.console import Console
|
||||
from rich.segment import Segment
|
||||
|
||||
from .._log import LogGroup, LogVerbosity
|
||||
from ..constants import DEVTOOLS_PORT
|
||||
|
||||
DEVTOOLS_PORT = 8081
|
||||
WEBSOCKET_CONNECT_TIMEOUT = 3
|
||||
LOG_QUEUE_MAXSIZE = 512
|
||||
|
||||
@@ -88,10 +88,12 @@ class DevtoolsClient:
|
||||
|
||||
Args:
|
||||
host: The host the devtools server is running on, defaults to "127.0.0.1"
|
||||
port: The port the devtools server is accessed via, defaults to 8081
|
||||
port: The port the devtools server is accessed via, `DEVTOOLS_PORT` by default.
|
||||
"""
|
||||
|
||||
def __init__(self, host: str = "127.0.0.1", port: int = DEVTOOLS_PORT) -> None:
|
||||
def __init__(self, host: str = "127.0.0.1", port: int | None = None) -> None:
|
||||
if port is None:
|
||||
port = DEVTOOLS_PORT
|
||||
self.url: str = f"ws://{host}:{port}"
|
||||
self.session: aiohttp.ClientSession | None = None
|
||||
self.log_queue_task: Task | None = None
|
||||
|
||||
@@ -46,7 +46,10 @@ def _run_devtools(verbose: bool, exclude: list[str] | None = None) -> None:
|
||||
|
||||
try:
|
||||
run_app(
|
||||
app, port=DEVTOOLS_PORT, print=noop_print, loop=asyncio.get_event_loop()
|
||||
app,
|
||||
port=DEVTOOLS_PORT,
|
||||
print=noop_print,
|
||||
loop=asyncio.get_event_loop(),
|
||||
)
|
||||
except OSError:
|
||||
from rich import print
|
||||
|
||||
@@ -10,6 +10,7 @@ from rich.console import ConsoleDimensions
|
||||
from rich.panel import Panel
|
||||
|
||||
from tests.utilities.render import wait_for_predicate
|
||||
from textual.constants import DEFAULT_DEVTOOLS_PORT
|
||||
from textual.devtools.client import DevtoolsClient
|
||||
from textual.devtools.redirect_output import DevtoolsLog
|
||||
|
||||
@@ -21,7 +22,7 @@ TIMESTAMP = 1649166819
|
||||
|
||||
def test_devtools_client_initialize_defaults():
|
||||
devtools = DevtoolsClient()
|
||||
assert devtools.url == "ws://127.0.0.1:8081"
|
||||
assert devtools.url == f"ws://127.0.0.1:{DEFAULT_DEVTOOLS_PORT}"
|
||||
|
||||
|
||||
async def test_devtools_client_is_connected(devtools):
|
||||
|
||||
@@ -18,7 +18,7 @@ class MyApp(App[None]):
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with VerticalScroll():
|
||||
yield Label(("SPAM\n" * 25)[:-1])
|
||||
yield Label(("SPAM\n" * 205)[:-1])
|
||||
with VerticalScroll():
|
||||
yield Label(("SPAM\n" * 53)[:-1])
|
||||
with VerticalScroll(id="vertical"):
|
||||
|
||||
@@ -418,6 +418,9 @@ def test_scroll_visible(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_visible.py", press=["t"])
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason="Unpredictable while https://github.com/Textualize/textual/issues/2254 is open."
|
||||
)
|
||||
def test_scroll_to_center(snap_compare):
|
||||
# READ THIS IF THIS TEST FAILS:
|
||||
# While https://github.com/Textualize/textual/issues/2254 is open, the snapshot
|
||||
|
||||
Reference in New Issue
Block a user