cli
Version: 2.1.5
wexample-cli supplies the decorator layer — @command, @option, and @middleware — that turns plain Python methods into typed, discoverable commands for kernels built on wexample-app. Options are declared with Python types, short names, defaults, required flags, and per-value validators; a composable middleware pipeline can fan a single invocation across multiple execution contexts, run them in parallel, and abort on the first failure. It is aimed at Python developers building or extending a wexample-app kernel with new CLI commands.
Table of Contents
- Installation
- Quickstart
- Tests
- Architecture
- Integration in the Suite
- Dependencies
- Versioning & Compatibility Policy
- License
- About us
- Known Limitations & Roadmap
- Status & Compatibility
- Useful Links
- Migration Notes
Installation
pip install wexample-cli
Requires Python >=3.10.
Quickstart
Install the package:
pip install wexample-cli
Declare a command inside your kernel class. @command wraps the method into a CommandMethodWrapper; each @option stacks on top in outer-to-inner order to append a typed option to it.
from wexample_cli.decorator.command import command
from wexample_cli.decorator.option import option
@option(name="name", type=str, description="Who to greet", required=True)
@command(type="demo", description="Print a greeting")
def demo__greet(context, name: str) -> None:
context.io.log(f"Hello, {name}!")
The decorated object is a CommandMethodWrapper. You can inspect it immediately:
demo__greet.description # "Print a greeting"
demo__greet.options[0].name # "name"
The kernel maps the function name demo__greet to the command path demo/greet. To exercise the full decorator and middleware pipeline in a test, use the helpers in src/wexample_cli/testing/kernel.py:
from wexample_cli.testing.kernel import boot_kernel, dispatch_command
kernel = boot_kernel(MyKernel, entrypoint_path="/path/to/__main__.py")
response = dispatch_command(kernel, "demo/greet", {"name": "world"})
dispatch_command accepts pre-parsed kwargs ({"name": "world"}) or CLI-style strings (["--name", "world"]).
Tests
This project uses pytest for testing and pytest-cov for code coverage analysis.
Installation
First, install the required testing dependencies:
.venv/bin/python -m pip install pytest pytest-cov
Basic Usage
Run all tests with coverage:
.venv/bin/python -m pytest --cov --cov-report=html
Common Commands
# Run tests with coverage for a specific module
.venv/bin/python -m pytest --cov=your_module
# Show which lines are not covered
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing
# Generate an HTML coverage report
.venv/bin/python -m pytest --cov=your_module --cov-report=html
# Combine terminal and HTML reports
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html
# Run specific test file with coverage
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
Viewing HTML Reports
After generating an HTML report, open htmlcov/index.html in your browser to view detailed line-by-line coverage information.
Coverage Threshold
To enforce a minimum coverage percentage:
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
This will cause the test suite to fail if coverage drops below 80%.
Architecture
wexample-cli is a decorator and dispatch layer that sits between a wexample-app kernel and the Python functions that implement its commands. It has no entry-point of its own; downstream kernels import its decorators, wrap their methods, and pass the resulting objects to the kernel's registration machinery.
Parts
Decorators — src/wexample_cli/decorator/
The decorators are the only thing a command author touches directly. Applied bottom-to-top on a plain function, they produce a CommandMethodWrapper:
@command(type, description, tags)— creates the wrapper; must be the innermost decorator.@option(name, type, ...)— callswrapper.set_option(Option(...))to append a typed option.@middleware(name, **kwargs)— callswrapper.register_middleware(name, kwargs)to schedule a middleware class at runtime.@alias(*names)— extendswrapper.aliases.@screenable(interval, height)— adds--screenand--screen-intervaloptions and pushes a pipeline wrapper that drives a refresh loop.@as_sudo()— setswrapper.sudo = True.@webhook()— setswrapper.webhook = True.@option_stop_on_failure()— adds a--stop-on-failureflag option.
CommandMethodWrapper — src/wexample_cli/common/command_method_wrapper.py
The data structure produced by decoration. It carries everything the runtime needs: function, options, middlewares_attributes (names → init-kwargs, resolved at call time), pipeline_wrappers (registered in outer-to-inner order), aliases, tags, and the boolean flags sudo and webhook. It owns no execution logic.
ExtendedCommand — src/wexample_cli/command/extended_command.py
The runtime command object registered with the kernel; extends wexample_app.common.command.Command. It owns the full execution path for one CommandRequest.
AbstractMiddleware — src/wexample_cli/middleware/abstract_middleware.py
Base class for middleware. A middleware can add options dynamically (append_options), produce multiple ExecutionContext objects from a single request (build_execution_contexts), and declare whether execution should be parallel, show a progress bar, or abort on the first failure. The values "allways" and "optional" (defined in src/wexample_cli/const/middleware.py) allow a middleware to make a behaviour mandatory or user-selectable.
ExecutionContext — src/wexample_cli/context/execution_context.py
A single unit of work inside one dispatch. Holds function_kwargs, request, kernel, middleware, and an optional function override. Its __attrs_post_init__ injects context=self into function_kwargs so every command function receives it. Also provides progress-tracking helpers (create_progress_range, finish_progress, get_or_create_progress).
Exceptions — src/wexample_cli/exception/
CommandOptionMissingException and CommandOptionValidationException both extend AbstractCommandOptionException. The first is raised when a required option is absent; the second when an AbstractValidator rejects a value.
Constants — src/wexample_cli/const/
types.py—ParsedArgs = dict[str, Any].middleware.py— the"allways"/"optional"sentinel strings.tags.py—EffectTag,AudienceTag, andScopeTagclass-level string constants for@command(tags=[...]).
Testing helpers — src/wexample_cli/testing/kernel.py
boot_kernel(KernelClass, entrypoint_path) and dispatch_command(kernel, name, arguments) exercise the full decorator and dispatch pipeline from a test, identical to a real invocation.
Helper — src/wexample_cli/helper/extra_args.py
resolve_shell_command(context, command, extra_args) — resolves a shell command string from either a --command "..." option or positional -- args, preferring the positional form.
Call path
At import time (decorators run):
@alias / @as_sudo / @webhook
@screenable → set_option(screen, screen_interval) + register_pipeline_wrapper(screen_wrapper)
@middleware(name) → register_middleware(name, kwargs)
@option(...) → set_option(Option(...))
@command(type, ...) → CommandMethodWrapper(function=fn, ...)
The resulting CommandMethodWrapper is what the kernel stores; ExtendedCommand wraps it.
At invocation (ExtendedCommand.execute_request):
- Instantiate each middleware from the kernel's
"middlewares"registry usingmiddlewares_attributes; attach it withset_middleware(which also extendswrapper.optionswith the middleware's own options). - If
--helpor-his in the raw arguments, render help via_render_helpand return. _build_function_kwargs:- Each middleware calls
append_optionsto inject any dynamic options (e.g.--parallel,--progress). - Raw arguments (list or dict) are parsed by
_parse_argumentsintoParsedArgs. - Each declared option is resolved from parsed args, then its default, then
CommandOptionMissingExceptionif required. - Each non-
Nonevalue is run through its validators; failure raisesCommandOptionValidationException. - If
--passthrough tokens are present, they land inparsed_args["__extra_args__"]and are forwarded asextra_argsif the function declares that parameter, otherwiseCommandUnexpectedArgumentException.
- Each middleware calls
- Build the pipeline:
_make_dispatchproduces the innermost callable; each entry inpipeline_wrappersis composed around it with_wrap_dispatch. - Call
dispatch(function_kwargs).
Inside _execute_dispatch:
- With middlewares: each middleware calls
build_execution_contexts(base returns one context; subclasses may fan out). Contexts are executed sequentially or in parallel (asyncio+ThreadPoolExecutor). Parallel runs each context with aPromptBufferOutputHandlerso output is buffered per-context. Results accumulate in aMultipleResponse; a single result is unwrapped.stop_on_failurehalts the loop early onFailureResponse. - Without middlewares: a single
ExecutionContextis built byrequest.resolver.build_execution_context, and the function is called directly.
In both paths, ExecutionContext.__attrs_post_init__ injects context=self so the function can access context.io, context.kernel, progress handles, and the resolved function_kwargs.
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
- wexample-app: >=19.3.0
- wexample-helpers: >=20.2.0
- wexample-prompt: >=16.0.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-cli
- Documentation: docs.wexample.com
- Issue Tracker: https://github.com/wexample/python-cli/issues
- Discussions: https://github.com/wexample/python-cli/discussions
- PyPI: pypi.org/project/wexample-cli
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-cli 2.1.5
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_cli-2.1.5.tar.gz | 24.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wexample_cli-2.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.6 kB
Release files / wexample_cli-2.1.5.tar.gz
| Download URL | wexample_cli-2.1.5.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bb81171a061360c728f35593810b2ce0a1edba2b4d1bd399bbbfb510b87d2021
|
|
BLAKE2b-256 checksum How to use checksums |
85fc5a6eb089f48f5a8a9750dcc305ff4efc1781db756593294f25e1b319ea42
|
| 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_cli-2.1.5-py3-none-any.whl
| Download URL | wexample_cli-2.1.5-py3-none-any.whl |
|---|---|
| Size | 27.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cec245327b2dd0a8be55c3f185ba3acc32bf5cacda432e0ec07ee3591b1454b9
|
|
BLAKE2b-256 checksum How to use checksums |
d09ebb950b36c4c09f5bab27dd81b980028ece4903876121dbe549fbda4edcba
|
| 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
|