Skip to main content

No project description provided

Project description

migration

Version: 0.1.1

Table of Contents

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

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
  • pyyaml: >=6.0
  • wexample-filestate: >=1.0.0
  • wexample-helpers: >=1.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.

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.

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.

Migration System — Design

Concept

Migration is the temporal counterpart of filestate.

While filestate manages the state of a filesystem at a given point in time, migration manages the transition between states over time.

The core idea: a migration describes how to evolve a workdir/app/repo from one version to another, reusing filestate primitives (Options, Operations) whenever possible.

By adding WithMigrationWorkdirMixin to a workdir, you declare that it has a versioned lifecycle and supports sequential migrations.


Primary Use Case

The main use case is upgrading an application’s structure/configuration to match a new version of wex.

wex migration:run  (executed inside myapp/)
  → AppWorkdir loaded with target_path=myapp/
  → version read from config.yml (e.g. "5.0.0")
  → migrations 5.x → 6.x applied to the directory
  → config.yml updated with the new version

Versioning Unit

The main unit of versioning is the workdir (or app/repo), not individual files.

  • The current version is stored in the app’s config.yml
  • config.yml is the single source of truth
  • No separate migration state file is required

Legacy Support

If no version is present in config.yml, the system can:

  • Fall back to guess_version()
  • Infer the version from the actual filesystem state

Once detected, the version is written to config.yml and normal behavior resumes.


Core Components

MigrationContext

Context object passed to each migration:

class MigrationContext:
    target_path: Path     # root of the target workdir
    current_version: str  # version read from config.yml
    dry_run: bool         # if True, no real changes are applied

AbstractMigration

Base class for all migrations:

class AbstractMigration:
    VERSION: str          # target version, e.g. "6.0.0"
    DESCRIPTION: str      # free-form documentation

    def apply(self, context: MigrationContext) -> None: ...
    def rollback(self, context: MigrationContext) -> None: ...   # optional
    def is_applicable(self, context: MigrationContext) -> bool: ...  # optional guard
    def guess_version(self, context: MigrationContext) -> bool: ...  # optional (legacy bootstrap)

Notes

  • apply() performs the migration
  • rollback() is optional but recommended when possible
  • is_applicable() allows conditional execution
  • guess_version() is used only when no version is available (legacy workdirs)

MigrationRunner

Responsible for orchestrating migrations:

  • Reads current version from config.yml
  • If missing, attempts detection via guess_version()
  • Sorts migrations using semantic versioning
  • Applies migrations sequentially where VERSION > current_version
  • Updates config.yml after each successful migration

FilestateItemMigration

A specialized migration for declarative transformations using filestate.

It internally creates a FileStateManager and applies a filestate config.

class Migration600(FilestateItemMigration):
    VERSION = "6.0.0"

    def get_filestate_config(self, context: MigrationContext) -> dict:
        return {
            "children": [
                {
                    "name": ".wex",
                    "type": "directory",
                    "should_exist": True,
                }
            ]
        }

Use this when:

  • Creating files/directories
  • Enforcing structure
  • Applying predictable transformations

WithMigrationWorkdirMixin

Mixin used to declare migrations on a workdir:

class AppWorkdir(WithMigrationWorkdirMixin, Workdir):
    def get_migrations(self):
        from .migrations.migration_5_0_0 import Migration500
        from .migrations.migration_6_0_0 import Migration600
        return [Migration500, Migration600]

Conventions

  • Migrations live in a migrations/ directory:
your-package/
  migrations/
    migration_5_0_0.py
    migration_6_0_0.py
  • They are defined in the wex package, not in the target app
  • The app itself is unaware of the migration system

Declaring a Migration

Example:

class Migration110(AbstractMigration):
    VERSION = "1.1.0"
    DESCRIPTION = "Rename .wex_config to .wex/config"

    def apply(self, context: MigrationContext) -> None:
        old = context.target_path / ".wex_config"
        new = context.target_path / ".wex" / "config"
        old.rename(new)

    def rollback(self, context: MigrationContext) -> None:
        # optional
        ...

    def guess_version(self, context: MigrationContext) -> bool:
        # legacy detection
        return (context.target_path / ".wex_config").exists()

wex Commands

wex filestate::migration/status    # current version + pending migrations
wex filestate::migration/run       # apply all pending migrations
wex filestate::migration/run -d    # dry-run (no changes written)
wex filestate::migration/rollback  # rollback last applied migration

Design Choices (Intentional Constraints)

No Separate State File

  • The version is stored only in config.yml
  • Avoids duplication and desynchronization
  • Keeps the system simple and explicit

No Migration History

  • Only the current version is tracked
  • No need to store which migrations were applied
  • The version itself is sufficient to determine what remains

No YAML DSL for Migrations

  • Migrations are inherently procedural (conditions, logic)
  • YAML is suited for describing state (filestate), not transformations
  • Code remains the source of truth

Version Detection Only for Bootstrap

  • guess_version() is used only for legacy workdirs
  • Once a version is detected and written, it is no longer needed
  • Normal operation relies solely on config.yml

Inspirations

  • filestate: Option → Operation pipeline, dry-run, rollback, file/directory modeling
  • Alembic / Django migrations: sequential execution, version ordering
  • Rector (PHP): composable and declarative transformations
  • wex v5 legacy: migration_X_Y_Z.py, version detection patterns (concept reused, implementation redesigned)

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

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wexample_migration-0.1.1.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wexample_migration-0.1.1-py3-none-any.whl (12.7 kB view details)

Uploaded Python 3

File details

Details for the file wexample_migration-0.1.1.tar.gz.

File metadata

  • Download URL: wexample_migration-0.1.1.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.25.9 CPython/3.12.3 Linux/6.8.0-107-generic

File hashes

Hashes for wexample_migration-0.1.1.tar.gz
Algorithm Hash digest
SHA256 95253352086ec9271bb7d7518e89528eb1abd5235f122b2ad3bf4914a4659b12
MD5 61da03c0bd9c96ed8d8b369477d02f69
BLAKE2b-256 1e4cc576cc1659c7b4d5fd4191997448b3dae070401fa431e5afcfbb2e4a4c11

See more details on using hashes here.

File details

Details for the file wexample_migration-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: wexample_migration-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 12.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.25.9 CPython/3.12.3 Linux/6.8.0-107-generic

File hashes

Hashes for wexample_migration-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8607972a29891f482ca13a8914b344dc00876da6f2c5579f802812f91d156185
MD5 eb8e2f9c945c2dd322a1f66756053ab2
BLAKE2b-256 72bfee0dc42ba595ff334b0fa94b3e59a6d2a4ebe809231909c762839f617b75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page