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
15 changes: 15 additions & 0 deletions .changelog/017.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: disable_interactive_shell
ts: 2026-05-22 13:13:32.449577+00:00
type: make_public
details: created in _internal/_run_env.py
full_path: _internal._run_env.disable_interactive_shell
group: console
---
name: AskShellSettings
ts: 2026-05-22 13:13:32.918803+00:00
type: additional_change
auto_generated: true
change_kind: optional_field_added
details: 'added optional field ''disable_interactive_shell'' (default: False)'
field_name: disable_interactive_shell
group: __ROOT__
1 change: 1 addition & 0 deletions .groups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ groups:
- _internal.rich_progress
- _internal.typer_command
owned_refs:
- _internal._run_env.disable_interactive_shell
- _internal._run_env.interactive_shell
- _internal.rich_live.RemoveLivePart
- _internal.rich_live.add_renderable
Expand Down
15 changes: 14 additions & 1 deletion ask_shell/_internal/_run_env.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import os
import sys
from functools import lru_cache
from os import getenv
Expand Down Expand Up @@ -26,7 +27,13 @@ def _not_interactive_reason() -> str:

@lru_cache
def interactive_shell() -> bool:
if AskShellSettings.from_env().force_interactive_shell:
settings = AskShellSettings.from_env()
if settings.disable_interactive_shell:
logger.debug(
f"Interactive shell disabled by environment variable {settings.ENV_NAME_DISABLE_INTERACTIVE_SHELL}"
)
return False
if settings.force_interactive_shell:
logger.debug(
f"Interactive shell forced by environment variable {_global_settings.ENV_NAME_FORCE_INTERACTIVE_SHELL}"
)
Expand All @@ -37,6 +44,12 @@ def interactive_shell() -> bool:
return True


def disable_interactive_shell() -> None:
"""Force non-interactive mode for the remainder of this process."""
os.environ[AskShellSettings.ENV_NAME_DISABLE_INTERACTIVE_SHELL] = "true"
interactive_shell.cache_clear()


def resolve_terminal_dimensions(
settings: AskShellSettings | None = None,
) -> tuple[int | None, int | None]:
Expand Down
16 changes: 15 additions & 1 deletion ask_shell/_internal/_run_env_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@


@pytest.fixture(autouse=True)
def _clear_interactive_cache():
def _clear_interactive_cache(monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv(AskShellSettings.ENV_NAME_DISABLE_INTERACTIVE_SHELL, raising=False)
monkeypatch.delenv(AskShellSettings.ENV_NAME_FORCE_INTERACTIVE_SHELL, raising=False)
interactive_shell.cache_clear()
yield
monkeypatch.delenv(AskShellSettings.ENV_NAME_DISABLE_INTERACTIVE_SHELL, raising=False)
monkeypatch.delenv(AskShellSettings.ENV_NAME_FORCE_INTERACTIVE_SHELL, raising=False)
interactive_shell.cache_clear()


def test_disable_interactive_shell_forces_non_interactive(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv(AskShellSettings.ENV_NAME_FORCE_INTERACTIVE_SHELL, "true")
interactive_shell.cache_clear()
assert interactive_shell()

monkeypatch.setenv(AskShellSettings.ENV_NAME_DISABLE_INTERACTIVE_SHELL, "true")
interactive_shell.cache_clear()
assert not interactive_shell()


def test_resolve_terminal_dimensions_interactive():
with patch.object(_run_env, "interactive_shell", return_value=True):
assert resolve_terminal_dimensions() == (None, None)
Expand Down
2 changes: 2 additions & 0 deletions ask_shell/console.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Generated by pkg-ext
from ask_shell._internal._run_env import disable_interactive_shell as _disable_interactive_shell
from ask_shell._internal._run_env import interactive_shell as _interactive_shell
from ask_shell._internal.rich_live import RemoveLivePart as _RemoveLivePart
from ask_shell._internal.rich_live import add_renderable as _add_renderable
Expand All @@ -8,6 +9,7 @@
from ask_shell._internal.rich_progress import new_task as _new_task
from ask_shell._internal.typer_command import configure_logging as _configure_logging

disable_interactive_shell = _disable_interactive_shell
interactive_shell = _interactive_shell
RemoveLivePart = _RemoveLivePart
add_renderable = _add_renderable
Expand Down
6 changes: 6 additions & 0 deletions ask_shell/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ class AskShellSettings(StaticSettings):
alias=ENV_NAME_FORCE_INTERACTIVE_SHELL,
description="Useful for testing",
)
ENV_NAME_DISABLE_INTERACTIVE_SHELL: ClassVar[str] = f"{ENV_PREFIX}DISABLE_INTERACTIVE_SHELL"
disable_interactive_shell: bool = Field(
default=False,
alias=ENV_NAME_DISABLE_INTERACTIVE_SHELL,
description="Force non-interactive mode for long-running servers and automation",
)
ENV_NAME_THREAD_COUNT: ClassVar[str] = f"{ENV_PREFIX}THREAD_COUNT"
thread_count: int = Field(
default=100,
Expand Down
2 changes: 2 additions & 0 deletions docs/_root/askshellsettings.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AskShellSettings(StaticSettings):
SKIP_APP_NAME: bool = False
log_level: Literal[DEBUG, INFO, WARNING, ERROR, CRITICAL, UNSET] = 'UNSET'
force_interactive_shell: bool = False
disable_interactive_shell: bool = False
thread_count: int = 100
thread_pool_full_wait_time_seconds: float = 5
search_enabled_after_choices: int = 7
Expand Down Expand Up @@ -72,6 +73,7 @@ class AskShellSettings(StaticSettings):

| Version | Change |
|---------|--------|
| unreleased | added optional field 'disable_interactive_shell' (default: False) |
| 0.8.0 | added optional field 'terminal_width' (default: 120) |
| 0.8.0 | added optional field 'terminal_height' (default: 40) |
| 0.7.0 | added optional field 'shell_run_summary' (default: <ShellRunSummary.ALL: 'all'>) |
Expand Down
25 changes: 23 additions & 2 deletions docs/console/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [`RemoveLivePart`](#removelivepart_def)
- [`add_renderable`](#add_renderable_def)
- [`configure_logging`](#configure_logging_def)
- [`disable_interactive_shell`](#disable_interactive_shell_def)
- [`get_live_console`](#get_live_console_def)
- [`interactive_shell`](#interactive_shell_def)
- [`log_to_live`](#log_to_live_def)
Expand Down Expand Up @@ -95,7 +96,7 @@ def get_live_console() -> Console:
<a id="interactive_shell_def"></a>

### function: `interactive_shell`
- [source](../../ask_shell/_internal/_run_env.py#L27)
- [source](../../ask_shell/_internal/_run_env.py#L28)
> **Since:** 0.3.0

```python
Expand Down Expand Up @@ -180,4 +181,24 @@ def print_to_live(*objects, sep: str = ' ', end: str = '\n', style: str | Style
| Version | Change |
|---------|--------|
| 0.3.0 | Made public |
<!-- === OK_EDIT: pkg-ext print_to_live_def === -->
<!-- === OK_EDIT: pkg-ext print_to_live_def === -->
<!-- === DO_NOT_EDIT: pkg-ext disable_interactive_shell_def === -->
<a id="disable_interactive_shell_def"></a>

### function: `disable_interactive_shell`
- [source](../../ask_shell/_internal/_run_env.py#L47)
> **Since:** unreleased

```python
def disable_interactive_shell() -> None:
...
```

Force non-interactive mode for the remainder of this process.

### Changes

| Version | Change |
|---------|--------|
| unreleased | Made public |
<!-- === OK_EDIT: pkg-ext disable_interactive_shell_def === -->
Loading