Skip to main content

prompt

Version: 16.0.1

wexample-prompt renders structured terminal output and captures interactive input for Python CLI applications. It centres on an IoManager that any class can own or share, producing typed responses — log, info, success, title, table, list, tree, choice, confirm, and more — each honouring the current indentation level and verbosity. Python developers building applications that need consistent, nestable terminal I/O across a hierarchy of objects are its intended audience.

Table of Contents

Quickstart

Install from PyPI:

pip install wexample-prompt

The entry point is IoManager, defined in src/wexample_prompt/common/io_manager.py. Instantiate it once and call response methods directly:

from wexample_prompt.common.io_manager import IoManager

io = IoManager()
io.log("Starting")
io.info("Something happened")
io.success("All done")

Each call prints a styled line to stdout immediately and returns the response object.

Using the mixin inside a class

When output is spread across several classes, inherit WithIoMethods (src/wexample_prompt/mixin/with_io_methods.py) instead of holding an IoManager by hand. The mixin exposes every IoManager method directly on self and injects the correct indentation context automatically:

from wexample_helpers.decorator.base_class import base_class
from wexample_prompt.mixin.with_io_methods import WithIoMethods

@base_class
class Worker(WithIoMethods):
    def run(self):
        self.log("Starting")
        self.success("All done")

Worker().run()

ensure_io_manager() is called lazily on the first method access, so no explicit setup is needed. To share one manager across a parent–child hierarchy, pass the parent when constructing the child:

@base_class
class Child(WithIoMethods):
    def run(self):
        self.log("nested message")   # printed one indentation level deeper

worker = Worker()
child = Child(parent_io_handler=worker)
child.run()

Tests

Run the test suite:

pytest tests/

With coverage:

pytest --cov=prompt tests/

Architecture

wexample-prompt is organised as a layered pipeline: callers talk to IoManager, which builds a typed response object, passes it through a PromptContext to a render chain, then hands the result to an output handler. Every layer is independently replaceable.


IoManager

src/wexample_prompt/common/io_manager.py is the single public surface for callers. It is assembled at class definition time through multiple inheritance: each response category contributes one *ManagerMixin that declares one method (info, title, choice, …). IoManager itself owns:

  • output — the active AbstractPromptOutputHandler (stdout by default)
  • default_context_verbosity / default_response_verbosity — fall-through values when callers omit verbosity
  • _recorder_stack — a LIFO list of capture buffers; push_recorder() / pop_recorder() let callers collect every response emitted inside a block
  • _terminal_width — lazily populated via shutil.get_terminal_size(), refreshed on SIGWINCH when enable_resize_listening() has been called

print_response() is the single gate every response passes through:

  1. Stamps response.created_at (if absent).
  2. Appends to the top capture buffer when the recorder stack is active.
  3. Returns early without rendering when response.verbosity == VerbosityLevel.QUIET.
  4. Calls self.output.print(response, self.create_context(context)).

Mixin layer

src/wexample_prompt/mixin/response/messages/info_prompt_response_manager_mixin.py is a representative example. Every *ManagerMixin follows the same three-step body:

response = XxxPromptResponse.create_xxx(message=message, verbosity=..., ...)
return self.print_response(
    response=response,
    context=XxxPromptResponse.rebuild_context_for_kwargs(context=context, parent_kwargs=kwargs),
    frame=frame,
)

The mixins live under src/wexample_prompt/mixin/response/, grouped by category (messages, titles, data, interactive). They carry no state and no rendering logic — all they do is construct the response and call print_response.


Response classes

src/wexample_prompt/responses/abstract_prompt_response.py is the base for every response. It holds lines: list[PromptResponseLine], an optional verbosity, and a created_at timestamp. Its render(context) method joins line.render(context) across all lines and returns the final string (or None when verbosity filtering suppresses it).

Concrete classes are grouped in subdirectories of src/wexample_prompt/responses/:

Directory Examples
messages/ InfoPromptResponse, SuccessPromptResponse, ErrorPromptResponse, …
titles/ TitlePromptResponse, SubtitlePromptResponse, SeparatorPromptResponse
data/ ListPromptResponse, TablePromptResponse, TreePromptResponse, …
interactive/ ChoicePromptResponse, InputPromptResponse, SpinnerPromptResponse, …
(root) EchoPromptResponse, LogPromptResponse, FramePromptResponse, CodePromptResponse

AbstractMessageResponse (src/wexample_prompt/responses/messages/abstract_message_response.py) adds a class-level SYMBOL prepended to the first segment and implements apply_prefix_to_kwargs so that WithIoMethods subclasses can inject a class prefix without double-prefixing the symbol.

AbstractInteractivePromptResponse (src/wexample_prompt/responses/interactive/abstract_interactive_prompt_response.py) wraps the render loop with terminal-height tracking to support cursor rewinding (_partial_clear), and stores the user's answer in _answer.

FramePromptResponse (src/wexample_prompt/responses/frame_prompt_response.py) overrides render() entirely: it renders its child responses list into an inner context, then wraps every resulting line in ╭─…─╮ / │…│ / ╰─…─╯ borders. IoManager.print_response() also accepts a frame= shortcut that wraps any response in a FramePromptResponse before printing.


Rendering pipeline

A call to response.render(context) descends through three levels:

AbstractPromptResponse.render(context)
  └─ for line in self.lines:
       PromptResponseLine.render(context)   ← applies indentation, wraps to context.width
         └─ for seg in self.segments:
              PromptResponseSegment.render(context, line_remaining_width)
                └─ ColorManager.colorize(text, color, styles)   ← emits ANSI codes

PromptResponseLine (src/wexample_prompt/common/prompt_response_line.py) prepends the indentation string produced by context.render_indentation(). When context.width is set and context.formatting is not False, it wraps segments to fit the available visible width, starting a new indentation + content line whenever the remaining width is exhausted.

PromptResponseSegment (src/wexample_prompt/common/prompt_response_segment.py) holds a single span of raw text with an optional TerminalColor and a list of TextStyle values. Its render() splits the text at the remaining width boundary and returns (rendered_fit, remainder_segment | None) so the line can continue wrapping.

ColorManager (src/wexample_prompt/common/color_manager.py) translates TerminalColor, TerminalBgColor, and TextStyle enums into ANSI prefix strings (via colorama), concatenates them before the text, and appends \033[0m.


Style markup parser

src/wexample_prompt/common/style_markup_parser.py converts inline markup such as @blue+bold{word} or @color:red{text} embedded in a string into lists of PromptResponseSegments, one list per logical line. It is called by PromptResponseLine.create_from_string() so callers can pass a plain string to any response factory without manually constructing segments. Special directive types @path{…} and @time{…} emit clickable paths and formatted timestamps rather than color segments.


Output handlers

src/wexample_prompt/output/abstract_prompt_output_handler.py declares two abstract methods: print(response, context) and erase(response).

PromptStdoutOutputHandler (src/wexample_prompt/output/prompt_stdout_output_handler.py) calls response.render(context), writes the result to sys.stdout, and implements erase() with ANSI cursor-up sequences computed from response.rendered_content.

PromptBufferOutputHandler (src/wexample_prompt/output/prompt_buffer_output_handler.py) stores both the original response objects (responses) and their rendered strings (rendered). flush() clears both lists and returns the rendered strings. Used in tests and anywhere callers want to capture output without touching the terminal.


PromptContext

src/wexample_prompt/common/prompt_context.py is a plain value object (not mutated during rendering). It carries:

  • width — effective terminal width at the point of print (in visible characters)
  • indentation / indentation_length / indentation_character / indentation_style — how the indentation prefix is built
  • colorized — whether ANSI codes are emitted
  • verbosity — upper bound; responses with a higher verbosity level are suppressed
  • bordered — passed to nested renders (e.g. FramePromptResponse sets this to False for its inner context to prevent double-bordering)
  • parent_context — linked list used by WithIoManager to chain contexts across object hierarchies

IoManager.create_context(context) merges caller-provided context with the manager's own indentation level and verbosity defaults. A child class that received parent_io_handler gets one extra indentation level automatically.


Application integration

WithIoManager (src/wexample_prompt/mixin/with_io_manager.py) gives any class the ability to own or share an IoManager. ensure_io_manager() lazily creates one (or inherits it from parent_io_handler). create_io_context() builds a PromptContext from the class's current indentation, color, and verbosity settings, chained to the parent's context when present.

WithIoMethods (src/wexample_prompt/mixin/with_io_methods.py) extends WithIoManager with __getattr__: any unknown attribute that matches a method name on IoManager is returned as a wrapper function that injects context=self.create_io_context(). This lets subclasses call self.info(…), self.title(…), etc., without holding a reference to self.io.

WithIndentation (src/wexample_prompt/mixin/with_indentation.py) is mixed into IoManager to maintain a manager-level indentation counter (indentation_up() / indentation_down()). create_context() adds this to any per-call context before passing it to print_response.


Complete call path

For io.info("Something happened"):

  1. InfoPromptResponseManagerMixin.info() calls InfoPromptResponse.create_info(message, …), which calls AbstractMessageResponse._create_symbol_message(), which calls PromptResponseLine.create_from_string() → parse_style_markup() → builds PromptResponseSegment objects.
  2. The constructed InfoPromptResponse is passed to IoManager.print_response(response, context).
  3. print_response stamps created_at, appends to the recorder stack (if active), checks VerbosityLevel.QUIET, then calls self.output.print(response, self.create_context(context)).
  4. PromptStdoutOutputHandler.print() calls response.render(context), which iterates PromptResponseLine.render(context) → PromptResponseSegment.render(context, remaining_width) → ColorManager.colorize() → ANSI string.
  5. The joined string is written to sys.stdout.

For self.info("…") on a WithIoMethods subclass:

  1. __getattr__("info") resolves to a wrapper around self.io.info.
  2. The wrapper injects context=self.create_io_context() (which includes the class's indentation level), then calls self.io.info(…, context=…).
  3. From step 1 of the path above.

Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the Wexample Suite documentation for the complete package ecosystem.

Dependencies

  • attrs: >=23.1.0
  • cattrs: >=23.1.0
  • colorama:
  • readchar:
  • wcwidth:
  • wexample-helpers: >=20.2.0

Versioning & Compatibility Policy

Wexample packages follow Semantic Versioning (SemVer):

  • MAJOR: Breaking changes
  • MINOR: New features, backward compatible
  • PATCH: Bug fixes, backward compatible

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Free to use in both personal and commercial projects.

About us

Wexample stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

Known Limitations & Roadmap

Current limitations and planned features are tracked in the GitHub issues.

See the project roadmap for upcoming features and improvements.

Status & Compatibility

Maturity: Production-ready

Python Support: >=3.10

OS Support: Linux, macOS, Windows

Status: Actively maintained

Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.

Metadata

Release files for wexample-prompt 16.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wexample-prompt 16.0.1
File Size Uploaded
wexample_prompt-16.0.1.tar.gz 156.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wexample-prompt 16.0.1
File Interpreter ABI Platform
wexample_prompt-16.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 373.8 kB

Release files / wexample_prompt-16.0.1.tar.gz

Download URL wexample_prompt-16.0.1.tar.gz
Size 156.4 kB
Tags Source
SHA-256 checksum
How to use checksums
4687da6a0c3a82349092ad9a7b3fc1dbc1b78cc573c7e1e604f2ac6c067b1f31
BLAKE2b-256 checksum
How to use checksums
19cab74a6cde408020e41c265a84394564e8f1a458335c858952073dc8b9cbac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.25.9 CPython/3.12.3 Linux/6.8.0-139-generic

Release files / wexample_prompt-16.0.1-py3-none-any.whl

Download URL wexample_prompt-16.0.1-py3-none-any.whl
Size 217.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e6297e5fec163681627646e88a6534949497022a352f2759674bd7e56bbe4f9f
BLAKE2b-256 checksum
How to use checksums
e036415ff9a5eebef8f260a0a49d4582b7cabd5ea45d45d345ec2292d43b3a55
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.25.9 CPython/3.12.3 Linux/6.8.0-139-generic

Release history Release notifications | RSS feed

16.0.3

2 release files

This release

16.0.1 This release

2 release files

15.1.0

2 release files

15.0.0

2 release files

14.1.0

2 release files

13.4.2

2 release files

13.4.1

2 release files

13.4.0

2 release files

13.3.0

2 release files

13.2.0

2 release files

13.1.3

2 release files

13.1.2

2 release files

13.1.1

2 release files

13.1.0

2 release files

13.0.0

2 release files

9.2.0

2 release files

9.1.0

2 release files

9.0.2

2 release files

9.0.1

2 release files

8.0.0

2 release files

6.1.2

2 release files

6.1.1

2 release files

6.1.0

2 release files

6.0.24

2 release files

6.0.23

2 release files

6.0.22

2 release files

2.0.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.98

2 release files

0.0.94

2 release files

0.0.93

2 release files

0.0.56

2 release files

0.0.52

2 release files

0.0.51

2 release files

0.0.47

2 release files

0.0.44

2 release files

0.0.43

2 release files

0.0.42

2 release files

0.0.41

2 release files

0.0.40

2 release files

0.0.39

2 release files

0.0.34

2 release files

0.0.33

2 release files

0.0.32

2 release files

0.0.31

2 release files

0.0.21

2 release files

0.0.20

2 release files

0.0.19

2 release files

0.0.18

2 release files

0.0.17

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page