Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .changelog/015.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: run
ts: 2026-05-19 09:57:42.377983+00:00
type: breaking_change
auto_generated: true
change_kind: param_type_changed
details: 'param ''message_callbacks'' type: list[Callable[[ShellRunBefore | ShellRunPOpenStarted | ShellRunStdStarted | ShellRunStdReadError | ShellRunStdOutput | ShellRunRetryAttempt | ShellRunAfter], bool]] | None -> list[Callable[[ShellRunBefore | ShellRunPOpenStarted | ShellRunStdStarted | ShellRunStdReadError | ShellRunStdOutput | ShellRunRetryAttempt | ShellRunAfter], bool | None]] | None'
field_name: message_callbacks
group: shell
---
name: run_and_wait
ts: 2026-05-19 09:57:42.378001+00:00
type: breaking_change
auto_generated: true
change_kind: param_type_changed
details: 'param ''message_callbacks'' type: list[Callable[[ShellRunBefore | ShellRunPOpenStarted | ShellRunStdStarted | ShellRunStdReadError | ShellRunStdOutput | ShellRunRetryAttempt | ShellRunAfter], bool]] | None -> list[Callable[[ShellRunBefore | ShellRunPOpenStarted | ShellRunStdStarted | ShellRunStdReadError | ShellRunStdOutput | ShellRunRetryAttempt | ShellRunAfter], bool | None]] | None'
field_name: message_callbacks
group: shell
---
name: resolve_terminal_dimensions
ts: 2026-05-19 10:33:13.554374+00:00
type: keep_private
full_path: _internal._run_env.resolve_terminal_dimensions
---
name: AskShellSettings
ts: 2026-05-19 10:33:13.999068+00:00
type: additional_change
auto_generated: true
change_kind: optional_field_added
details: 'added optional field ''terminal_height'' (default: 40)'
field_name: terminal_height
group: __ROOT__
---
name: AskShellSettings
ts: 2026-05-19 10:33:13.999077+00:00
type: additional_change
auto_generated: true
change_kind: optional_field_added
details: 'added optional field ''terminal_width'' (default: 120)'
field_name: terminal_width
group: __ROOT__
17 changes: 16 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# path-sync copy -n python-template
name: CI

on:
Expand Down Expand Up @@ -87,3 +86,19 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: just pkg-pre-commit

apply-live:
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
runs-on: ubuntu-latest
env:
CI: true
PYTHONUNBUFFERED: 1
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd
with:
terraform_version: 1.12.1
terraform_wrapper: false
- run: just apply-live
6 changes: 3 additions & 3 deletions ask_shell/_internal/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from ask_shell._internal.events import (
ShellRunAfter,
ShellRunBefore,
ShellRunCallbackT,
ShellRunPOpenStarted,
ShellRunRetryAttempt,
ShellRunStdOutput,
Expand All @@ -45,7 +46,6 @@
RunIncompleteError,
ShellConfig,
ShellRun,
ShellRunEventT,
ShellRunQueueT,
)
from ask_shell.settings import AskShellSettings, _global_settings
Expand Down Expand Up @@ -467,7 +467,7 @@ def run(
env: dict[str, str] | None = None,
extra_popen_kwargs: dict | None = None,
is_binary_call: bool | None = None,
message_callbacks: list[Callable[[ShellRunEventT], bool]] | None = None,
message_callbacks: list[ShellRunCallbackT] | None = None,
print_prefix: str | None = None,
retry_initial_wait: float | None = None,
retry_jitter: float | None = None,
Expand Down Expand Up @@ -534,7 +534,7 @@ def run_and_wait(
env: dict[str, str] | None = None,
extra_popen_kwargs: dict | None = None,
is_binary_call: bool | None = None,
message_callbacks: list[Callable[[ShellRunEventT], bool]] | None = None,
message_callbacks: list[ShellRunCallbackT] | None = None,
print_prefix: str | None = None,
retry_initial_wait: float | None = None,
retry_jitter: float | None = None,
Expand Down
9 changes: 9 additions & 0 deletions ask_shell/_internal/_run_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,12 @@ def interactive_shell() -> bool:
logger.debug(f"Interactive shell not available: {non_interactive_reason}")
return False
return True


def resolve_terminal_dimensions(
settings: AskShellSettings | None = None,
) -> tuple[int | None, int | None]:
if interactive_shell():
return None, None
settings = settings or AskShellSettings.from_env()
return settings.terminal_width, settings.terminal_height
31 changes: 28 additions & 3 deletions ask_shell/_internal/_run_env_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
from ask_shell._internal._run_env import interactive_shell
from unittest.mock import patch

import pytest

def test_interactive_shell():
assert not interactive_shell()
from ask_shell._internal import _run_env
from ask_shell._internal._run_env import interactive_shell, resolve_terminal_dimensions
from ask_shell.settings import AskShellSettings


@pytest.fixture(autouse=True)
def _clear_interactive_cache():
interactive_shell.cache_clear()
yield
interactive_shell.cache_clear()


def test_resolve_terminal_dimensions_interactive():
with patch.object(_run_env, "interactive_shell", return_value=True):
assert resolve_terminal_dimensions() == (None, None)


def test_resolve_terminal_dimensions_non_interactive():
with patch.object(_run_env, "interactive_shell", return_value=False):
assert resolve_terminal_dimensions(AskShellSettings()) == (120, 40)


def test_resolve_terminal_dimensions_env_override(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv(AskShellSettings.ENV_NAME_TERMINAL_WIDTH, "100")
with patch.object(_run_env, "interactive_shell", return_value=False):
assert resolve_terminal_dimensions() == (100, 40)
11 changes: 10 additions & 1 deletion ask_shell/_internal/rich_live.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from zero_3rdparty.error import error_and_traceback
from zero_3rdparty.id_creator import simple_id

from ask_shell._internal._run_env import resolve_terminal_dimensions

_live: Live | None = None
_lock = RLock()

Expand Down Expand Up @@ -61,13 +63,20 @@ def wrapper(*args, **kwargs):
return wrapper


def _live_console() -> Console:
width, height = resolve_terminal_dimensions()
if width is not None and height is not None:
return Console(width=width, height=height)
return Console()


def get_live() -> Live:
global _live
if _live is not None:
return _live
with _lock:
if _live is None:
_live = Live(transient=True)
_live = Live(transient=True, console=_live_console())
live_render = _live._live_render
old_console = live_render.__rich_console__
live_render.__rich_console__ = _console_hook(old_console)
Expand Down
4 changes: 4 additions & 0 deletions ask_shell/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ class AskShellSettings(StaticSettings):
default=ShellRunSummary.ALL,
alias=ENV_NAME_SHELL_RUN_SUMMARY,
)
ENV_NAME_TERMINAL_WIDTH: ClassVar[str] = f"{ENV_PREFIX}TERMINAL_WIDTH"
terminal_width: int = Field(default=120, alias=ENV_NAME_TERMINAL_WIDTH)
ENV_NAME_TERMINAL_HEIGHT: ClassVar[str] = f"{ENV_PREFIX}TERMINAL_HEIGHT"
terminal_height: int = Field(default=40, alias=ENV_NAME_TERMINAL_HEIGHT)

@model_validator(mode="after")
def ensure_vars_set(self) -> Self:
Expand Down
10 changes: 8 additions & 2 deletions ask_shell/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
import pytest
from pytest_examples import CodeExample, EvalExample, find_examples

EXAMPLES_DIR = Path(__file__).parent.parent / "docs" / "examples"
PACKAGE_ROOT = Path(__file__).parent.parent
EXAMPLES_DIR = PACKAGE_ROOT / "docs" / "examples"


@pytest.fixture(autouse=True)
def _examples_package_cwd(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.chdir(PACKAGE_ROOT)


@pytest.mark.parametrize("example", find_examples(EXAMPLES_DIR), ids=str)
def test_examples(example: CodeExample, eval_example: EvalExample):
eval_example.set_config(line_length=120, target_version="py313", ruff_ignore=["I001", "T"]) # pyright: ignore[reportArgumentType]
eval_example.set_config(line_length=120, target_version="py313", ruff_ignore=["T"]) # pyright: ignore[reportArgumentType]
prefix = example.prefix_settings()
if prefix.get("test", "").startswith("skip"):
pytest.skip(prefix["test"])
Expand Down
4 changes: 4 additions & 0 deletions docs/_root/askshellsettings.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class AskShellSettings(StaticSettings):
run_logs_dir: Path | None = None
run_logs_clean: str = 'yesterday'
shell_run_summary: ShellRunSummary = <ShellRunSummary.ALL: 'all'>
terminal_width: int = 120
terminal_height: int = 40
```
<!-- === OK_EDIT: pkg-ext askshellsettings_def === -->

Expand Down Expand Up @@ -70,6 +72,8 @@ class AskShellSettings(StaticSettings):

| Version | Change |
|---------|--------|
| unreleased | added optional field 'terminal_width' (default: 120) |
| unreleased | added optional field 'terminal_height' (default: 40) |
| 0.7.0 | added optional field 'shell_run_summary' (default: <ShellRunSummary.ALL: 'all'>) |
| 0.6.0 | field 'thread_count' default: 50 -> 100 |
| 0.3.2 | field 'run_logs_dir' default added: None |
Expand Down
11 changes: 6 additions & 5 deletions docs/console/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<a id="removelivepart_def"></a>

### class: `RemoveLivePart`
- [source](../../ask_shell/_internal/rich_live.py#L148)
- [source](../../ask_shell/_internal/rich_live.py#L157)
> **Since:** 0.3.0

```python
Expand All @@ -40,7 +40,8 @@ class RemoveLivePart:
<a id="add_renderable_def"></a>

### function: `add_renderable`
- [source](../../ask_shell/_internal/rich_live.py#L152)
- [source](../../ask_shell/_internal/rich_live.py#L161)
- [Example: Mount a dynamic Rich renderable in ask-shell's live region with add_renderable, plus an apply-live demo with CI heartbeats](../examples/console/add_renderable.md)
> **Since:** 0.3.0

```python
Expand Down Expand Up @@ -76,7 +77,7 @@ def configure_logging(app: Typer, *, settings: AskShellSettings | None = None, a
<a id="get_live_console_def"></a>

### function: `get_live_console`
- [source](../../ask_shell/_internal/rich_live.py#L170)
- [source](../../ask_shell/_internal/rich_live.py#L179)
> **Since:** 0.3.0

```python
Expand Down Expand Up @@ -112,7 +113,7 @@ def interactive_shell() -> bool:
<a id="log_to_live_def"></a>

### function: `log_to_live`
- [source](../../ask_shell/_internal/rich_live.py#L210)
- [source](../../ask_shell/_internal/rich_live.py#L219)
> **Since:** 0.3.0

```python
Expand Down Expand Up @@ -166,7 +167,7 @@ class new_task:
<a id="print_to_live_def"></a>

### function: `print_to_live`
- [source](../../ask_shell/_internal/rich_live.py#L174)
- [source](../../ask_shell/_internal/rich_live.py#L183)
> **Since:** 0.3.0

```python
Expand Down
Loading
Loading