prompt
Version: 15.0.0
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
- Using the mixin inside a class
- Tests
- Architecture
- Integration in the Suite
- Dependencies
- Versioning & Compatibility Policy
- License
- About us
- Known Limitations & Roadmap
- Status & Compatibility
- Useful Links
- Migration Notes
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 activeAbstractPromptOutputHandler(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 viashutil.get_terminal_size(), refreshed on SIGWINCH whenenable_resize_listening()has been called
print_response() is the single gate every response passes through:
- Stamps
response.created_at(if absent). - Appends to the top capture buffer when the recorder stack is active.
- Returns early without rendering when
response.verbosity == VerbosityLevel.QUIET. - 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 builtcolorized— whether ANSI codes are emittedverbosity— upper bound; responses with a higher verbosity level are suppressedbordered— passed to nested renders (e.g.FramePromptResponsesets this toFalsefor its inner context to prevent double-bordering)parent_context— linked list used byWithIoManagerto 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"):
InfoPromptResponseManagerMixin.info()callsInfoPromptResponse.create_info(message, …), which callsAbstractMessageResponse._create_symbol_message(), which callsPromptResponseLine.create_from_string()→parse_style_markup()→ buildsPromptResponseSegmentobjects.- The constructed
InfoPromptResponseis passed toIoManager.print_response(response, context). print_responsestampscreated_at, appends to the recorder stack (if active), checksVerbosityLevel.QUIET, then callsself.output.print(response, self.create_context(context)).PromptStdoutOutputHandler.print()callsresponse.render(context), which iteratesPromptResponseLine.render(context)→PromptResponseSegment.render(context, remaining_width)→ColorManager.colorize()→ ANSI string.- The joined string is written to
sys.stdout.
For self.info("…") on a WithIoMethods subclass:
__getattr__("info")resolves to a wrapper aroundself.io.info.- The wrapper injects
context=self.create_io_context()(which includes the class's indentation level), then callsself.io.info(…, context=…). - 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.
Related Packages
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: >=19.1.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
Useful Links
- Homepage: https://github.com/wexample/python-prompt
- Documentation: docs.wexample.com
- Issue Tracker: https://github.com/wexample/python-prompt/issues
- Discussions: https://github.com/wexample/python-prompt/discussions
- PyPI: pypi.org/project/wexample-prompt
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 15.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wexample_prompt-15.0.0.tar.gz | 155.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wexample_prompt-15.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 372.1 kB
Release files / wexample_prompt-15.0.0.tar.gz
| Download URL | wexample_prompt-15.0.0.tar.gz |
|---|---|
| Size | 155.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d5934605fedf9175af2fbdee9a3260182f34bda219ddab34ab9328b539f771b
|
|
BLAKE2b-256 checksum How to use checksums |
3d1869e9a22164e7671b0c0d1633285e33299844bf2cc1a2cd0235a95acef482
|
| 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-138-generic
|
Release files / wexample_prompt-15.0.0-py3-none-any.whl
| Download URL | wexample_prompt-15.0.0-py3-none-any.whl |
|---|---|
| Size | 217.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7450ee34059964c831dc479270ea22e649ac87cf46571ac2a7e2cde2f33127c1
|
|
BLAKE2b-256 checksum How to use checksums |
71dab775b4e61c7688aee91dfe4507583ac57e7d21bca6f49fe9737a83e127ca
|
| 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-138-generic
|