Skip to content

Commit 3b70db5

Browse files
authored
Merge pull request #3777 from Textualize/tty-interactive
TTY_INTERACTIVE env var
2 parents 26af5f5 + b46bd78 commit 3b70db5

File tree

5 files changed

+48
-5
lines changed

5 files changed

+48
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
- Fixed extraction of recursive exceptions https://github.com/Textualize/rich/pull/3772
1818

19+
### Added
20+
21+
- Added `TTY_INTERACTIVE` environment variable to force interactive mode off or on https://github.com/Textualize/rich/pull/3777
22+
1923
## [14.0.0] - 2025-03-30
2024

2125
### Added

docs/source/console.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,13 @@ If Rich detects that it is not writing to a terminal it will strip control codes
405405

406406
Letting Rich auto-detect terminals is useful as it will write plain text when you pipe output to a file or other application.
407407

408+
.. _interactive_mode:
409+
408410
Interactive mode
409411
----------------
410412

411-
Rich will remove animations such as progress bars and status indicators when not writing to a terminal as you probably don't want to write these out to a text file (for example). You can override this behavior by setting the ``force_interactive`` argument on the constructor. Set it to True to enable animations or False to disable them.
413+
Rich will remove animations such as progress bars and status indicators when not writing to a terminal as you probably don't want to write these out to a text file (for example). You can override this behavior by setting the ``force_interactive`` argument on the constructor. Set it to ``True`` to enable animations or ``False`` to disable them.
412414

413-
.. note::
414-
Some CI systems support ANSI color and style but not anything that moves the cursor or selectively refreshes parts of the terminal. For these you might want to set ``force_terminal`` to ``True`` and ``force_interactive`` to ``False``.
415415

416416
Environment variables
417417
---------------------
@@ -429,8 +429,11 @@ If the environment variable ``NO_COLOR`` is set, Rich will disable all color in
429429

430430
The environment variable ``TTY_COMPATIBLE`` is used to override Rich's auto-detection of terminal support. If ``TTY_COMPATIBLE`` is set to ``1`` then Rich will assume it is writing to a device which can handle escape sequences like a terminal. If ``TTY_COMPATIBLE`` is set to ``"0"``, then Rich will assume that it is writing to a device that is *not* capable of displaying escape sequences (such as a regular file). If the variable is not set, or set to a value other than "0" or "1", then Rich will attempt to auto-detect terminal support.
431431

432+
The environment variable ``TTY_INTERACTIVE`` is used to override Rich's auto-detection of :ref:`interactive_mode`. If you set this to ``"0"``, it will disable interactive mode even if Rich thinks it is writing to a terminal. Set this to ``"1"`` to force interactive mode on. If this environment variable is not set, or set to any other value, then interactive mode will be auto-detected as normal.
433+
432434
.. note::
433-
If you want Rich output in CI or Github Actions, then you should set ``TTY_COMPATIBLE=1``.
435+
If you want Rich output in CI or Github Actions, then you should set ``TTY_COMPATIBLE=1`` and ``TTY_INTERACTIVE=0``. The combination of both these variables tells rich that it can output escape sequences,
436+
and also that there is no user interacting with the terminal -- so it won't bother animating progress bars.
434437

435438
If ``width`` / ``height`` arguments are not explicitly provided as arguments to ``Console`` then the environment variables ``COLUMNS`` / ``LINES`` can be used to set the console width / height. ``JUPYTER_COLUMNS`` / ``JUPYTER_LINES`` behave similarly and are used in Jupyter.
436439

rich/console.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,20 @@
2222
Dict,
2323
Iterable,
2424
List,
25+
Literal,
2526
Mapping,
2627
NamedTuple,
2728
Optional,
29+
Protocol,
2830
TextIO,
2931
Tuple,
3032
Type,
3133
Union,
3234
cast,
35+
runtime_checkable,
3336
)
3437

3538
from rich._null_file import NULL_FILE
36-
from typing import Literal, Protocol, runtime_checkable
3739

3840
from . import errors, themes
3941
from ._emoji_replace import _emoji_replace
@@ -731,6 +733,14 @@ def __init__(
731733
if no_color is not None
732734
else self._environ.get("NO_COLOR", "") != ""
733735
)
736+
if force_interactive is None:
737+
tty_interactive = self._environ.get("TTY_INTERACTIVE", None)
738+
if tty_interactive is not None:
739+
if tty_interactive == "0":
740+
force_interactive = False
741+
elif tty_interactive == "1":
742+
force_interactive = True
743+
734744
self.is_interactive = (
735745
(self.is_terminal and not self.is_dumb_terminal)
736746
if force_interactive is None

rich/diagnose.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def report() -> None: # pragma: no cover
2626
"TERM_PROGRAM",
2727
"TERM",
2828
"TTY_COMPATIBLE",
29+
"TTY_INTERACTIVE",
2930
"VSCODE_VERBOSE_LOGGING",
3031
)
3132
env = {name: os.getenv(name) for name in env_names}

tests/test_console.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,31 @@ def test_capture_and_record() -> None:
10381038
assert recorded_content == "Print 0\n"
10391039

10401040

1041+
def test_tty_interactive() -> None:
1042+
"""Check TTY_INTERACTIVE environment var."""
1043+
1044+
# Bytes file, not interactive
1045+
console = Console(file=io.BytesIO())
1046+
assert not console.is_interactive
1047+
1048+
# Bytes file, force interactive
1049+
console = Console(file=io.BytesIO(), _environ={"TTY_INTERACTIVE": "1"})
1050+
assert console.is_interactive
1051+
1052+
# Force tty compatible, should be interactive
1053+
console = Console(file=io.BytesIO(), _environ={"TTY_COMPATIBLE": "1"})
1054+
assert console.is_interactive
1055+
1056+
# Force tty compatible, force not interactive
1057+
console = Console(
1058+
file=io.BytesIO(), _environ={"TTY_COMPATIBLE": "1", "TTY_INTERACTIVE": "0"}
1059+
)
1060+
1061+
# Bytes file, Unknown value of TTY_INTERACTIVE should still auto-detect
1062+
console = Console(file=io.BytesIO(), _environ={"TTY_INTERACTIVE": "foo"})
1063+
assert not console.is_interactive
1064+
1065+
10411066
def test_tty_compatible() -> None:
10421067
"""Check TTY_COMPATIBLE environment var."""
10431068

0 commit comments

Comments
 (0)