from __future__ import annotations
from asyncio.events import AbstractEventLoop
from typing import TYPE_CHECKING, Any, TextIO
from prompt_toolkit.application import Application
from prompt_toolkit.application.current import get_app_or_none, get_app_session
from prompt_toolkit.application.run_in_terminal import run_in_terminal
from prompt_toolkit.formatted_text import (
FormattedText,
StyleAndTextTuples,
to_formatted_text,
)
from prompt_toolkit.input import DummyInput
from prompt_toolkit.layout import Layout
from prompt_toolkit.output import ColorDepth, Output
from prompt_toolkit.output.defaults import create_output
from prompt_toolkit.renderer import (
print_formatted_text as renderer_print_formatted_text,
)
from prompt_toolkit.styles import (
BaseStyle,
StyleTransformation,
default_pygments_style,
default_ui_style,
merge_styles,
)
if TYPE_CHECKING:
from prompt_toolkit.layout.containers import AnyContainer
__all__ = [
"print_formatted_text",
"print_container",
"clear",
"set_title",
"clear_title",
]
def print_formatted_text(
*values: Any,
sep: str = " ",
end: str = "\n",
file: TextIO | None = None,
flush: bool = False,
style: BaseStyle | None = None,
output: Output | None = None,
color_depth: ColorDepth | None = None,
style_transformation: StyleTransformation | None = None,
include_default_pygments_style: bool = True,
) -> None:
"""
::
print_formatted_text(*values, sep=' ', end='\\n', file=None, flush=False, style=None, output=None)
Print text to stdout. This is supposed to be compatible with Python's print
function, but supports printing of formatted text. You can pass a
:class:`~prompt_toolkit.formatted_text.FormattedText`,
:class:`~prompt_toolkit.formatted_text.HTML` or
:class:`~prompt_toolkit.formatted_text.ANSI` object to print formatted
text.
* Print HTML as follows::
print_formatted_text(HTML('Some italic text This is red!'))
style = Style.from_dict({
'hello': '#ff0066',
'world': '#884444 italic',
})
print_formatted_text(HTML('Hello world!'), style=style)
* Print a list of (style_str, text) tuples in the given style to the
output. E.g.::
style = Style.from_dict({
'hello': '#ff0066',
'world': '#884444 italic',
})
fragments = FormattedText([
('class:hello', 'Hello'),
('class:world', 'World'),
])
print_formatted_text(fragments, style=style)
If you want to print a list of Pygments tokens, wrap it in
:class:`~prompt_toolkit.formatted_text.PygmentsTokens` to do the
conversion.
If a prompt_toolkit `Application` is currently running, this will always
print above the application or prompt (similar to `patch_stdout`). So,
`print_formatted_text` will erase the current application, print the text,
and render the application again.
:param values: Any kind of printable object, or formatted string.
:param sep: String inserted between values, default a space.
:param end: String appended after the last value, default a newline.
:param style: :class:`.Style` instance for the color scheme.
:param include_default_pygments_style: `bool`. Include the default Pygments
style when set to `True` (the default).
"""
assert not (output and file)
# Create Output object.
if output is None:
if file:
output = create_output(stdout=file)
else:
output = get_app_session().output
assert isinstance(output, Output)
# Get color depth.
color_depth = color_depth or output.get_default_color_depth()
# Merges values.
def to_text(val: Any) -> StyleAndTextTuples:
# Normal lists which are not instances of `FormattedText` are
# considered plain text.
if isinstance(val, list) and not isinstance(val, FormattedText):
return to_formatted_text(f"{val}")
return to_formatted_text(val, auto_convert=True)
fragments = []
for i, value in enumerate(values):
fragments.extend(to_text(value))
if sep and i != len(values) - 1:
fragments.extend(to_text(sep))
fragments.extend(to_text(end))
# Print output.
def render() -> None:
assert isinstance(output, Output)
renderer_print_formatted_text(
output,
fragments,
_create_merged_style(
style, include_default_pygments_style=include_default_pygments_style
),
color_depth=color_depth,
style_transformation=style_transformation,
)
# Flush the output stream.
if flush:
output.flush()
# If an application is running, print above the app. This does not require
# `patch_stdout`.
loop: AbstractEventLoop | None = None
app = get_app_or_none()
if app is not None:
loop = app.loop
if loop is not None:
loop.call_soon_threadsafe(lambda: run_in_terminal(render))
else:
render()
def print_container(
container: AnyContainer,
file: TextIO | None = None,
style: BaseStyle | None = None,
include_default_pygments_style: bool = True,
) -> None:
"""
Print any layout to the output in a non-interactive way.
Example usage::
from prompt_toolkit.widgets import Frame, TextArea
print_container(
Frame(TextArea(text='Hello world!')))
"""
if file:
output = create_output(stdout=file)
else:
output = get_app_session().output
app: Application[None] = Application(
layout=Layout(container=container),
output=output,
# `DummyInput` will cause the application to terminate immediately.
input=DummyInput(),
style=_create_merged_style(
style, include_default_pygments_style=include_default_pygments_style
),
)
try:
app.run(in_thread=True)
except EOFError:
pass
def _create_merged_style(
style: BaseStyle | None, include_default_pygments_style: bool
) -> BaseStyle:
"""
Merge user defined style with built-in style.
"""
styles = [default_ui_style()]
if include_default_pygments_style:
styles.append(default_pygments_style())
if style:
styles.append(style)
return merge_styles(styles)
def clear() -> None:
"""
Clear the screen.
"""
output = get_app_session().output
output.erase_screen()
output.cursor_goto(0, 0)
output.flush()
def set_title(text: str) -> None:
"""
Set the terminal title.
"""
output = get_app_session().output
output.set_title(text)
def clear_title() -> None:
"""
Erase the current title.
"""
set_title("")