Skip to main content

wex_addon_app

Version: 31.0.2

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

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.yml to confirm a valid app directory, then loads .wex/python/app_manager/app_workdir.py if the project ships a custom AppWorkdir class, otherwise falls back to ManagedWorkdir. Services contribute their own children configuration before workdir.configure() is called.
  • Service registry. get_app_services(workdir) always injects the default service first, then adds any service declared under service: in config.yml. Service manifests support extends: inheritance; get_service_inheritance_chain() resolves the full chain and get_service_manifest() deep-merges it.
  • Command resolvers. get_command_resolver_classes() returns AppCommandResolver and ServiceCommandResolver.
  • 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 proxy and network.

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.

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: >=9.4.0
  • wexample-wex-core: >=30.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-wex-addon-app 31.0.2

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-wex-addon-app 31.0.2
File Size Uploaded
wexample_wex_addon_app-31.0.2.tar.gz 207.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wexample-wex-addon-app 31.0.2
File Interpreter ABI Platform
wexample_wex_addon_app-31.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 551.7 kB

Release files / wexample_wex_addon_app-31.0.2.tar.gz

Download URL wexample_wex_addon_app-31.0.2.tar.gz
Size 207.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7f6fa090d44cd98163ca4ef7f18d3efad2f3677ee9b578286f577c3a6fd1c8f5
BLAKE2b-256 checksum
How to use checksums
0630159b62d2aa90ee2590110c5fe3552eeb865f2c51c337d182ca888f5c3362
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_wex_addon_app-31.0.2-py3-none-any.whl

Download URL wexample_wex_addon_app-31.0.2-py3-none-any.whl
Size 344.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2aa5f2782d4c4abd6986c4a56d350e4bea58185fd7010c04986ea454ac7a05e
BLAKE2b-256 checksum
How to use checksums
4b471a20bbee66b3d6857834e19b1c04442580b32077efa8b4f0ed155275fa25
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

33.2.0

2 release files

33.1.0

2 release files

33.0.0

2 release files

32.0.0

2 release files

31.1.0

2 release files

This release

31.0.2 This release

2 release files

30.0.0

2 release files

29.0.0

2 release files

28.1.0

2 release files

27.0.0

2 release files

26.2.0

2 release files

26.1.0

2 release files

26.0.0

2 release files

25.4.0

2 release files

25.3.0

2 release files

25.2.0

2 release files

25.1.0

2 release files

25.0.0

2 release files

24.1.0

2 release files

24.0.1

2 release files

24.0.0

2 release files

20.8.0

2 release files

20.7.0

2 release files

20.6.1

2 release files

20.6.0

2 release files

20.5.0

2 release files

20.4.0

2 release files

20.3.0

2 release files

20.2.0

2 release files

20.1.0

2 release files

20.0.0

2 release files

19.4.1

2 release files

19.4.0

2 release files

19.3.0

2 release files

19.2.1

2 release files

19.2.0

2 release files

19.1.0

2 release files

19.0.0

2 release files

18.1.0

2 release files

18.0.2

2 release files

18.0.1

2 release files

18.0.0

2 release files

16.6.0

2 release files

16.5.0

2 release files

16.4.0

2 release files

16.3.0

2 release files

16.2.0

2 release files

16.1.0

2 release files

16.0.0

2 release files

15.1.0

2 release files

15.0.0

2 release files

12.0.0

2 release files

11.1.0

2 release files

11.0.0

2 release files

10.0.0

2 release files

9.7.0

2 release files

9.6.0

2 release files

9.5.0

2 release files

9.4.0

2 release files

9.3.3

2 release files

9.3.2

2 release files

9.3.1

2 release files

9.3.0

2 release files

9.2.0

2 release files

9.1.0

2 release files

9.0.0

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.0.56

2 release files

0.0.53

2 release files

0.0.52

2 release files

0.0.47

2 release files

0.0.46

2 release files

0.0.45

2 release files

0.0.41

2 release files

0.0.39

2 release files

0.0.38

1 release file

0.0.37

2 release files

0.0.36

2 release files

0.0.35

2 release files

0.0.34

2 release files

0.0.33

2 release files

0.0.28

2 release files

0.0.27

2 release files

0.0.26

1 release file

0.0.25

1 release file

0.0.24

2 release files

0.0.16

2 release files

0.0.15

2 release files

0.0.14

2 release files

0.0.13

2 release files

0.0.12

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.4

2 release files

0.0.3

2 release files

0.0.2

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