wex_addon_app
Version: 33.1.0
wex_addon_app extends wex with Docker application management: service lifecycle commands, environment configuration, and the ManagedWorkdir base class that every managed project builds on. It installs a per-project .wex/bin/app-manager executable that runs each project's commands in an isolated subprocess, so suite-level operations can iterate over all packages without shared state leaking between them. It is aimed at developers who maintain multi-package Python suites and need a consistent, scriptable interface for building, running, and publishing Docker-backed applications.
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-addon-app
Requires Python >=3.10.
Quickstart
Install the package:
pip install wexample-wex-addon-app
From Python, the first real thing the package can do is detect whether a directory is a wex-managed application — one whose .wex/config.yml defines global.version:
from wexample_wex_addon_app.workdir.managed_workdir import ManagedWorkdir
print(ManagedWorkdir.is_app_workdir_path("/path/to/my-project"))
# True when .wex/config.yml defines global.version, False otherwise
Once a project is set up (.wex/bin/app-manager exists), every command the addon registers is available through that script. The simplest end-to-end call returns a JSON summary of the project:
.wex/bin/app-manager app::info/show
{"name": "my-app", "version": "1.0.0", "env": "local"}
app-manager is a thin shell wrapper that delegates to the wex binary with the current project's addon stack loaded. The addon provides it with the app:: command namespace — app::info/show, app::setup/install, app::app/restart, and the rest — so adding the package to a wex kernel is all that is needed to make those commands available.
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
The addon is built around four cooperating layers: a kernel-level addon manager, a workdir hierarchy, a middleware pipeline, and a pair of command resolvers. A thin Bash script acts as the per-project entrypoint and delegates every call to the wex binary.
Entrypoint
Each managed project contains a copy of src/wexample_wex_addon_app/resources/app-manager.sh written to .wex/bin/app-manager by the filestate rectifier. The script finds the wex binary (via $CORE_BIN or which wex) and forwards all arguments verbatim with exec "$CORE_BIN" "$@". The wex kernel then routes the command through the addon stack.
AppAddonManager
src/wexample_wex_addon_app/app_addon_manager.py is registered with the kernel as the addon for this package. It owns:
- Workdir instantiation.
create_app_workdir(path)reads.wex/config.ymlto confirm a valid app directory, then loads.wex/python/app_manager/app_workdir.pyif the project ships a customAppWorkdirclass, otherwise falls back toManagedWorkdir. Services contribute their ownchildrenconfiguration beforeworkdir.configure()is called. - Service registry.
get_app_services(workdir)always injects thedefaultservice first, then adds any service declared underservice:inconfig.yml. Service manifests supportextends:inheritance;get_service_inheritance_chain()resolves the full chain andget_service_manifest()deep-merges it. - Command resolvers.
get_command_resolver_classes()returnsAppCommandResolverandServiceCommandResolver. - Middleware classes.
get_middlewares_classes()registers all five middleware types described below.
Workdir hierarchy
The workdir classes live in src/wexample_wex_addon_app/workdir/ and form a single inheritance chain.
src/wexample_wex_addon_app/workdir/managed_workdir.py is the base for every managed project. It assembles the mixin stack (WithMigrationWorkdirMixin, WithRunnersRootMixin, WithAppConfigWorkdirMixin, WithSuiteTreeWorkdirMixin, WithReadmeWorkdirMixin, WithAppVersionWorkdirMixin, WithRuntimeConfigMixin, WithAppRegistryMixin, WithLocalDataMixin, Workdir). Key responsibilities: declaring the .wex/ directory tree via prepare_value(), invoking the app-manager subprocess through manager_run() / manager_run_command(), and managing Docker container naming.
src/wexample_wex_addon_app/workdir/repo_workdir.py extends ManagedWorkdir with the version lifecycle: bump() creates a version-x.y.z branch and writes the new version; release() orchestrates the full publish pipeline (test → library sync → bump → rectify → push → propagate → build → publish); classify_version_bump() inspects git changes to pick patch vs minor vs major.
src/wexample_wex_addon_app/workdir/code_base_workdir.py extends RepoWorkdir with git operations and dependency management: add_publication_tag(), commit_changes(), push_changes(), update_dependencies(), depends_from(). This is the workdir type used for individual publishable packages.
src/wexample_wex_addon_app/workdir/framework_packages_suite_workdir.py extends RepoWorkdir for a directory that aggregates multiple packages. get_packages() discovers child packages via find_all_by_type(); get_ordered_packages() returns them in topological dependency order. packages_execute_manager(command, arguments) iterates over every package path and calls ManagedWorkdir.manager_run_from_path() in each — so each package runs in its own subprocess with its own app-manager. packages_execute_function() resolves a Python function reference to a CLI command string first, then delegates to packages_execute_manager().
Workdir mixins
src/wexample_wex_addon_app/workdir/mixin/with_suite_tree_workdir_mixin.py walks the filesystem upward to locate a parent suite directory (one that declares package_suite: in its config), caches the result, and provides search_closest_in_suites_tree() / collect_stack_in_suites_tree() for config and env-parameter lookups that fall back up the tree.
src/wexample_wex_addon_app/workdir/mixin/with_app_config_workdir_mixin.py declares the abstract get_app_config_file() that language-specific subclasses implement to return their manifest file (e.g. pyproject.toml).
Middleware pipeline
All middleware lives in src/wexample_wex_addon_app/middleware/. They all extend AbstractMiddleware; a command selects one or more via @middleware(middleware=...) decorators.
src/wexample_wex_addon_app/middleware/app_middleware.py — the base. build_execution_contexts() pops app_path from the kwargs (defaulting to the kernel's call directory), calls AppAddonManager.create_app_workdir(), and injects the result as app_workdir. Also enforces config_requirements and env_requirements declared in the command's extra dict.
src/wexample_wex_addon_app/middleware/package_suite_middleware.py — extends AppMiddleware. Validates that the resolved workdir is a FrameworkPackageSuiteWorkdir; raises InvalidWorkdirTypeException (with a hint about the nearest suite path) when it is not and _fail_if_not_suite_workdir is True.
src/wexample_wex_addon_app/middleware/each_suite_package_middleware.py — extends PackageSuiteMiddleware. Adds --all-packages. When the flag is absent the command runs normally on the single workdir. When it is present, the middleware replaces the command function with a closure that calls suite_workdir.packages_execute_manager(command, arguments), iterating every package in its own subprocess.
src/wexample_wex_addon_app/middleware/suite_or_each_package_middleware.py — extends PackageSuiteMiddleware. Adds --all-packages, --packages-only, and --suite-only. Builds up to two execution contexts: one for the suite itself and one that iterates packages, depending on the flags.
src/wexample_wex_addon_app/middleware/code_base_middleware.py — restricts execution to workdirs that implement the CodeBaseWorkdir interface.
Command resolvers
src/wexample_wex_addon_app/resolver/app_command_resolver.py handles the .group/command address pattern. It walks up from the current working directory to find the nearest .wex/commands/ directory and maps the address to a file there. Commands of this type are local to the project, not to the addon package.
src/wexample_wex_addon_app/resolver/service_command_resolver.py handles @service::group/command. It scans services/<name>/commands/ directories inside every registered addon, building the registry from those files. When building an execution context it creates an AppService instance and injects it as service into the function kwargs. is_attachment_active() skips service-attached commands when the current project does not declare that service.
Services
src/wexample_wex_addon_app/service/app_service.py represents one active service. It holds the service name, the resolved service_dir path from the addon, the parsed manifest, and the owning workdir. get_runtime_contribution() resolves compose file paths (including env-specific overrides) and runtime.bind declarations from service.yml. A project can ship a custom app_service.py in its service directory; AppAddonManager.get_app_service() loads it dynamically.
Two services are bundled with this addon:
- src/wexample_wex_addon_app/services/default/service.yml — always injected first; provides the base Docker Compose configuration (network, restart policy, tty).
- src/wexample_wex_addon_app/services/proxy/service.yml — optional reverse proxy, tagged
proxyandnetwork.
Filestate integration
ManagedWorkdir.prepare_value() declares the full .wex/ directory tree as a filestate configuration. The SetupManagerOption (registered via src/wexample_wex_addon_app/filestate/options_provider/setup_manager_options_provider.py) handles setup_manager.auto_migrate: true in config, which triggers SetupManagerMigrationOperation during every rectify pass.
Guest mode
workdir: {guest: true} in config.yml tells wex it does not own the repository: it manages .wex/ and leaves everything else alone — for projects that follow conventions other than ours. ManagedWorkdir._is_guest() carries the flag, and each class contributing children outside .wex/ has to consult it, since only that class knows which of its own children sit outside. Enforcement cannot be centralised: prepare_value() runs base-first, each subclass appending after its super() call, so a filter placed in ManagedWorkdir would never see what the subclasses add. Honoured today by ManagedWorkdir (README, version.txt, root .gitignore, .iml, and the 777 permission sweep, which moves onto .wex/ rather than disappearing — containers still write there) and by CodeBaseWorkdir (.gitlab-ci.yml). Not honoured by the language workdirs of wex-addon-dev-python, -php, -javascript and -flutter, which still impose their source layout and formatting; a guest project typed with one of those will see its sources rewritten.
Publication pipeline
src/wexample_wex_addon_app/publication/strategy/abstract_publication_strategy.py defines prepare_commit() (called before the bump commit) and run_post_publish_pipeline() (called after the package reaches the registry). Two concrete strategies are shipped: BranchMergePublicationStrategy merges the version branch back to main; MainPushPublicationStrategy pushes directly from main. The active strategy is selected by AbstractPublicationStrategy.from_workdir(self) based on the project's configuration.
Call path for a single command
.wex/bin/app-manager app::info/show
→ wex binary (kernel)
→ AppCommandResolver.supports() matches app::info/show
→ loads commands/info/show.py → app__info__show function
→ @middleware(AppMiddleware) fires:
AppAddonManager.create_app_workdir(cwd)
reads .wex/config.yml
loads optional .wex/python/app_manager/app_workdir.py
assembles services, calls workdir.configure()
→ app__info__show(context, app_workdir=<ManagedWorkdir>)
Call path across all packages
.wex/bin/app-manager app::state/rectify --all-packages
→ EachSuitePackageMiddleware.build_execution_contexts()
resolves suite_workdir (FrameworkPackageSuiteWorkdir)
--all-packages present → replaces function with:
suite_workdir.packages_execute_manager(
"app::state/rectify", original_arguments
)
→ packages_execute_manager() iterates get_packages_paths():
for each package_path:
ManagedWorkdir.manager_run_from_path(
cmd=[".wex/bin/app-manager", "--subprocess",
"app::state/rectify", ...],
path=package_path
)
Each package subprocess runs its own isolated kernel with its own app-manager, so custom AppWorkdir classes and per-package dependencies do not leak across packages.
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
- jinja2: >=3.0
- tomlkit:
- wexample-migration: >=10.1.0
- wexample-runner: >=10.0.0
- wexample-wex-core: >=31.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
Useful Links
- Homepage: https://github.com/wexample/python-wex-addon-app
- Documentation: docs.wexample.com
- Issue Tracker: https://github.com/wexample/python-wex-addon-app/issues
- Discussions: https://github.com/wexample/python-wex-addon-app/discussions
- PyPI: pypi.org/project/wexample-wex-addon-app
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-addon-app 33.1.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_addon_app-33.1.0.tar.gz | 267.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wexample_wex_addon_app-33.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 697.4 kB
Release files / wexample_wex_addon_app-33.1.0.tar.gz
| Download URL | wexample_wex_addon_app-33.1.0.tar.gz |
|---|---|
| Size | 267.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
93bf116e716f88bb8890471f9ac1266f696212897d696c1e2ba64df302f29758
|
|
BLAKE2b-256 checksum How to use checksums |
eb9c657319b55554a5ec2030e8cee4c20b4e55606c8e248a402324938fda4a35
|
| 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_addon_app-33.1.0-py3-none-any.whl
| Download URL | wexample_wex_addon_app-33.1.0-py3-none-any.whl |
|---|---|
| Size | 430.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
38e6691838361d70519f97c3107c89d69dab40b9ac4347e3bd2cf1d8844fa90a
|
|
BLAKE2b-256 checksum How to use checksums |
5b2010c1bbec8adc884f3fd27cbc8d9b11a6daa35afd51a4387aa9f3dde210d8
|
| 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
|