From dc7efaa6deda42b27354a0a7808bab05689aa860 Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Tue, 16 Apr 2024 16:56:00 +0200 Subject: [PATCH] Add output.pipe_to_pager() that pipes lines to a pager without creating an intermediate temporary file --- osc/output/__init__.py | 1 + osc/output/output.py | 58 +++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/osc/output/__init__.py b/osc/output/__init__.py index 720a265f..c12d8370 100644 --- a/osc/output/__init__.py +++ b/osc/output/__init__.py @@ -1,6 +1,7 @@ from .key_value_table import KeyValueTable from .input import get_user_input from .output import get_default_pager +from .output import pipe_to_pager from .output import print_msg from .output import run_pager from .output import sanitize_text diff --git a/osc/output/output.py b/osc/output/output.py index fe90b665..50aa98b5 100644 --- a/osc/output/output.py +++ b/osc/output/output.py @@ -2,9 +2,11 @@ import os import platform import re import shlex +import subprocess import sys import tempfile from typing import Dict +from typing import List from typing import Optional from typing import TextIO from typing import Union @@ -170,6 +172,25 @@ def get_default_pager(): return 'more' +def get_pager(): + """ + Return (pager, env) where + ``pager`` is a list with parsed pager command + ``env`` is copy of os.environ() with added variables specific to the pager + """ + env = os.environ.copy() + pager = os.getenv("PAGER", default="").strip() + pager = pager or get_default_pager() + + # LESS env is not always set and we need -R to display escape sequences properly + less_opts = os.getenv("LESS", default="") + if "-R" not in less_opts: + less_opts += " -R" + env["LESS"] = less_opts + + return shlex.split(pager), env + + def run_pager(message: Union[bytes, str], tmp_suffix: str = ""): from ..core import run_external @@ -185,16 +206,29 @@ def run_pager(message: Union[bytes, str], tmp_suffix: str = ""): safe_write(tmpfile, message) tmpfile.flush() - env = os.environ.copy() - - pager = os.getenv("PAGER", default="").strip() - pager = pager or get_default_pager() - - # LESS env is not always set and we need -R to display escape sequences properly - less_opts = os.getenv("LESS", default="") - if "-R" not in less_opts: - less_opts += " -R" - env["LESS"] = less_opts - - cmd = shlex.split(pager) + [tmpfile.name] + pager, env = get_pager() + cmd = pager + [tmpfile.name] run_external(*cmd, env=env) + + +def pipe_to_pager(lines: Union[List[bytes], List[str]], *, add_newlines=False): + """ + Pipe ``lines`` to the pager. + If running in a non-interactive terminal, print the data instead. + Add a newline after each line if ``add_newlines`` is ``True``. + """ + if not tty.IS_INTERACTIVE: + for line in lines: + safe_write(sys.stdout, line, add_newline=add_newlines) + return + + pager, env = get_pager() + with subprocess.Popen(pager, stdin=subprocess.PIPE, encoding="utf-8", env=env) as proc: + try: + for line in lines: + safe_write(proc.stdin, line, add_newline=add_newlines) + proc.stdin.flush() + proc.stdin.close() + except BrokenPipeError: + pass + proc.wait()