{"id":4865,"library":"yoyo-migrations","title":"Yoyo Migrations","description":"Yoyo Migrations is a robust, database-agnostic migration tool for Python projects, enabling users to manage SQL-based schema changes. It supports various database systems with both synchronous and asynchronous drivers. Currently at version 9.0.0, it maintains an active release cadence, introducing new features and refining API usability while periodically updating Python version support.","status":"active","version":"9.0.0","language":"en","source_language":"en","source_url":"https://github.com/olly/yoyo","tags":["database","migrations","sql","orm-agnostic","asyncio","schema-management"],"install":[{"cmd":"pip install yoyo-migrations","lang":"bash","label":"Basic Installation"},{"cmd":"pip install yoyo-migrations[psycopg2-binary]","lang":"bash","label":"PostgreSQL Driver Example"},{"cmd":"pip install yoyo-migrations[mysqlclient]","lang":"bash","label":"MySQL Driver Example"}],"dependencies":[{"reason":"Optional: PostgreSQL driver for synchronous connections.","package":"psycopg2-binary","optional":true},{"reason":"Optional: PostgreSQL driver for asynchronous connections.","package":"asyncpg","optional":true},{"reason":"Optional: MySQL/MariaDB driver for synchronous connections.","package":"mysqlclient","optional":true}],"imports":[{"symbol":"get_migrations","correct":"from yoyo import get_migrations"},{"symbol":"connect","correct":"from yoyo.connections import connect"},{"symbol":"Migration","correct":"from yoyo.migrations import Migration"}],"quickstart":{"code":"import asyncio\nimport os\nfrom pathlib import Path\nfrom yoyo import get_migrations\nfrom yoyo.connections import connect\n\n# Create a temporary directory for migrations\nTEMP_MIGRATIONS_DIR = Path('./temp_yoyo_migrations_quickstart')\nTEMP_MIGRATIONS_DIR.mkdir(exist_ok=True)\n\n# Create dummy migration files\n(TEMP_MIGRATIONS_DIR / '0001.create_table.sql').write_text(\n    'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);'\n)\n(TEMP_MIGRATIONS_DIR / '0002.add_email_field.sql').write_text(\n    'ALTER TABLE users ADD COLUMN email TEXT;'\n)\n\n# Use an in-memory SQLite database for the example\nDB_URI = \"sqlite:///:memory:\"\n\nasync def run_yoyo_programmatically():\n    print(f\"Connecting to database: {DB_URI}\")\n    backend = await connect(DB_URI)\n    try:\n        migrations = get_migrations(TEMP_MIGRATIONS_DIR)\n        print(f\"Found {len(migrations)} migrations in {TEMP_MIGRATIONS_DIR}\")\n\n        with backend.transaction():\n            to_apply = backend.to_apply(migrations)\n            if to_apply:\n                applied_migrations = await backend.apply_migrations(to_apply)\n                print(f\"Successfully applied {len(applied_migrations)} migrations.\")\n            else:\n                print(\"No new migrations to apply.\")\n\n            # Example of checking current schema (simplified)\n            cursor = await backend.cursor()\n            await cursor.execute(\"PRAGMA table_info(users);\")\n            schema = await cursor.fetchall()\n            print(\"Current 'users' table schema:\")\n            for col in schema:\n                print(f\"  - {col[1]} ({col[2]})\") # col[1]=name, col[2]=type\n            await cursor.close()\n    finally:\n        await backend.close()\n        # Clean up temporary migration files and directory\n        for f in TEMP_MIGRATIONS_DIR.iterdir():\n            f.unlink()\n        TEMP_MIGRATIONS_DIR.rmdir()\n\nif __name__ == '__main__':\n    asyncio.run(run_yoyo_programmatically())\n","lang":"python","description":"This quickstart demonstrates how to programmatically apply Yoyo migrations using a temporary SQLite in-memory database and dynamically created migration files. It connects to the database, reads migrations from a specified directory, applies them, and then prints the updated table schema before cleaning up. Note the use of `await` for async database operations, a common pattern in Yoyo 9.x."},"warnings":[{"fix":"Upgrade your Python environment to 3.7 or a newer version.","message":"Yoyo 9.0.0 dropped support for Python 3.5 and 3.6. Users must be on Python 3.7 or newer to use Yoyo 9.x.","severity":"breaking","affected_versions":"9.0.0+"},{"fix":"Refactor database connection calls to use a single connection string URL. Consult the Yoyo documentation for correct URL formats for your specific database.","message":"In Yoyo 9.0.0, the database connection parameters changed from multiple `dbapi`-style arguments (e.g., `host='localhost', user='user'`) to a single URL connection string (e.g., `postgresql://user:password@host/dbname`).","severity":"breaking","affected_versions":"9.0.0+"},{"fix":"Be mindful of your chosen database driver. If using an async driver (e.g., `asyncpg`), ensure all `yoyo.connections.connect()` and `backend` object methods are called with `await` within an `async` function. If using a sync driver (e.g., `psycopg2-binary`), `await` is not necessary but using `asyncio.run` to call the main function that wraps `connect` is still fine.","message":"Yoyo 9.0.0 introduced extensive `asyncio` support. The `yoyo.connections.connect()` function now returns an `AsyncConnection` object if an async driver is detected, requiring all subsequent database operations (e.g., `apply_migrations`, `cursor().execute()`) to be `await`ed. For sync drivers, it returns a `SyncConnection` and no `await` is needed.","severity":"gotcha","affected_versions":"9.0.0+"},{"fix":"Design migration scripts to handle existing states gracefully (e.g., `CREATE TABLE IF NOT EXISTS`, `ALTER TABLE ... ADD COLUMN ... IF NOT EXISTS`). Test your migrations thoroughly in environments resembling production before deployment.","message":"Migration files (SQL or Python scripts) should ideally be idempotent or carefully managed to avoid issues during re-application or rollbacks. If a migration is not idempotent, re-running it on an already migrated database could lead to errors.","severity":"gotcha","affected_versions":"All"},{"fix":"For CLI usage, always ensure `yoyo.ini` is correctly configured and located. For programmatic integration, explicitly provide the `migrations` directory path to `get_migrations()` and the database URI to `connect()`, managing configuration outside of `yoyo.ini`.","message":"While Yoyo has a programmatic API, its primary interface is the CLI, which relies on a `yoyo.ini` configuration file and a dedicated `migrations` directory. Programmatic usage requires explicitly passing migration directories and connection details, as `yoyo.ini` is not automatically picked up.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-12T00:00:00.000Z","next_check":"2026-07-11T00:00:00.000Z"}