Coverage for src / gitq / output.py: 100%
46 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 15:32 -0400
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 15:32 -0400
1import sys
2import os
3import re
4import shlex
5from contextlib import contextmanager
6from typing import ContextManager, List, Any
7from contextvars import ContextVar
10class Output:
12 indentation: ContextVar[int] = ContextVar(
13 "indentation", default=int(os.getenv("GITQ_INDENT", "0"))
14 )
16 @classmethod
17 def indent(cls) -> ContextManager:
18 return cls.heading()
20 @classmethod
21 @contextmanager
22 def heading(cls, message: str | None = None):
23 n = cls.indentation.get() + 1
24 if message is not None:
25 print(" " * (n - 1) + "#" * n, message)
26 sys.stdout.flush()
27 token = cls.indentation.set(n)
28 try:
29 os.environ["GITQ_INDENT"] = str(n)
30 yield
31 finally:
32 cls.indentation.reset(token)
33 n = cls.indentation.get()
34 if n == 0:
35 del os.environ["GITQ_INDENT"]
36 else:
37 os.environ["GITQ_INDENT"] = str(n)
39 @classmethod
40 def log_cmd(cls, cmd: List | str, comment: str = ""):
41 def quote(x):
42 return shlex.quote(re.sub(r"\n.*", "...", str(x), flags=re.DOTALL))
44 if not isinstance(cmd, str):
45 cmd = " ".join(map(quote, cmd))
46 if comment:
47 cmd += " # " + comment
48 print(" " * cls.indentation.get() + "+", cmd)
49 sys.stdout.flush()
51 @classmethod
52 def print(cls, *args: Any) -> None:
53 for line in " ".join(map(str, args)).splitlines():
54 print(" " * cls.indentation.get() + line)
56 @classmethod
57 def flush(cls):
58 sys.stdout.flush()
59 sys.stderr.flush()