wex_core
Version: 31.2.0
wexample-wex-core is the kernel and command-dispatch layer of the wex CLI: it resolves the four command namespaces (addon::group/name, .group/name, @addon::group/name, ~group/name), runs commands as Python functions or multi-step YAML scripts (bash, python, docker runners), and provides the AbstractAddonManager base that every higher-level package uses to register its own commands, resolvers, middlewares, and step guards. It is the shared foundation consumed by all other wex packages, not an end-user tool on its own.
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-wex-core
Requires Python >=3.10.
Quickstart
Install the package:
pip install wexample-wex-core
The snippet below builds a kernel, registers two addons, and executes the built-in demo::ping/pong command. It needs no pre-existing project on disk.
import tempfile
from pathlib import Path
from wexample_app.const.output import OUTPUT_TARGET_NONE
from wexample_wex_core.addons.core.core_addon_manager import CoreAddonManager
from wexample_wex_core.addons.demo.demo_addon_manager import DemoAddonManager
from wexample_wex_core.common.command_request import CommandRequest
from wexample_wex_core.common.kernel import Kernel
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
wex_dir = root / "wex"
wex_dir.mkdir()
(root / ".env.yml").write_text("APP_ENV: test\n")
kernel = Kernel(entrypoint_path=wex_dir)
kernel.setup(addons=[CoreAddonManager, DemoAddonManager])
request = CommandRequest(
kernel=kernel,
name="demo::ping/pong",
output_target=[OUTPUT_TARGET_NONE],
arguments={"type": "dict"},
)
response = kernel.execute_kernel_command(request)
print(response.content) # {'status': 'pong'}
Kernel(entrypoint_path=...) takes the .wex/ directory itself — a wex/ subdirectory of the project root. The .env.yml file lives one level up (at the project root) and must declare at least APP_ENV.
CoreAddonManager wires the built-in middlewares. DemoAddonManager registers the demo namespace, including ping/pong. A real application passes its own addon managers to setup() in place of or alongside these.
The CommandRequest fields that matter at call time:
| field | purpose |
|---|---|
name |
command address — addon::group/name, .group/name, @addon::group/name, or ~group/name |
output_target |
where output goes: OUTPUT_TARGET_NONE, OUTPUT_TARGET_STDOUT, or OUTPUT_TARGET_FILE |
arguments |
option values keyed by option name |
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-wex-core is the kernel and command-dispatch layer consumed by every other wex package. It owns four things: the Kernel that orchestrates startup and execution, the resolver chain that maps a command string to a file, the runner pair that executes that file, and the addon system that lets higher-level packages extend all of the above.
Source layout
src/wexample_wex_core/
common/ # Kernel, AbstractAddonManager, CommandRequest, CommandAddress, RegistryBuilder
registry/ # KernelRegistry — the in-memory command map
path/ # KernelRegistryFile — JSON persistence of the registry
resolver/ # AbstractCommandResolver, AddonCommandResolver, UserCommandResolver
runner/ # CorePythonCommandRunner, CoreYamlCommandRunner
yaml/ # YamlCommandDefinition, AbstractScriptRunner and its four runners
middleware/ # AbstractEachPathMiddleware, EachPathMiddleware, EachFileMiddleware, EachDirectoryMiddleware
decorator/ # @command, @option, @alias, @attach, @as_sudo — applied to command functions
context/ # ExecutionContext — passed to every command function
workdir/ # KernelWorkdir, AddonWorkdir — file-state wrappers for .wex directories
webhook/ # HTTP server, routing, token store, type resolvers
addons/ # CoreAddonManager, DemoAddonManager, DockerAddonManager, GitAddonManager, SystemAddonManager
const/ # globals.py — command patterns, separators, type names
exception/ # Typed exceptions for option and command errors
Kernel
src/wexample_wex_core/common/kernel.py is the root object. Kernel.setup() runs a fixed sequence of _init_* methods:
_init_local_env— loads.wex/local/env.ymlintoos.environ._init_command_line_kernel— parses core CLI flags (--output-target,--output-format,--indentation-level, …)._init_logging— attaches aWARNING-level stderr logger namedwex._init_addons— instantiates everyAbstractAddonManagerclass passed tosetup()._auto_detect_env— asks each addon for auto-detectable env vars (e.g. PATH entries) and persists found values back to.wex/local/env.yml._init_resolvers— buildsAddonCommandResolver,UserCommandResolver, and any resolver classes contributed by addons._init_runners— buildsCorePythonCommandRunnerandCoreYamlCommandRunner._init_middlewares— collects middleware classes from every addon._init_registry— reads or buildssrc/wexample_wex_core/path/kernel_registry_file.pyat.wex/tmp/registry.json. Live resolvers (user) are always re-scanned; addon resolver data is read from the cached file when present._init_script_runner_registry— registersbash,docker,exec, andpythonYAML step runners._init_step_guard_registry— collectsAbstractStepGuardsubclasses from addons.
execute_kernel_command(request) is the single dispatch entry point. It increments an execution-depth counter (used to gate sudo re-exec to depth 0), fires before attachment commands, delegates to the parent runner chain, and fires after / always_after attachments.
Command namespaces
Four patterns, defined in src/wexample_wex_core/const/globals.py:
| Pattern | Example | Resolver |
|---|---|---|
addon::group/name |
core::ping/hi |
AddonCommandResolver |
.group/name |
.install/local |
app resolver (contributed by host) |
@addon::group/name |
@nginx::status |
service resolver (contributed by host) |
~group/name |
~my-group/my-cmd |
UserCommandResolver |
Unqualified group/name (no prefix) is resolved by scanning all loaded addon workdirs for a unique match; if ambiguous, it raises. Aliases declared with @alias(…) are resolved first by searching the registry before the pattern match.
A CommandAddress (src/wexample_wex_core/common/command_address.py) is the canonical three-tuple (addon, group, name). It converts between every representation used internally: command string (core::ping/hi), Python function name (core__ping__hi), and file path (commands/ping/hi.py).
Resolvers
src/wexample_wex_core/resolver/abstract_command_resolver.py defines build_registry_data(), build_command_path(), build_command_function_name(), and is_live(). is_live() returns False for addon commands (cached in the registry file) and True for user commands (always scanned fresh).
src/wexample_wex_core/resolver/addon_command_resolver.py walks each addon's commands/ directory: for .py files it imports the module with importlib and reads the CommandMethodWrapper; for .yml files it reads the decorators: block. Both paths populate the same RegistryCommandData structure.
src/wexample_wex_core/resolver/user_command_resolver.py does the same scan against ~/.wex/commands/ and adds that directory to sys.path on first use.
Registry
src/wexample_wex_core/registry/kernel_registry.py holds _resolvers, a nested dict {resolver_key: {addon_name: {command_key: RegistryCommandData}}}. get_all_commands() flattens it into a single keyed dict and caches the result. serialize() serialises non-live resolvers to JSON; hydrate() restores from the same JSON. The file is managed by src/wexample_wex_core/path/kernel_registry_file.py, which extends JsonFile from wexample-filestate.
Running wex core::registry/build (src/wexample_wex_core/addons/core/commands/registry/build.py) forces a full re-scan, overwrites the file, and writes .wex/tmp/autocomplete.json for shell completion.
Runners
Two runners handle the two command file types:
CorePythonCommandRunner (src/wexample_wex_core/runner/core_python_command_runner.py) — calls the parent PythonCommandRunner._build_command_function() to import the module and fetch the decorated function, wraps it in an ExtendedCommand, and returns it. The function must be named {addon}__{group}__{name} and decorated with @command(type=COMMAND_TYPE_ADDON).
CoreYamlCommandRunner (src/wexample_wex_core/runner/core_yaml_command_runner.py) — reads and caches YamlCommandDefinition.from_path(), synthesises a CommandMethodWrapper around a closure that iterates over scripts:, and returns an ExtendedCommand. Before each step runs, ${VAR_NAME} placeholders are substituted from a merged dict of .wex/local/env.yml, os.environ, built-ins (PATH_CURRENT), and option values (uppercased). After substitution, StepGuardRegistry.should_skip_step() is checked; if the step has a command: key it dispatches a sub-request; otherwise it looks up the named script runner.
YAML step runners
All four implement src/wexample_wex_core/yaml/abstract_script_runner.py:
runner: key |
Class | What it executes |
|---|---|---|
bash |
BashScriptRunner |
bash -c <script> or bash <file> on the host |
python |
PythonScriptRunner |
inline Python via PythonScriptResponse (executed in-process) |
docker |
DockerScriptRunner |
docker exec --env CI=true <container> bash -c <script> |
exec |
ExecScriptRunner |
arbitrary interpreter list, e.g. [node, -e] + script |
DockerScriptRunner._resolve_service_name() iterates addons calling get_service_docker_container_name(service); the first non-None answer wins.
Addons
Every addon is a subclass of src/wexample_wex_core/common/abstract_addon_manager.py. At construction its workdir is set to the directory containing the addon manager file, so commands/ is always found relative to the package. The methods an addon can override:
get_command_resolver_classes()— add resolver types (e.g. an app or service resolver).get_middlewares_classes()— add middleware types.get_step_guard_classes()— addAbstractStepGuardsubclasses that can skip YAML steps.get_local_configurable_keys()— declare env vars the kernel should auto-detect.get_service_docker_container_name(service)— resolve a short Docker service name.get_webhook_resolvers()— register webhook type resolvers.
The five addons shipped in this package are CoreAddonManager, DemoAddonManager, DockerAddonManager, GitAddonManager, and SystemAddonManager. CoreAddonManager is the only one that contributes middlewares: EachPathMiddleware, EachFileMiddleware, and EachDirectoryMiddleware.
Middlewares
src/wexample_wex_core/middleware/abstract_each_path_middleware.py is a middleware base that expands a path option into multiple ExecutionContext objects — one per matching file or directory — before the command function is called. Subclasses override _should_process_item() and _should_explore_directory() to filter by type. Options recursive, expand_glob, recursion_limit, and should_exist are declared as public attrs.
Call path (Python command)
CLI argv
→ Kernel._build_command_requests_from_arguments() # parse name + options
→ Kernel.execute_kernel_command(CommandRequest)
→ _enforce_sudo_if_needed() # re-exec under sudo if @as_sudo
→ _execute_attached(request, "before") # before-hooks
→ AbstractKernel.execute_kernel_command()
→ AddonCommandResolver.supports() # regex match; alias/unqualified fallback
→ CorePythonCommandRunner.will_run() # check .py file exists
→ CorePythonCommandRunner.build_runnable_command()
→ import module, fetch CommandMethodWrapper
→ ExtendedCommand(kernel, wrapper)
→ ExtendedCommand.execute(context)
→ middleware chain → command function(context, **options)
→ _execute_attached(request, "after")
Call path (YAML command)
→ CoreYamlCommandRunner.will_run() # check .yml file exists
→ CoreYamlCommandRunner.build_runnable_command()
→ YamlCommandDefinition.from_path() # parse yaml once, cache
→ synthesise CommandMethodWrapper(_make_executor)
→ ExtendedCommand.execute()
→ _make_executor(**kwargs)
for step in scripts:
yaml_substitute_step(step, variables) # ${VAR} replacement
StepGuardRegistry.should_skip_step()
if "command": sub-request → execute_kernel_command()
else: ScriptRunner.run(step, variables) # bash / python / docker / exec
Webhook server
core::webhook/listen starts a ThreadingHTTPServer (src/wexample_wex_core/webhook/handler.py) on a configurable port. Incoming GET requests are validated by routing_is_allowed_route(), then matched to a route (/webhook/{type}/{path}, /health, /metrics). Token validation uses HMAC comparison. Matched requests spawn a wex core::webhook/exec --command-str … subprocess; synchronous calls wait for its output, async calls return immediately with the PID. Prometheus-format metrics are accumulated in-process across requests.
Key types
CommandRequest—(kernel, name, arguments, output_target, request_id). Thenamefield is rewritten in-place by resolvers when an alias or unqualified form is found.CommandAddress— frozen dataclass(addon, group, name)with conversion helpers.KernelRegistry— in-memory command map; serialisable to JSON.RegistryCommandData— typed dict:command,path,test,description,alias,attachments,options,tags,sudo,webhook.ExecutionContext— carrieskernel,request,command_wrapper,function_kwargs, andmiddlewareinto every command function.
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
- click:
- psutil: >=5.9
- wexample-app: >=19.3.0
- wexample-cli: >=2.1.0
- wexample-filestate-git: >=8.2.0
- wexample-filestate: >=17.5.0
- wexample-helpers-git: >=7.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-wex-core
- Documentation: docs.wexample.com
- Issue Tracker: https://github.com/wexample/python-wex-core/issues
- Discussions: https://github.com/wexample/python-wex-core/discussions
- PyPI: pypi.org/project/wexample-wex-core
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-wex-core 31.2.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_wex_core-31.2.0.tar.gz | 84.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wexample_wex_core-31.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 214.9 kB
Release files / wexample_wex_core-31.2.0.tar.gz
| Download URL | wexample_wex_core-31.2.0.tar.gz |
|---|---|
| Size | 84.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22fcebdaf9182561ba81a29ccbd15d18d103e0ddd9d459f31ce4f3832a044b5b
|
|
BLAKE2b-256 checksum How to use checksums |
211bc8287aa34d52f8120ba372b71797001e1e7d3269d892849f747c35d1a937
|
| 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-142-generic
|
Release files / wexample_wex_core-31.2.0-py3-none-any.whl
| Download URL | wexample_wex_core-31.2.0-py3-none-any.whl |
|---|---|
| Size | 130.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4a84eb18b5b47a85325fd22f6e997ac41bca46d9f105b635ad60a26eefb1d501
|
|
BLAKE2b-256 checksum How to use checksums |
00379075f1edf2371222fadc283aa50f70b0364352a60e1a00fc74f93b2a7e70
|
| 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-142-generic
|