Print formatted text to file¶
Printing formatted text to file or terminal while respecting terminal width or other settings.
While working with CLIs, it is important to separate user-facing messages from program’s output.
For most use cases, this is easy: user-facing messages go to yuio.io, which
sends them to stderr, while program output goes to print()
or sys.stdout.
This way, users can redirect stdout to a file while still seeing stderr
in their terminal.
Complications arise when we want to print formatted content as program’s output: we have to face several scenarios:
User wants to see program’s output in the terminal:
$ appIn this case, we want to format it according to the terminal’s capabilities.
User piped program’s output to a pager:
$ app | less -R
In this case, we’d also like to format it according to the terminal’s capabilities. However, we can’t know output’s destination: will it end up in a terminal, some text processing utility, or a file?
User redirected output to a file:
$ app > output.txt
From our app’s perspective, this case looks exactly like case #2: all we know is that output is redirected, but we don’t know where it goes.
Our program has a distinct option for specifying output:
$ app -o output.txt
In this case, we need to format output for a file, choosing reasonable text width and other settings.
Here’s how Yuio handles these scenarios, and how we can control its default behavior.
Case 1: output goes to a terminal¶
In this case, we can use yuio.io to send output to stdout:
result = ...
yuio.io.info(result, to_stdout=True)
Here, result will be automatically formatted according
to terminal’s capabilities.
Cases 2 and 3: output is redirected¶
When user redirects program’s output, Yuio assumes that it goes to a file. Colors are
disabled, and formatting width is set to
Theme.fallback_width.
If user wants to treat output as a terminal, they can enable colors by setting
environment variable FORCE_COLOR or giving flag --color:
$ app --color | less -R
Case 4: dedicated option for output file¶
If our program has a distinct option for specifying output, we need to set up
MessageChannel in order to format output.
Here’s how we can do this:
import contextlib
import pathlib
import yuio.app
import yuio.io
import yuio.term
@yuio.app.app
def main(
#: Where to print program's output.
output: pathlib.Path | None = yuio.app.field(
default=None,
flags=["-o", "--output"],
),
):
with contextlib.ExitStack() as closer:
if output is None:
# If output file is not given, print to stdout.
ch = yuio.io.MessageChannel(to_stdout=True)
else:
# Otherwise, print to output.
output_stream = closer.enter_context(open(output, "w", encoding="utf-8"))
ch = yuio.io.MessageChannel(
# We can create `MessageChannel` for a file.
# Color support will be disabled.
term=yuio.term.get_term_from_stream(output_stream),
# We can also override formatting width and other options.
width=120,
)
# Some result that goes to file or to `stdout`.
result = yuio.io.Stack(
yuio.io.Hr(),
yuio.io.Format("Output = %#r", output),
yuio.io.Format("Term = %#+r", ch.make_repr_context().term),
yuio.io.Hr(),
)
# Print user-facing message to `stderr`.
yuio.io.success("Result is ready!")
# Print program result to file or to `stdout`.
ch.info(result)
if __name__ == "__main__":
main.run()