Skip to main content

wex_addon_app

Version: 34.4.0

Adds Docker application management to wex: service lifecycle commands, environment config, and managed workdir setup

Table of Contents

API Reference

The wex-addon-app framework provides two distinct patterns for executing commands across package suites, representing an evolution from legacy inline iteration to a more modular, middleware-based approach.

This pattern externalizes iteration logic from the command implementation, allowing commands to focus solely on their core functionality while middlewares handle the iteration concerns.

How It Works

Commands are decorated with middleware that can optionally iterate over packages:

  • AppMiddleware: Provides the base app_workdir context for a single application
  • EachSuitePackageMiddleware: Extends AppMiddleware and adds the --all-packages flag
    • When --all-packages is not specified: executes the command once on the target application
    • When --all-packages is specified: automatically iterates over all packages in the suite, executing the command on each package individually

Example: app::file-state/rectify

@middleware(middleware=AppMiddleware)
@middleware(middleware=EachSuitePackageMiddleware)
@command(type=COMMAND_TYPE_ADDON)
def app__file_state__rectify(
    context: ExecutionContext,
    app_workdir: BasicAppWorkdir,
    # ... other options
) -> None:
    # Command implementation focuses only on rectifying a single app
    # Iteration is handled externally by the middleware
    workdir.apply(...)

Usage:

# Rectify only the main suite application
.wex/bin/app-manager app::file-state/rectify

# Rectify all packages in the suite
.wex/bin/app-manager app::file-state/rectify --all-packages

Benefits:

  • Clean separation of concerns: command logic vs. iteration logic
  • Reusable middleware across multiple commands
  • Consistent iteration behavior
  • Easy to add iteration capability to existing commands

Pattern 2: Delegated Iteration (For Suite-Specific Commands)

This pattern is used when the command is inherently suite-oriented and always operates on multiple packages. The iteration is delegated to helper methods on the FrameworkPackageSuiteWorkdir.

How It Works

Commands use PackageSuiteMiddleware to ensure they receive a FrameworkPackageSuiteWorkdir, then delegate iteration to:

  • packages_execute_shell(cmd): Execute shell commands on all packages
  • packages_execute_manager(command, arguments, context): Execute manager commands (e.g., app::info/show) on all packages

Example: app::suite/exec-command

@middleware(middleware=PackageSuiteMiddleware)
@command(type=COMMAND_TYPE_ADDON)
def app__suite__exec_command(
    context: ExecutionContext,
    command: str,
    app_workdir: FrameworkPackageSuiteWorkdir,
    arguments: str = None,
) -> None:
    # Delegate iteration to the workdir helper
    app_workdir.packages_execute_manager(
        command=command,
        arguments=shell_split_cmd(arguments) if arguments else None,
        context=context
    )

Usage:

# Execute a manager command on all packages
.wex/bin/app-manager app::suite/exec-command -c app::info/show

# Execute a shell command on all packages
.wex/bin/app-manager app::suite/exec-shell -c "ls -la"

Benefits:

  • Explicit suite-wide operations
  • Centralized iteration logic in workdir helpers
  • Consistent error handling and progress reporting
  • Suitable for commands that only make sense in a suite context

Migration Guide: Legacy to Modern Patterns

Legacy approach (deprecated):

def old_command(context, app_path):
    suite = get_suite(app_path)
    for package in suite.get_packages():  # Iteration inside command
        # Do something with package

Modern approach (Pattern 1):

@middleware(middleware=EachSuitePackageMiddleware)
def new_command(context, app_workdir):
    # Iteration handled by middleware when --all-packages is used
    # Command only handles single app logic

Modern approach (Pattern 2):

@middleware(middleware=PackageSuiteMiddleware)
def suite_command(context, app_workdir: FrameworkPackageSuiteWorkdir):
    # Delegate iteration to workdir helper
    app_workdir.packages_execute_manager(...)

Choosing the Right Pattern

  • Use Pattern 1 (Middleware-Based) when:

    • The command can operate on both single apps and suites
    • You want optional iteration via --all-packages
    • The command logic is app-focused, not suite-focused
  • Use Pattern 2 (Delegated) when:

    • The command is inherently suite-oriented
    • Iteration is always required (no single-app mode)
    • You're executing shell commands or other manager commands on packages

Usage

Setup & Installation

Initial setup of the development environment:

# Install dependencies for the suite and all packages (production mode)
app::setup/install

# Install dependencies + local editable packages (development mode)
app::setup/install --env local

For a single package:

# Production: Install PDM dependencies only
.wex/bin/app-manager setup

# Development: Install dependencies + local editable packages
.wex/bin/app-manager app::setup/install --env local

Complete Development & Publication Workflow

1. Initial Setup (one-time)

# Install dependencies and local editable packages for development
app::setup/install --env local

2. Development Cycle

# Rectify code across all packages
app::file-state/rectify --all-packages

# Bump only packages with changes (excluding suite)
app::package/bump --packages-only --force

# Check for circular dependencies
app::dependencies/check

# Propagate versions across all packages
app::version/propagate --packages-only

# Commit and push all changes
app::package/push --all-packages --yes

3. Publication

# Publish packages with changes to PyPI
package::suite/publish

File State Management

  • app::file-state/rectify [--all-packages]: Normalizes/rectifies code for a single app or across all packages in the suite; no commits.

Version Management

The version management commands use SuiteOrEachPackageMiddleware which provides flexible execution control:

  • Default: Executes on the suite (or single package if app_path points to a package)
  • --all-packages: Executes on both the suite AND all packages
  • --packages-only: Executes only on packages (excludes suite, implies --all-packages)
  • --suite-only: Executes only on the suite (excludes packages)

Commands:

  • app::package/bump [--all-packages|--packages-only|--suite-only] [--yes] [--force]: Bumps version for suite and/or packages. Use --force to bump only packages/suite with changes (no current version tag on HEAD).
  • app::version/propagate [--packages-only]: Propagates the current package version to other packages in the suite that depend on it. Use --packages-only to propagate all versions at once.

Git Operations

  • app::package/push [--all-packages] [--yes]: Commits and pushes changes for a single package or all packages with uncommitted changes when using --all-packages.

Dependencies Management

  • app::dependencies/check: Validates internal dependencies across the suite to prevent circular dependencies.

Suite Execution

  • app::suite/exec-command -c <command> [--arguments "<args>"]: Executes a manager command on all packages (e.g., app::info/show).
  • app::suite/exec-shell -c "<command>": Executes a shell command on all packages.

Setup & Installation

  • app::setup/install: Installs PDM dependencies for the suite and all packages. Runs pdm install in the suite and each package.
  • app::setup/install --env local: Development mode - installs dependencies + all local packages as editable dependencies (pip install -e) for cross-package development.
  • .wex/bin/app-manager setup: Low-level command that runs pdm install for a single package (called by app-manager.sh).

Publishing

  • package::suite/publish: Publishes packages to PyPI. Only publishes packages with changes since their last publication tag. Automatically bumps versions, rectifies file state, commits, propagates versions, and publishes. Creates and pushes publication tags after successful publish.

Common Workflows

Setup development environment:

# Full development setup with editable packages
app::setup/install --env local

Bump only packages with changes (excluding suite):

app::package/bump --packages-only --force && app::version/propagate --packages-only && app::package/push --all-packages --yes

Bump suite and all packages:

app::package/bump --all-packages

Bump only the suite:

app::package/bump --suite-only

Rectify all packages:

app::file-state/rectify --all-packages

Get info for all packages:

app::suite/exec-command -c app::info/show

Introduction

App Manager Architecture

The wex-addon-app framework uses a subprocess-based architecture to manage individual packages within a suite. Each package is managed through its own App Manager instance, which is a self-contained Python application that handles package-specific operations.

How It Works

Each package in a suite has access to an App Manager executable located at .wex/bin/app-manager. This manager is automatically installed and configured for each package, providing a consistent interface for executing commands on that specific package.

Key characteristics:

  • Isolation: Each package runs commands in its own subprocess via the App Manager
  • Custom behavior: Packages can have specialized classes, imports, and dependencies without affecting others
  • Consistency: All packages share the same command interface through their App Manager
  • Flexibility: Package-specific configurations and behaviors are encapsulated within each package's context

Example Usage

From any package directory, you can execute commands through the App Manager:

# Show information about the current package
.wex/bin/app-manager app::info/show

# Rectify file state for the current package
.wex/bin/app-manager app::file-state/rectify

# Bump the package version
.wex/bin/app-manager app::package/bump

The App Manager executable is located at:

/path/to/package/.wex/bin/app-manager

This architecture enables suite-level operations to delegate work to individual packages while maintaining proper isolation and allowing each package to implement custom behavior as needed.

Suite-Level Operations

When working with a package suite (a collection of related packages), the framework provides commands that can iterate over all packages, executing the App Manager for each one. This is handled through two main patterns:

  1. Middleware-based iteration (EachSuitePackageMiddleware): Commands that can optionally iterate over all packages using the --all-packages flag
  2. Delegated iteration (PackageSuiteMiddleware): Suite-specific commands that always operate on multiple packages

See the API Reference for detailed information on these patterns.

Suite Publication

Command

wex package::suite/publish --yes

From the suite root (e.g. PACKAGES/PYTHON/).


What suite/publish does

1. Shows the status Each package shows its version, the planned bump type (patch / minor / major), and whether it is to publish or up to date.

2. Validates internal dependencies Checks that imports match the declarations in pyproject.toml. Also auto-corrects == pins on internal packages → converted to >= (modified packages are then automatically included in the publication).

3. Synchronises external libraries (libraries/sync) If a package is configured with external libraries (third-party suites), their versions are updated.

4. Publishes in topological order (leaves → trunk) For each package with changes:

  • Bump: increments the version according to the type of detected change
  • Rectify: applies the file state (README, structure, etc.)
  • Commit & push: commits the changes on a version-x.y.z branch
  • Propagate: updates dependants in the suite with >=new_version (only for intermediate/major bumps)
  • Publish: uploads to PyPI

Bump types

Type Modified digit Propagation to dependants
minor (patch) 3rd (x.y.Z) No — >= already satisfied
intermediate (minor) 2nd (x.Y.z) Yes
major 1st (X.y.z) Yes

Detection is automatic: changes in src/ → intermediate or major, rest → minor.


Constraint rules

Internal dependencies (suite packages) must always use >=, never ==. A == pin will survive minor bumps without updating, causing resolution failures (pip/uv silent backtracking).

The validation at the start of suite/publish auto-corrects violations.


After publication

Update requirements.txt in consumer projects:

uv pip compile requirements.in --upgrade --output-file requirements.txt

pip-compile may fail with ResolutionTooDeepError on deep suites — use uv instead.

Installation

pip install wexample-wex-addon-app

Requires Python >=3.10.

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

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

Metadata

Release files for wexample-wex-addon-app 34.4.0

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 34.4.0
File Size Uploaded Provenance
wexample_wex_addon_app-34.4.0.tar.gz 272.1 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 716.1 kB

Release files / wexample_wex_addon_app-34.4.0.tar.gz

Download URL wexample_wex_addon_app-34.4.0.tar.gz
Size 272.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3df306d6001ce2bc951a6f474bbf2fff60cf30f6cfab33d57a35c8d6f84ccad7
BLAKE2b-256 checksum
How to use checksums
a3c31556060b934ed8b4543363389dbbcabbd7b1e3816ff458d46b8fa434a3a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.29.3 CPython/3.12.3 Linux/6.1.0-37-amd64

Release files / wexample_wex_addon_app-34.4.0-py3-none-any.whl

Download URL wexample_wex_addon_app-34.4.0-py3-none-any.whl
Size 444.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
119c51d1c41708525ae6966c780847ffab0a97a5867dfa6a1ada0d52e8d53aaf
BLAKE2b-256 checksum
How to use checksums
dd46d07e71ae7a476202af7307ffc122887f5dee9db152f9ad8b6e6f2a2b193d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.29.3 CPython/3.12.3 Linux/6.1.0-37-amd64

Security for wexample-wex-addon-app 34.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

34.4.0 This release

2 release files No provenance

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