Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 12 additions & 2 deletions src/huggingface_hub/cli/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@
import click

from huggingface_hub.errors import ConfirmationError
from huggingface_hub.utils import ANSI, StatusLine, disable_progress_bars, is_agent, tabulate
from huggingface_hub.utils import (
ANSI,
StatusLine,
disable_progress_bars,
enable_progress_bars,
is_agent,
tabulate,
)


class OutputFormat(str, Enum):
Expand Down Expand Up @@ -63,10 +70,13 @@ def __init__(self) -> None:
def set_mode(self, mode: OutputFormat = OutputFormat.auto) -> None:
"""Override the output mode (called once at startup and again per '--format' flag)."""
if mode == OutputFormat.auto:
mode = OutputFormat.agent if is_agent() else OutputFormat.human
is_interactive = bool(getattr(sys.stderr, "isatty", lambda: False)())
mode = OutputFormat.agent if (is_agent() and not is_interactive) else OutputFormat.human
self.mode = mode
if mode != OutputFormat.human:
disable_progress_bars()
else:
enable_progress_bars()
Comment thread
cursor[bot] marked this conversation as resolved.

def set_no_truncate(self, no_truncate: bool) -> None:
"""Toggle off cell truncation for human table output."""
Expand Down
2 changes: 1 addition & 1 deletion src/huggingface_hub/utils/_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def yellow(cls, s: str) -> str:

@classmethod
def _format(cls, s: str, code: str) -> str:
if os.environ.get("NO_COLOR") or is_agent():
if os.environ.get("NO_COLOR") or (is_agent() and not bool(getattr(sys.stderr, "isatty", lambda: False)())):
# See https://no-color.org/
return s
return f"{code}{s}{cls._reset}"
Expand Down
42 changes: 42 additions & 0 deletions tests/test_cli_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from huggingface_hub.cli._output import Output, OutputFormat, _ascii_safe, _to_header
from huggingface_hub.errors import ConfirmationError
from huggingface_hub.utils import are_progress_bars_disabled, enable_progress_bars


HUMAN = OutputFormat.human
Expand Down Expand Up @@ -86,6 +87,47 @@ def test_auto_resets_after_explicit():
assert o.mode == HUMAN


def test_set_mode_human_reenables_progress_bars():
try:
o = Output()
o.set_mode(OutputFormat.agent)
assert are_progress_bars_disabled()
o.set_mode(OutputFormat.human)
assert not are_progress_bars_disabled()
finally:
enable_progress_bars()


def test_auto_resolves_to_human_in_interactive_terminal_even_if_agent(monkeypatch):
monkeypatch.setattr("huggingface_hub.cli._output.is_agent", lambda: True)
monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
assert Output().mode == HUMAN


def test_auto_resolves_to_agent_when_not_interactive(monkeypatch):
monkeypatch.setattr("huggingface_hub.cli._output.is_agent", lambda: True)
monkeypatch.setattr(sys.stderr, "isatty", lambda: False)
assert Output().mode == AGENT


def test_explicit_agent_mode_forces_agent_even_if_interactive(monkeypatch):
monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
o = Output()
o.set_mode(OutputFormat.agent)
assert o.mode == AGENT


def test_explicit_agent_mode_disables_bars_even_in_interactive(monkeypatch):
monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
try:
o = Output()
o.set_mode(OutputFormat.agent)
assert o.mode == AGENT
assert are_progress_bars_disabled()
finally:
enable_progress_bars()


# =============================================================================
# out.result()
# =============================================================================
Expand Down
15 changes: 15 additions & 0 deletions tests/test_utils_terminal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
from unittest import mock

import pytest
Expand Down Expand Up @@ -30,6 +31,20 @@ def test_ansi_no_color(self) -> None:

assert ANSI.gray(ANSI.bold("this is bold and grey")) == "this is bold and grey"

@mock.patch.dict(os.environ, {}, clear=True)
def test_ansi_agent_interactive_preserves_color(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test `ANSI` does not suppress color in interactive terminal even if is_agent() is True."""
monkeypatch.setattr("huggingface_hub.utils._terminal.is_agent", lambda: True)
monkeypatch.setattr(sys.stderr, "isatty", lambda: True)
assert ANSI.bold("this is bold") == "\x1b[1mthis is bold\x1b[0m"

@mock.patch.dict(os.environ, {}, clear=True)
def test_ansi_agent_non_interactive_suppresses_color(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test `ANSI` suppresses color in non-interactive session when is_agent() is True."""
monkeypatch.setattr("huggingface_hub.utils._terminal.is_agent", lambda: True)
monkeypatch.setattr(sys.stderr, "isatty", lambda: False)
assert ANSI.bold("this is bold") == "this is bold"

def test_tabulate_utility(self) -> None:
"""Test `tabulate` works as expected."""
rows = [[1, 2, 3], ["a very long value", "foo", "bar"], ["", 123, 456]]
Expand Down