샘플
rich 15.0.0: Handle empty print end parameters, preserve trailing ANSI newlines, and proxy isatty on FileProxy streams in rich >= 15.0.0
검증된 샘플 — pypi rich 15.0.0: Handle empty print end parameters, preserve trailing ANSI newlines, and proxy isatty on FileProxy streams in rich >= 15.0.0.…
sha256:c73375b6e6a674d2e337f7db2a22f2919aaf5d0dbee950a40d6160b3cf946798
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 2
- 빌드한 서명 키
- 2
선언된 환경
python linux x64 python python pip
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-16 |
| python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
케이스
HOW- 목표
- Handle empty print end parameters, preserve trailing ANSI newlines, and proxy isatty on FileProxy streams in rich >= 15.0.0
- 패키지
- 심벌
-
- rich.console.Console.print
- rich.text.Text.from_ansi
- rich.file_proxy.FileProxy.isatty
- 환경
- python
- 생성일
- 2026-08-16T06:22:48Z
컨트랙트
- assert console.print(end="") with no objects emits an empty string instead of an unwanted newline
- assert console.print(end="prefix ") with no objects emits the custom end string
- assert console.print() with no arguments still emits a default newline
- assert Text.from_ansi preserves trailing newlines instead of stripping them via splitlines
- assert Text.from_ansi on a bare newline returns text with length 1 containing newline
- assert FileProxy.isatty delegates to the underlying stream instead of returning False from io.TextIOBase
파일
- NOTES.md
- csx.json
- requirements.txt
- src/__init__.py
- src/stream_adapter.py
- test/contract.py
소스
# Notes on rich >= 15.0.0 Behavioral Fixes
## What search_known_solution Answered
`search_known_solution` returned existing sample `sha256:3dae0c04df2ebeff413e9a4a6dc0213423def8b204f079fa3e738abce3780035` ("Render rich Console output to a string a test can assert on, and pin the escape codes, width, markup and highlighting that decide what the string holds"), which focused on general Console string rendering, terminal detection heuristics, style caching, and highlighter mechanics. This sample instead addresses three distinct behavioral bugfixes resolved in rich 15.0.0: empty print `end` argument handling (PR 4075), trailing newline preservation in `Text.from_ansi` (PR 4076), and `FileProxy.isatty` stream capability forwarding (PR 4077).
## What a Model Would Have Written Instead
A model confidently assumes `console.print(end="")` emits nothing like Python's built-in `print(end="")`, `Text.from_ansi()` preserves exact trailing newlines without trimming, and `FileProxy` delegates `isatty()` to the wrapped file stream via `__getattr__`.
## How the Wrong Version Fails
On rich <= 14.3.4, the wrong version fails loudly with `AssertionError`: `console.print(end="")` unexpectedly emits a newline (`\n`), `Text.from_ansi("\n")` drops its trailing newline to produce an empty string (`""`), and `FileProxy.isatty()` unconditionally returns `False` due to shadowing by `io.TextIOBase.isatty`.
{"case":{"caseId":"case:sha256:60ee42228f5804b784e1e603f10c435fe34e09a1c089570c4836a7809e89ab71","contract":["assert console.print(end=\"\") with no objects emits an empty string instead of an unwanted newline","assert console.print(end=\"prefix \") with no objects emits the custom end string","assert console.print() with no arguments still emits a default newline","assert Text.from_ansi preserves trailing newlines instead of stripping them via splitlines","assert Text.from_ansi on a bare newline returns text with length 1 containing newline","assert FileProxy.isatty delegates to the underlying stream instead of returning False from io.TextIOBase"],"goal":"Handle empty print end parameters, preserve trailing ANSI newlines, and proxy isatty on FileProxy streams in rich \u003e= 15.0.0","kind":"HOW","packages":["pkg:pypi/rich@15.0.0"],"schemaVersion":1,"symbols":["rich.console.Console.print","rich.text.Text.from_ansi","rich.file_proxy.FileProxy.isatty"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/rich@15.0.0"],"schemaVersion":1,"symbols":["rich.console.Console.print","rich.text.Text.from_ansi","rich.file_proxy.FileProxy.isatty"],"verifierAdapter":"python@1"}
# Full closure for rich 15.0.0
rich==15.0.0
markdown-it-py==4.2.0
mdurl==0.1.2
pygments==2.20.0
"""CodeSampleX sample for rich 15.0.0 behavioural fixes."""
"""Adapters demonstrating fixed stream, ANSI, and proxy behaviors in Rich >= 15.0.0.
In versions prior to Rich 15.0.0:
1. `Console.print(end="")` with no positional objects unconditionally printed a newline (`\\n`),
ignoring the `end` argument because `if not objects:` mapped to `(NewLine(),)`.
2. `Text.from_ansi()` used `splitlines()`, which dropped trailing empty lines and stripped
trailing newlines from ANSI text streams.
3. `FileProxy` inherited from `io.TextIOBase`, whose default `isatty()` returns `False`.
Because `isatty` already existed on the base class, `__getattr__` was bypassed,
preventing proxied TTY streams from reporting `isatty() == True`.
"""
from __future__ import annotations
import io
from typing import IO
from rich.ansi import AnsiDecoder
from rich.console import Console
from rich.file_proxy import FileProxy
from rich.text import Text
def emit_prompt_end(console: Console, end: str = "") -> None:
"""Print with no objects and a specified `end` terminator.
In Rich >= 15.0.0, calling `console.print(end="")` emits an empty string
without adding an unintended newline.
"""
console.print(end=end)
def parse_ansi_text(raw_ansi: str) -> Text:
"""Parse ANSI formatted text into a Rich Text instance while preserving trailing newlines."""
return Text.from_ansi(raw_ansi)
def decode_ansi_lines(raw_ansi: str) -> list[Text]:
"""Decode raw ANSI text into a list of line Text instances via AnsiDecoder."""
decoder = AnsiDecoder()
return list(decoder.decode(raw_ansi))
def create_proxied_stream(console: Console, stream: IO[str]) -> FileProxy:
"""Wrap a stream in Rich's FileProxy, preserving underlying isatty() capability."""
return FileProxy(console, stream)
import io
import os
import sys
from importlib.metadata import version
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import rich
from rich.ansi import AnsiDecoder
from rich.console import Console
from rich.file_proxy import FileProxy
from rich.text import Text
from src.stream_adapter import (
create_proxied_stream,
decode_ansi_lines,
emit_prompt_end,
parse_ansi_text,
)
# Clear rich environment variables to ensure stable test environment
for key in (
"COLUMNS",
"LINES",
"TERM",
"NO_COLOR",
"FORCE_COLOR",
"TTY_COMPATIBLE",
"COLORTERM",
"TTY_INTERACTIVE",
"UNICODE_VERSION",
):
os.environ.pop(key, None)
# --- 1. Console.print(end=...) with no positional arguments ---------------
# In rich < 15.0.0, console.print(end="") emitted "\n" because `if not objects:`
# defaulted to (NewLine(),), ignoring `end`.
# In rich >= 15.0.0, console.print(end="") emits "" (empty string).
buffer_empty = io.StringIO()
console_empty = Console(file=buffer_empty, force_terminal=False)
emit_prompt_end(console_empty, end="")
assert buffer_empty.getvalue() == "", (
f"Expected empty string from console.print(end=''), got {buffer_empty.getvalue()!r}"
)
buffer_custom = io.StringIO()
console_custom = Console(file=buffer_custom, force_terminal=False)
emit_prompt_end(console_custom, end=">>> ")
assert buffer_custom.getvalue() == ">>> ", (
f"Expected '>>> ' from console.print(end='>>> '), got {buffer_custom.getvalue()!r}"
)
buffer_default = io.StringIO()
console_default = Console(file=buffer_default, force_terminal=False)
console_default.print()
assert buffer_default.getvalue() == "\n", (
f"Expected default '\\n' from console.print(), got {buffer_default.getvalue()!r}"
)
# --- 2. Text.from_ansi trailing newline preservation ---------------------
# In rich < 15.0.0, Text.from_ansi called splitlines(), which stripped trailing
# newlines. A string like "line1\nline2\n" became "line1\nline2", and "\n" became "".
# In rich >= 15.0.0, trailing newlines are preserved in the Text plain content.
t_single_newline = parse_ansi_text("\n")
assert t_single_newline.plain == "\n", (
f"Expected '\\n' from Text.from_ansi('\\n'), got {t_single_newline.plain!r}"
)
t_ansi_trailing = parse_ansi_text("\x1b[32mhello\x1b[0m\n\x1b[34mworld\x1b[0m\n")
assert t_ansi_trailing.plain == "hello\nworld\n", (
f"Expected 'hello\\nworld\\n', got {t_ansi_trailing.plain!r}"
)
decoded_lines = decode_ansi_lines("first\nsecond\n")
assert len(decoded_lines) == 3, (
f"Expected 3 decoded lines (including trailing empty line), got {len(decoded_lines)}"
)
assert [line.plain for line in decoded_lines] == ["first", "second", ""]
# --- 3. FileProxy.isatty forwarding --------------------------------------
# In rich < 15.0.0, FileProxy subclassed io.TextIOBase without overriding isatty().
# io.TextIOBase.isatty() returns False, which masked __getattr__ from reaching the
# underlying file object.
# In rich >= 15.0.0, FileProxy explicitly defines `def isatty(self) -> bool: return self.__file.isatty()`.
class MockTTYStream(io.StringIO):
def isatty(self) -> bool:
return True
class MockNonTTYStream(io.StringIO):
def isatty(self) -> bool:
return False
tty_target = MockTTYStream()
non_tty_target = MockNonTTYStream()
c = Console(file=io.StringIO())
proxy_tty = create_proxied_stream(c, tty_target)
assert proxy_tty.isatty() is True, (
f"Expected proxy_tty.isatty() to return True, got {proxy_tty.isatty()}"
)
proxy_non_tty = create_proxied_stream(c, non_tty_target)
assert proxy_non_tty.isatty() is False, (
f"Expected proxy_non_tty.isatty() to return False, got {proxy_non_tty.isatty()}"
)
print(f"contract ok: rich {version('rich')} patch fixes verified")