├── .gitignore ├── LICENSE ├── README.md ├── TODOS.md ├── cozo_migrate ├── __init__.py ├── __main__.py ├── api │ ├── __init__.py │ ├── apply.py │ ├── create.py │ ├── history.py │ ├── init.py │ └── status.py ├── cli │ ├── __init__.py │ ├── apply.py │ ├── create.py │ ├── history.py │ ├── init.py │ ├── main.py │ ├── show.py │ ├── status.py │ └── version.py ├── context.py ├── files.py ├── modules.py ├── ops.py ├── queries.py ├── schema.py ├── template.py ├── types.py └── utils │ ├── __init__.py │ ├── client.py │ ├── console.py │ ├── datetime.py │ ├── fn.py │ └── rich.py ├── poetry.lock ├── pyproject.toml └── test └── migrations ├── migrate_1704506472_init.py ├── migrate_1704519286_banana.py ├── migrate_1704550975_apple.py └── migrate_1704669102_orange.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Test data 2 | cozo.db 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | .pybuilder/ 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | # For a library or package, you might want to ignore these files since the code is 90 | # intended to run in multiple environments; otherwise, check them in: 91 | # .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | #poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/#use-with-ide 113 | .pdm.toml 114 | 115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 116 | __pypackages__/ 117 | 118 | # Celery stuff 119 | celerybeat-schedule 120 | celerybeat.pid 121 | 122 | # SageMath parsed files 123 | *.sage.py 124 | 125 | # Environments 126 | .env 127 | .venv 128 | env/ 129 | venv/ 130 | ENV/ 131 | env.bak/ 132 | venv.bak/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | 152 | # pytype static type analyzer 153 | .pytype/ 154 | 155 | # Cython debug symbols 156 | cython_debug/ 157 | 158 | # PyCharm 159 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 160 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 161 | # and can be added to the global gitignore or merged into this file. For a more nuclear 162 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 163 | #.idea/ 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 julep-ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cozo-migrate 2 | 3 | A simple utility for migrations for cozo db 4 | 5 | ## Features 6 | 7 | - It uses [typer](https://typer.tiangolo.com) and `pycozo` as its only dependencies. 8 | - Loosely modeled after [hasura-migrate](https://hasura.io/docs/latest/hasura-cli/commands/hasura_migrate/) which is one of the simplest migration tools I have used. 9 | - Auto-rollback if something goes wrong during the migrations 10 | - Has a programmatic API for using inside tests etc. 11 | 12 | ## CLI Usage 13 | 14 | #### Example 15 | 16 | ```bash 17 | $ # Install cozo-migrate 18 | $ pip install cozo-migrate 19 | 20 | $ # Let's start a cozo server in the background 21 | $ cozo server -e rocksdb & 22 | 23 | $ # Let's create a migration 24 | $ cozo-migrate -e http -d ./migrations create demo 25 | Writing 'migrations/migrate_1704803704_demo.py' Confirm? [y/N]: y 26 | ✔ Created migration at: migrations/migrate_1704803704_demo.py 27 | 28 | $ cat migrations/migrate_1704803704_demo.py 29 | #/usr/bin/env python3 30 | 31 | MIGRATION_ID = "demo" 32 | CREATED_AT = 1704803704.762879 33 | 34 | def up(client): 35 | pass 36 | 37 | def down(client): 38 | pass 39 | 40 | $ # Edit the migration to add schema change queries and then apply! 41 | $ cozo-migrate -e http -d ./migrations apply -a 42 | • Migrate path: [NONE] → demo 43 | Are you sure you want to apply these migrations? [y/N]: y 44 | ✔ Database migrated. 45 | ``` 46 | 47 | #### Core options 48 | 49 | ```bash 50 | Usage: cozo-migrate [OPTIONS] COMMAND [ARGS]... 51 | 52 | A simple migration tool for Cozo databases. 53 | 54 | ╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ 55 | │ --migrations-dir -d PATH Directory to use for looking up migration files. [env var: COZO_MIGRATIONS_DIR] [default: migrations] │ 56 | │ --engine -e [sqlite|rocksdb|http|mem] Engine to use [env var: COZO_ENGINE] [default: EngineType.sqlite] │ 57 | │ --path -f PATH Database file (not applicable for mem or http engines) [env var: COZO_PATH] [default: None] │ 58 | │ --host -h TEXT Host to connect to (http engine only) [env var: COZO_HOST] [default: http://127.0.0.1:9070] │ 59 | │ --auth TEXT Auth header (http engine only) [default: None] │ 60 | │ --verbose -v │ 61 | │ --install-completion Install completion for the current shell. │ 62 | │ --show-completion Show completion for the current shell, to copy it or customize the installation. │ 63 | │ --help Show this message and exit. │ 64 | ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ 65 | ╭─ Commands ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ 66 | │ apply Apply migrations to the database. You can specify the number of steps to apply and the direction. │ 67 | │ create Create a new migration file in the migrations directory. Format: {migration_dir}/migrate__.py │ 68 | │ history Display the migration history in the database as a pretty table. │ 69 | │ init Initialize the database with the migration manager table. │ 70 | │ status Display the current migration status. │ 71 | │ version Display the current version. │ 72 | ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ 73 | ``` 74 | 75 | #### Init 76 | 77 | ```bash 78 | Usage: cozo-migrate init [OPTIONS] 79 | 80 | Initialize the database with the migration manager table. 81 | 82 | ╭─ Options ───────────────────────────────────╮ 83 | │ --help Show this message and exit. │ 84 | ╰─────────────────────────────────────────────╯ 85 | ``` 86 | 87 | #### Create 88 | 89 | ```bash 90 | Usage: cozo-migrate create [OPTIONS] ID 91 | 92 | Create a new migration file in the migrations directory. Format: {migration_dir}/migrate__.py 93 | 94 | ╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ 95 | │ * id TEXT A short, descriptive, alphanumeric id for the migration. Only letters, numbers, and underscores are allowed. [required]│ 96 | ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ 97 | ╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ 98 | │ --yes -y │ 99 | │ --help Show this message and exit. │ 100 | ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ 101 | ``` 102 | 103 | #### Apply 104 | 105 | ```bash 106 | Usage: cozo-migrate apply [OPTIONS] [STEPS] 107 | 108 | Apply migrations to the database. You can specify the number of steps to apply and the direction. 109 | 110 | ╭─ Arguments ────────────────────────╮ 111 | │ steps [STEPS] [default: 1] │ 112 | ╰────────────────────────────────────╯ 113 | ╭─ Options ──────────────────────────────────────╮ 114 | │ --down │ 115 | │ --all -a │ 116 | │ --yes -y │ 117 | │ --help Show this message and exit. │ 118 | ╰────────────────────────────────────────────────╯ 119 | ``` 120 | 121 | #### Show 122 | 123 | ```bash 124 | Usage: cozo-migrate show 125 | 126 | Show the current schema in the database. 127 | 128 | ╭─ Options ────────────────────────────────────╮ 129 | │ --help Show this message and exit. │ 130 | ╰──────────────────────────────────────────────╯ 131 | ``` 132 | 133 | #### Status 134 | 135 | ```bash 136 | Usage: cozo-migrate status [OPTIONS] 137 | 138 | Display the current migration status. 139 | 140 | ╭─ Options ────────────────────────────────────╮ 141 | │ --help Show this message and exit. │ 142 | ╰──────────────────────────────────────────────╯ 143 | ``` 144 | 145 | #### History 146 | 147 | ```bash 148 | Usage: cozo-migrate history [OPTIONS] 149 | 150 | Display the migration history in the database as a pretty table. 151 | 152 | ╭─ Options ────────────────────────────────────╮ 153 | │ --help Show this message and exit. │ 154 | ╰──────────────────────────────────────────────╯ 155 | ``` 156 | 157 | ## API Usage 158 | 159 | ```python 160 | from cozo_migrate.api import init, apply 161 | from pycozo import Client 162 | 163 | migrations_dir: str = "./migrations" 164 | 165 | # Create a new client for testing 166 | # and initialize the schema. 167 | client = Client() 168 | 169 | # Initialize the migration schema 170 | init(client) 171 | 172 | # Apply migrations from the migration directory 173 | apply(client, migrations_dir=migrations_dir, all_=True) 174 | ``` 175 | -------------------------------------------------------------------------------- /TODOS.md: -------------------------------------------------------------------------------- 1 | # TODOS 2 | 3 | - [x] type checking 4 | - [x] programmatic access 5 | - [ ] docs 6 | - [ ] Use reflection query to check for schema table 7 | - [ ] test cmd 8 | - [ ] testing 9 | -------------------------------------------------------------------------------- /cozo_migrate/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa 2 | from .cli.main import app as cli 3 | -------------------------------------------------------------------------------- /cozo_migrate/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from .cli.main import app as cli 4 | 5 | cli() 6 | -------------------------------------------------------------------------------- /cozo_migrate/api/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa 2 | from .apply import apply 3 | from .create import create 4 | from .history import history 5 | from .init import init 6 | from .status import status 7 | -------------------------------------------------------------------------------- /cozo_migrate/api/apply.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Optional 3 | 4 | from pycozo.client import Client 5 | 6 | from ..files import get_migration_files 7 | from ..modules import get_adjacent_migrations 8 | from ..ops import apply_migrations 9 | from ..queries import get_latest_migration 10 | from ..types import Migration, MigrationModule, MigrationModuleInfo 11 | 12 | 13 | def apply( 14 | client: Client, 15 | migrations_dir: str | Path, 16 | steps: int = 1, 17 | all_: bool = False, 18 | down: bool = False, 19 | ): 20 | """ 21 | Apply migrations to the database. 22 | You can specify the number of steps to apply and the direction. 23 | """ 24 | 25 | # Get the current migration 26 | current_migration: Optional[Migration] = get_latest_migration(client) 27 | current_id: Optional[str] = current_migration.id if current_migration else None 28 | 29 | if down and current_id is None: 30 | raise ValueError("No migrations to rollback.") 31 | 32 | # Find migrations in migrations_dir 33 | migration_files: list[MigrationModuleInfo] = get_migration_files(migrations_dir) 34 | num_migrations_available = len(migration_files) 35 | assert num_migrations_available > 0, "No migrations found." 36 | 37 | # If all migrations are requested, set steps to the number of migrations available 38 | if all_: 39 | steps = num_migrations_available 40 | 41 | # Get the next steps migrations from starting_id (can be negative) 42 | migrations: list[MigrationModule] = get_adjacent_migrations( 43 | migration_files, current_id, steps, down 44 | ) 45 | 46 | assert len(migrations) > 0, "No migrations to apply." 47 | 48 | applied_successfully, migration_error = apply_migrations( 49 | client, migrations, down=down, verbose=False, from_cli=False 50 | ) 51 | 52 | if not applied_successfully: 53 | raise ValueError("Migration failed.") from migration_error 54 | -------------------------------------------------------------------------------- /cozo_migrate/api/create.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import re 3 | 4 | from ..files import get_migration_files 5 | from ..template import migration_template 6 | from ..utils.datetime import utcnow 7 | 8 | 9 | def create( 10 | id: str, 11 | migration_dir: str | Path, 12 | ) -> None: 13 | """ 14 | Create a new migration file in the migrations directory. 15 | Format: {migration_dir}/migrate__.py 16 | """ 17 | 18 | # Create migrations_dir if it doesn't exist 19 | migrations_dir = Path(migration_dir) 20 | migrations_dir.mkdir(parents=True, exist_ok=True) 21 | 22 | # id can only contain letters, numbers, and underscores 23 | # can only start with a lowercase letter 24 | regex = r"^[a-z][a-zA-Z0-9_]*$" 25 | assert re.match( 26 | regex, id 27 | ), "`id` can only contain letters, numbers, and underscores" 28 | 29 | # Check that a migration with same id doesn't already exist 30 | migration_files = get_migration_files(migrations_dir) 31 | any_existing = any(m.id == id for m in migration_files) 32 | 33 | assert not any_existing, f"A migration with id `{id}` already exists." 34 | 35 | # Create the migration file 36 | ts = utcnow().timestamp() 37 | path = migrations_dir / f"migrate_{int(ts)}_{id}.py" 38 | 39 | with open(path, "w") as f: 40 | f.write(migration_template.format(migration_id=id, created_at=ts)) 41 | -------------------------------------------------------------------------------- /cozo_migrate/api/history.py: -------------------------------------------------------------------------------- 1 | from pycozo.client import Client 2 | 3 | from ..queries import get_history 4 | 5 | 6 | def history(client: Client): 7 | """Get the migration history in the database as a pandas dataframe.""" 8 | 9 | history = get_history(client) 10 | return history 11 | -------------------------------------------------------------------------------- /cozo_migrate/api/init.py: -------------------------------------------------------------------------------- 1 | from pycozo.client import Client 2 | 3 | from ..schema import create_schema, schema_exists 4 | 5 | 6 | def init(client: Client): 7 | """Initialize the database with the migration manager table.""" 8 | 9 | assert not schema_exists(client), "Schema already exists." 10 | create_schema(client) 11 | -------------------------------------------------------------------------------- /cozo_migrate/api/status.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pycozo.client import Client 4 | 5 | from ..queries import get_latest_migration 6 | from ..types import Migration 7 | 8 | 9 | def status(client: Client): 10 | """Display the current migration status.""" 11 | 12 | last_migration: Optional[Migration] = get_latest_migration(client) 13 | assert last_migration is not None, "No migrations have been applied yet." 14 | 15 | return last_migration 16 | -------------------------------------------------------------------------------- /cozo_migrate/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa 2 | from .apply import apply 3 | from .create import create 4 | from .history import history 5 | from .init import init 6 | from .show import show 7 | from .status import status 8 | from .version import version 9 | -------------------------------------------------------------------------------- /cozo_migrate/cli/apply.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from typing_extensions import Annotated 3 | 4 | import typer 5 | 6 | from ..files import get_migration_files 7 | from ..modules import get_adjacent_migrations 8 | from ..ops import apply_migrations 9 | from ..queries import get_latest_migration 10 | from ..types import Migration, MigrationModule, MigrationModuleInfo 11 | from ..utils.console import fail, success, info 12 | 13 | from .main import app 14 | 15 | 16 | @app.command() 17 | def apply( 18 | ctx: typer.Context, 19 | steps: Annotated[int, typer.Argument()] = 1, 20 | down: Annotated[bool, typer.Option("--down")] = False, 21 | all_: Annotated[bool, typer.Option("--all", "-a")] = False, 22 | confirm: Annotated[bool, typer.Option("--yes", "-y")] = False, 23 | compact: Annotated[bool, typer.Option("--compact")] = False, 24 | ): 25 | """ 26 | Apply migrations to the database. 27 | You can specify the number of steps to apply and the direction. 28 | """ 29 | 30 | # Validate client 31 | ctx.obj.check_client() 32 | 33 | client = ctx.obj.client 34 | migrations_dir = ctx.obj.migrations_dir 35 | verbose = ctx.obj.verbose 36 | 37 | # Get the current migration 38 | current_migration: Optional[Migration] = get_latest_migration(client) 39 | current_id: Optional[str] = current_migration.id if current_migration else None 40 | 41 | if down and current_id is None: 42 | fail("No migrations to rollback.") 43 | 44 | # Find migrations in migrations_dir 45 | migration_files: list[MigrationModuleInfo] = get_migration_files(migrations_dir) 46 | num_migrations_available = len(migration_files) 47 | if num_migrations_available == 0: 48 | fail("No migrations found.") 49 | 50 | # If all migrations are requested, set steps to the number of migrations available 51 | if all_: 52 | steps = num_migrations_available 53 | 54 | # Get the next steps migrations from starting_id (can be negative) 55 | migrations: list[MigrationModule] = get_adjacent_migrations( 56 | migration_files, current_id, steps, down 57 | ) 58 | 59 | if len(migrations) == 0: 60 | fail("No migrations to apply.") 61 | 62 | # Print the migration path 63 | joiner = "[yellow] → [/yellow]" 64 | migration_ids = [m.MIGRATION_ID or "[NONE]" for m in migrations] 65 | 66 | info(f"Migrate path: {joiner.join(migration_ids)}") 67 | 68 | # Confirm 69 | if not confirm: 70 | confirmed = typer.confirm("Are you sure you want to apply these migrations?") 71 | if not confirmed: 72 | raise typer.Abort() 73 | 74 | info("Migrating the database...") 75 | applied_successfully, migration_error = apply_migrations( 76 | client, migrations, down=down, verbose=verbose, from_cli=True 77 | ) 78 | 79 | if compact: 80 | client.run("::compact") 81 | info("Database compaction initiated.") 82 | 83 | if applied_successfully: 84 | success("Database migrated.") 85 | else: 86 | fail("Migration failed.", error=migration_error) 87 | -------------------------------------------------------------------------------- /cozo_migrate/cli/create.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing_extensions import Annotated 3 | 4 | import typer 5 | 6 | from ..files import get_migration_files 7 | from ..template import migration_template 8 | from ..utils.console import fail, success 9 | from ..utils.datetime import utcnow 10 | 11 | from .main import app 12 | 13 | 14 | @app.command() 15 | def create( 16 | ctx: typer.Context, 17 | id: Annotated[ 18 | str, 19 | typer.Argument( 20 | ..., 21 | help="A short, descriptive, alphanumeric id for the migration. Only letters, numbers, and underscores are allowed.", 22 | ), 23 | ], 24 | confirm: Annotated[bool, typer.Option("--yes", "-y")] = False, 25 | ): 26 | """ 27 | Create a new migration file in the migrations directory. 28 | Format: {migration_dir}/migrate__.py 29 | """ 30 | 31 | migrations_dir = ctx.obj.migrations_dir 32 | 33 | # Create migrations_dir if it doesn't exist 34 | migrations_dir.mkdir(parents=True, exist_ok=True) 35 | 36 | # id can only contain letters, numbers, and underscores 37 | # can only start with a lowercase letter 38 | regex = r"^[a-z][a-zA-Z0-9_]*$" 39 | if not re.match(regex, id): 40 | fail("`id` can only contain letters, numbers, and underscores") 41 | 42 | # Check that a migration with same id doesn't already exist 43 | migration_files = get_migration_files(migrations_dir) 44 | any_existing = any(m.id == id for m in migration_files) 45 | if any_existing: 46 | fail(f"A migration with id `{id}` already exists.") 47 | 48 | # Create the migration file 49 | ts = utcnow().timestamp() 50 | path = migrations_dir / f"migrate_{int(ts)}_{id}.py" 51 | 52 | # Confirm 53 | if not confirm: 54 | confirmed = typer.confirm(f"Writing '{path}' Confirm?") 55 | if not confirmed: 56 | raise typer.Abort() 57 | 58 | with open(path, "w") as f: 59 | f.write(migration_template.format(migration_id=id, created_at=ts)) 60 | 61 | success(f"Created migration at: {path}") 62 | -------------------------------------------------------------------------------- /cozo_migrate/cli/history.py: -------------------------------------------------------------------------------- 1 | from rich import print 2 | import typer 3 | 4 | from ..queries import get_history 5 | from ..utils.rich import df_to_table, pretty_transforms 6 | 7 | from .main import app 8 | 9 | 10 | @app.command() 11 | def history(ctx: typer.Context): 12 | """Display the migration history in the database as a pretty table.""" 13 | 14 | # Validate client 15 | ctx.obj.check_client() 16 | 17 | client = ctx.obj.client 18 | 19 | history = get_history(client).transform(pretty_transforms) 20 | history_table = df_to_table(history) 21 | 22 | print(history_table) 23 | -------------------------------------------------------------------------------- /cozo_migrate/cli/init.py: -------------------------------------------------------------------------------- 1 | import typer 2 | 3 | from ..schema import create_schema, schema_exists 4 | from ..utils.console import success, info, fail 5 | 6 | from .main import app 7 | 8 | 9 | @app.command() 10 | def init(ctx: typer.Context): 11 | """Initialize the database with the migration manager table.""" 12 | 13 | # Validate client 14 | ctx.obj.validate_options() 15 | 16 | client = ctx.obj.client 17 | 18 | if schema_exists(client): 19 | fail("Schema already exists.") 20 | 21 | else: 22 | info("Initializing the database") 23 | create_schema(client) 24 | success("Schema created.") 25 | -------------------------------------------------------------------------------- /cozo_migrate/cli/main.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Optional 3 | from typing_extensions import Annotated 4 | 5 | import typer 6 | 7 | from ..context import AppContext 8 | from ..types import CozoConnectionOptions, EngineType 9 | 10 | app = typer.Typer(pretty_exceptions_show_locals=False) 11 | 12 | 13 | @app.callback() 14 | def main( 15 | ctx: typer.Context, 16 | migrations_dir: Annotated[ 17 | Path, 18 | typer.Option( 19 | "--migrations-dir", 20 | "-d", 21 | help="Directory to use for looking up migration files.", 22 | envvar="COZO_MIGRATIONS_DIR", 23 | ), 24 | ] = Path("./migrations"), 25 | engine: Annotated[ 26 | EngineType, 27 | typer.Option("--engine", "-e", help="Engine to use", envvar="COZO_ENGINE"), 28 | ] = EngineType.sqlite, 29 | path: Annotated[ 30 | Optional[Path], 31 | typer.Option( 32 | "--path", 33 | "-f", 34 | help="Database file (not applicable for mem or http engines)", 35 | envvar="COZO_PATH", 36 | ), 37 | ] = None, 38 | host: Annotated[ 39 | str, 40 | typer.Option( 41 | "--host", 42 | "-h", 43 | help="Host to connect to (http engine only)", 44 | envvar="COZO_HOST", 45 | ), 46 | ] = "http://127.0.0.1:9070", 47 | auth: Annotated[ 48 | Optional[str], 49 | typer.Option( 50 | help="Auth header (http engine only)", envvar="COZO_AUTH", show_envvar=False 51 | ), 52 | ] = None, 53 | verbose: Annotated[bool, typer.Option("--verbose", "-v")] = False, 54 | ): 55 | """ 56 | A simple migration tool for Cozo databases. 57 | """ 58 | 59 | # Prepare context 60 | options: CozoConnectionOptions = {"host": host, "auth": auth} 61 | 62 | app_context: AppContext = AppContext( 63 | engine=engine, 64 | path=path, 65 | options=options, 66 | migrations_dir=migrations_dir, 67 | verbose=verbose, 68 | ) 69 | 70 | ctx.obj = app_context 71 | -------------------------------------------------------------------------------- /cozo_migrate/cli/show.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from rich import print 4 | import typer 5 | 6 | from ..queries import get_current_schema 7 | from ..types import Migration 8 | from ..utils.console import fail 9 | from ..utils.rich import df_to_table, pretty_transforms 10 | 11 | from .main import app 12 | 13 | 14 | @app.command() 15 | def show(ctx: typer.Context): 16 | """Show the current schema in the database.""" 17 | 18 | # Validate client 19 | ctx.obj.check_client() 20 | 21 | client = ctx.obj.client 22 | 23 | db_schema = get_current_schema(client) 24 | 25 | for relation, columns in db_schema.items(): 26 | print(f"[bold][blue]\[{relation}][/blue][/bold]") 27 | print(df_to_table(columns)) 28 | print() 29 | -------------------------------------------------------------------------------- /cozo_migrate/cli/status.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from rich import print 4 | import typer 5 | 6 | from ..queries import get_latest_migration 7 | from ..types import Migration 8 | from ..utils.console import fail 9 | 10 | from .main import app 11 | 12 | 13 | @app.command() 14 | def status(ctx: typer.Context): 15 | """Display the current migration status.""" 16 | 17 | # Validate client 18 | ctx.obj.check_client() 19 | 20 | client = ctx.obj.client 21 | 22 | last_migration: Optional[Migration] = get_latest_migration(client) 23 | if last_migration is None: 24 | fail("No migrations have been applied yet.") 25 | 26 | else: 27 | print(last_migration.to_table()) 28 | -------------------------------------------------------------------------------- /cozo_migrate/cli/version.py: -------------------------------------------------------------------------------- 1 | from importlib import metadata 2 | 3 | from ..utils.console import info 4 | from .main import app 5 | 6 | 7 | @app.command() 8 | def version(): 9 | """Display the current version.""" 10 | 11 | info(f"cozo-migrate {metadata.version('cozo-migrate')}") 12 | -------------------------------------------------------------------------------- /cozo_migrate/context.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from dataclasses import dataclass 4 | from pathlib import Path 5 | from typing import Optional 6 | 7 | from pycozo.client import Client 8 | 9 | from .schema import schema_exists 10 | from .types import EngineType, CozoConnectionOptions 11 | from .utils.client import is_connected 12 | from .utils.console import fail 13 | 14 | 15 | @dataclass 16 | class AppContext: 17 | engine: EngineType 18 | path: Path 19 | options: CozoConnectionOptions 20 | migrations_dir: Path 21 | verbose: bool 22 | _client: Optional[Client] = None 23 | 24 | @property 25 | def client(self) -> Client: 26 | if self._client is None: 27 | self._client = Client(self.engine, str(self.path), options=self.options) 28 | 29 | return self._client 30 | 31 | def ensure_connected(self) -> None: 32 | if not is_connected(self.client): 33 | fail("Could not connect to database") 34 | 35 | def ensure_schema(self) -> None: 36 | # Ensure schema exists 37 | if not schema_exists(self.client): 38 | fail("Schema does not exist. Run `cozo-migrate init` first.") 39 | 40 | def validate_options(self) -> None: 41 | # Validate options 42 | if self.engine in (EngineType.sqlite, EngineType.rocksdb): 43 | if not self.path: 44 | fail( 45 | "`--path / -f` is required for sqlite and rocksdb engines" 46 | "\n\n" 47 | "Example:\n" 48 | "$ cozo-migrate -e sqlite -f ./mydb.db [COMMAND] [OPTIONS]" 49 | ) 50 | 51 | self.path = self.path.resolve() 52 | 53 | if self.engine == EngineType.http: 54 | if not (self.options and self.options.get("host")): 55 | fail("`--host / -h` is required for http engine") 56 | 57 | def check_client(self) -> None: 58 | self.validate_options() 59 | self.ensure_connected() 60 | self.ensure_schema() 61 | -------------------------------------------------------------------------------- /cozo_migrate/files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | from pathlib import Path 5 | 6 | from .types import MigrationModuleInfo 7 | 8 | 9 | def get_migration_files(migrations_dir: str | Path) -> list[MigrationModuleInfo]: 10 | files: list[MigrationModuleInfo] = [] 11 | 12 | for f in os.listdir(migrations_dir): 13 | path = os.path.join(migrations_dir, f) 14 | if not (os.path.isfile(path) and f.startswith("migrate_")): 15 | continue 16 | 17 | slug = f.replace("migrate_", "").replace(".py", "") 18 | timestamp, *id_parts = slug.split("_") 19 | timestamp = int(timestamp) 20 | id = "_".join(id_parts) 21 | 22 | files.append( 23 | MigrationModuleInfo( 24 | path=path, 25 | id=id, 26 | created_at=timestamp, 27 | ) 28 | ) 29 | 30 | return sorted(files, key=lambda f: f.created_at) 31 | -------------------------------------------------------------------------------- /cozo_migrate/modules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import importlib.util 4 | from pathlib import Path 5 | from types import ModuleType 6 | from typing import cast, Optional 7 | 8 | from pycozo.client import Client 9 | 10 | from .types import MigrationModule, MigrationModuleInfo 11 | from .utils.fn import create_obj 12 | 13 | 14 | class NullMigrationModule(MigrationModule): 15 | MIGRATION_ID: Optional[str] = None 16 | CREATED_AT: Optional[float] = None 17 | 18 | @staticmethod 19 | def up(client: Client) -> None: 20 | ... 21 | 22 | @staticmethod 23 | def down(client: Client) -> None: 24 | ... 25 | 26 | 27 | def sanity_check(module: MigrationModule) -> None: 28 | for attr in ["up", "down", "MIGRATION_ID", "CREATED_AT"]: 29 | assert hasattr( 30 | module, attr 31 | ), f"Missing {attr} attribute in migration:{module.__file__}" 32 | 33 | 34 | def import_migration_file(path: str | Path) -> MigrationModule: 35 | path = Path(path) 36 | module_name: str = path.stem 37 | spec = importlib.util.spec_from_file_location(module_name, str(path)) 38 | module: ModuleType = importlib.util.module_from_spec(spec) 39 | 40 | if not (spec and module): 41 | raise Exception(f"Could not import migration file: {path}") 42 | 43 | spec.loader.exec_module(module) 44 | 45 | module = cast(MigrationModule, module) 46 | sanity_check(module) 47 | 48 | return module 49 | 50 | 51 | def get_adjacent_migrations( 52 | migration_files: list[MigrationModuleInfo], 53 | starting_id: Optional[str], 54 | num: int, 55 | down: bool = False, 56 | ) -> list[MigrationModule]: 57 | """Get the next num migrations after the starting_id.""" 58 | 59 | # Add None to the beginning of the list 60 | migration_files = [create_obj(id=None)] + migration_files 61 | 62 | # Reverse the list if we're going down 63 | migration_files = migration_files[::-1] if down else migration_files 64 | 65 | # Get the index of the starting_id 66 | start = next( 67 | (i for i, m in enumerate(migration_files) if m.id == starting_id), None 68 | ) 69 | 70 | assert start is not None, f"Migration {starting_id} not found." 71 | 72 | end = start + num + 1 73 | 74 | # Load the migration files 75 | migrations: list[MigrationModule] = [ 76 | import_migration_file(m_file.path) 77 | if m_file.id is not None 78 | else NullMigrationModule("", "") 79 | for m_file in migration_files[start:end] 80 | ] 81 | 82 | return migrations 83 | -------------------------------------------------------------------------------- /cozo_migrate/ops.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | from typing import Optional 5 | 6 | from pycozo.client import Client 7 | 8 | from .schema import MANAGER_TABLE_NAME 9 | from .types import MigrationModule 10 | from .utils.console import fail, info, warn 11 | from .utils.fn import paired 12 | 13 | 14 | def add_migration( 15 | client: Client, id: Optional[str], previous_id: Optional[str], created_at: float 16 | ) -> None: 17 | """Add a migration to the migrations manager table.""" 18 | 19 | time.sleep(0.01) # Ensure that the migrated_at is unique 20 | client.insert( 21 | MANAGER_TABLE_NAME, 22 | { 23 | "created_at": created_at, 24 | "previous_id": previous_id, 25 | "id": id, 26 | }, 27 | ) 28 | 29 | 30 | def apply( 31 | client: Client, 32 | migration1: MigrationModule, 33 | migration2: MigrationModule, 34 | down: bool = False, 35 | ) -> str: 36 | """Apply a migration and return the operation name.""" 37 | # Up: migration1 -> migration2 (run migration2.up) 38 | # Down: migration1 -> migration2 (run migration1.down) 39 | op = migration1.down if down else migration2.up 40 | 41 | op_name = ( 42 | f"{migration1.MIGRATION_ID}::down" if down else f"{migration2.MIGRATION_ID}::up" 43 | ) 44 | 45 | previous_id = migration1.MIGRATION_ID 46 | id = migration2.MIGRATION_ID 47 | created_at = migration2.CREATED_AT 48 | assert id or previous_id, "Can't migrate from None to None" 49 | 50 | op(client) 51 | 52 | add_migration( 53 | client, 54 | created_at=created_at, 55 | id=id, 56 | previous_id=previous_id, 57 | ) 58 | 59 | return op_name 60 | 61 | 62 | def apply_migrations( 63 | client: Client, 64 | migrations: list[MigrationModule], 65 | down: bool = False, 66 | _rolling_back: bool = False, 67 | verbose: bool = False, 68 | from_cli: bool = True, 69 | ) -> tuple[bool, Optional[Exception]]: 70 | currently_applied = [] 71 | need_to_roll_back = False 72 | migration_error = None 73 | 74 | for from_, to in paired(migrations): 75 | try: 76 | op_name = apply(client, from_, to, down=down) 77 | from_cli and verbose and info(f"Executed `{op_name}`.") 78 | currently_applied.append(from_) 79 | except Exception as e: 80 | if _rolling_back: 81 | # We're already rolling back, so we can't roll back again 82 | # Just fail 83 | from_cli and fail(f"Rollback failed: {e}") 84 | raise e 85 | 86 | migration_error = e 87 | need_to_roll_back = True 88 | break 89 | 90 | if need_to_roll_back: 91 | from_cli and migration_error and warn(f"Error: {migration_error}") 92 | 93 | # Roll back the migrations that were applied 94 | # in reverse order 95 | from_cli and warn("Migration failed. Reverting...") 96 | 97 | apply_migrations( 98 | client, 99 | currently_applied[::-1], 100 | down=(not down), 101 | _rolling_back=True, 102 | verbose=True, 103 | from_cli=from_cli, 104 | ) 105 | 106 | return (False, migration_error) 107 | 108 | return (True, None) 109 | -------------------------------------------------------------------------------- /cozo_migrate/queries.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import Optional 4 | 5 | import pandas as pd 6 | from pycozo.client import Client 7 | 8 | from .schema import MANAGER_TABLE_NAME 9 | from .types import Migration 10 | 11 | get_first_query = f""" 12 | ?[ 13 | migrated_at, 14 | id, 15 | previous_id, 16 | created_at, 17 | ] := *{MANAGER_TABLE_NAME} {{ 18 | migrated_at_ms: validity, 19 | id, 20 | created_at, 21 | previous_id, 22 | }}, 23 | migrated_at = to_int(validity) / 1000, 24 | previous_id = null 25 | """ 26 | 27 | get_latest_query = f""" 28 | ?[ 29 | migrated_at, 30 | id, 31 | previous_id, 32 | created_at, 33 | ] := *{MANAGER_TABLE_NAME} {{ 34 | migrated_at_ms: validity, 35 | id, 36 | created_at, 37 | previous_id, 38 | @ "NOW" 39 | }}, migrated_at = to_int(validity) / 1000 40 | """ 41 | 42 | get_history_query = f""" 43 | ?[ 44 | migrated_at, 45 | id, 46 | previous_id, 47 | created_at, 48 | ] := *{MANAGER_TABLE_NAME} {{ 49 | migrated_at_ms: validity, 50 | id, 51 | created_at, 52 | previous_id, 53 | }}, migrated_at = to_int(validity) / 1000 54 | 55 | :sort migrated_at 56 | """ 57 | 58 | get_migration_path_query = f""" 59 | edges[ 60 | previous_id, 61 | id, 62 | ] := *{MANAGER_TABLE_NAME} {{ 63 | id, 64 | previous_id, 65 | }} 66 | 67 | starting[id] <- [[null]] 68 | latest[id] := *{MANAGER_TABLE_NAME} {{ id @ "NOW" }} 69 | 70 | ?[start, goal, path] <~ ShortestPathBFS( 71 | edges[from, to], 72 | starting[start_idx], 73 | latest[goal_idx], 74 | ) 75 | """ 76 | 77 | 78 | def get_history(client: Client) -> pd.DataFrame: 79 | results = client.run(get_history_query) 80 | return results 81 | 82 | 83 | def get_migration_path(client: Client) -> list[str]: 84 | results = client.run(get_migration_path_query) 85 | assert len(results) <= 1, "More than one path returned" 86 | 87 | if len(results) == 0: 88 | return [] 89 | 90 | path = results["path"][0] 91 | return path 92 | 93 | 94 | def get_latest_migration(client: Client) -> Optional[Migration]: 95 | migration = Migration.get_first(client.run(get_latest_query)) 96 | return migration 97 | 98 | 99 | def get_first_migration(client: Client) -> Optional[Migration]: 100 | migration = Migration.get_first(client.run(get_first_query)) 101 | return migration 102 | 103 | 104 | def get_current_schema(client: Client) -> pd.DataFrame: 105 | all_relations = client.run("::relations") 106 | normal_relations = all_relations[all_relations["access_level"] != "index"][ 107 | ["name", "description"] 108 | ] 109 | 110 | relation_wise_columns = { 111 | relation: client.run(f"::columns {relation}").drop("index", axis=1) 112 | for relation in normal_relations["name"] 113 | if relation != MANAGER_TABLE_NAME 114 | } 115 | 116 | return relation_wise_columns 117 | -------------------------------------------------------------------------------- /cozo_migrate/schema.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pycozo.client import Client 4 | 5 | from .utils.fn import doesnt_throw 6 | 7 | MANAGER_TABLE_NAME: str = "cozo_migrate_manager" 8 | 9 | check_schema_query = f""" 10 | ?[id] := *{MANAGER_TABLE_NAME} {{ id }} 11 | :limit 1 12 | """ 13 | 14 | create_schema_query = f""" 15 | :create {MANAGER_TABLE_NAME} {{ 16 | migrated_at_ms: Validity default [floor(now() * 1000), true] => 17 | id: String?, 18 | previous_id: String?, 19 | created_at: Float?, 20 | }}""" 21 | 22 | 23 | @doesnt_throw 24 | def schema_exists(client: Client) -> bool: 25 | # Ensure the migrate schema exists 26 | client.run(check_schema_query) 27 | return True 28 | 29 | 30 | def create_schema(client: Client) -> None: 31 | client.run(create_schema_query) 32 | -------------------------------------------------------------------------------- /cozo_migrate/template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | migration_template = """\ 4 | #/usr/bin/env python3 5 | 6 | MIGRATION_ID = "{migration_id}" 7 | CREATED_AT = {created_at} 8 | 9 | def up(client): 10 | pass 11 | 12 | def down(client): 13 | pass 14 | """ 15 | -------------------------------------------------------------------------------- /cozo_migrate/types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from dataclasses import asdict, dataclass 4 | from enum import Enum 5 | from types import ModuleType 6 | from typing import Any, Optional, TypedDict 7 | 8 | import pandas as pd 9 | from pycozo.client import Client 10 | from rich.table import Table 11 | 12 | from .utils.rich import dict_to_table, pretty_transforms 13 | 14 | 15 | class EngineType(str, Enum): 16 | sqlite = "sqlite" 17 | rocksdb = "rocksdb" 18 | http = "http" 19 | mem = "mem" 20 | 21 | 22 | @dataclass 23 | class Migration: 24 | id: Optional[str] = None 25 | previous_id: Optional[str] = None 26 | created_at: Optional[float] = None 27 | migrated_at: Optional[int] = None 28 | 29 | def to_pretty_dict(self) -> dict[str, Any]: 30 | data = asdict(self) 31 | pretty_dict = {k: pretty_transforms[k](v) for k, v in data.items()} 32 | 33 | return pretty_dict 34 | 35 | def to_table(self) -> Table: 36 | return dict_to_table(self.to_pretty_dict()) 37 | 38 | @classmethod 39 | def from_pandas(cls, df: pd.DataFrame) -> list["Migration"]: 40 | return [cls(**row) for row in df.to_dict(orient="records")] 41 | 42 | @classmethod 43 | def get_first(cls, df: pd.DataFrame, default: Any = None) -> "Migration": 44 | items = cls.from_pandas(df) 45 | return items[0] if items else default 46 | 47 | 48 | class MigrationModule(ModuleType): 49 | MIGRATION_ID: Optional[str] 50 | CREATED_AT: Optional[float] 51 | 52 | @staticmethod 53 | def up(client: Client) -> None: 54 | ... 55 | 56 | @staticmethod 57 | def down(client: Client) -> None: 58 | ... 59 | 60 | 61 | @dataclass 62 | class MigrationModuleInfo: 63 | path: str 64 | id: str 65 | created_at: int 66 | 67 | 68 | class CozoConnectionOptions(TypedDict): 69 | host: str 70 | auth: Optional[str] 71 | -------------------------------------------------------------------------------- /cozo_migrate/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | -------------------------------------------------------------------------------- /cozo_migrate/utils/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pycozo.client import Client 4 | 5 | from .fn import doesnt_throw 6 | 7 | 8 | @doesnt_throw 9 | def is_connected(client: Client) -> bool: 10 | client.run("?[a] <- [[1]]") 11 | return True 12 | -------------------------------------------------------------------------------- /cozo_migrate/utils/console.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import Optional 4 | 5 | from rich.console import Console 6 | import typer 7 | 8 | 9 | console = Console(stderr=True) 10 | error_console = Console(stderr=True) 11 | 12 | 13 | def warn(msg: str, console=console) -> None: 14 | console.print(f"[bold yellow]⚠[/bold yellow] {msg}") 15 | 16 | 17 | def info(msg: str, console=console) -> None: 18 | console.print(f"[bold blue]•[/bold blue] {msg}") 19 | 20 | 21 | def fail( 22 | msg: str, console=error_console, error: Optional[BaseException] = None 23 | ) -> None: 24 | console.print(f"[bold red]❌[/bold red] {msg}") 25 | console.print() 26 | 27 | raise typer.Exit(1) from error 28 | 29 | 30 | def success(msg: str, console=console) -> None: 31 | console.print(f"[bold green]✔[/bold green] {msg}") 32 | console.print() 33 | 34 | raise typer.Exit(0) 35 | -------------------------------------------------------------------------------- /cozo_migrate/utils/datetime.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | 3 | 4 | # Note: datetime.utcnow() returns a datetime object with tzinfo set to None 5 | # Which is not the same as datetime.now(timezone.utc) 6 | # See: https://medium.com/@life-is-short-so-enjoy-it/python-datetime-utcnow-maybe-no-more-for-me-221795e8ddbf 7 | def utcnow(): 8 | return datetime.now(timezone.utc) 9 | -------------------------------------------------------------------------------- /cozo_migrate/utils/fn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from datetime import datetime 4 | from functools import wraps 5 | from typing import Callable, Any 6 | 7 | 8 | def format_ts(ts): 9 | return datetime.fromtimestamp(int(ts)) 10 | 11 | 12 | def doesnt_throw(fn: Callable[..., Any]) -> Callable[..., bool]: 13 | @wraps(fn) 14 | def wrapper(*args, **kwargs): 15 | try: 16 | fn(*args, **kwargs) 17 | return True 18 | 19 | except Exception: 20 | return False 21 | 22 | return wrapper 23 | 24 | 25 | def maybe_apply( 26 | fn: Callable[..., Any], 27 | value: Any, 28 | reject_if: Callable[[Any], bool] = lambda x: x is None, 29 | ) -> Any: 30 | if reject_if(value): 31 | return None 32 | 33 | return fn(value) 34 | 35 | 36 | def paired(lst): 37 | return [*zip(lst[:-1], lst[1:])] 38 | 39 | 40 | def create_obj(**kwargs): 41 | return type("obj", (), kwargs)() 42 | -------------------------------------------------------------------------------- /cozo_migrate/utils/rich.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import Optional 4 | 5 | import numpy as np 6 | import pandas as pd 7 | from rich import box 8 | from rich.table import Table 9 | 10 | from .fn import maybe_apply, format_ts 11 | 12 | 13 | def reject_nullish(x): 14 | return x is None or np.isnan(x) 15 | 16 | 17 | pretty_transforms = dict( 18 | created_at=lambda x: maybe_apply(format_ts, x, reject_if=reject_nullish) or "---", 19 | migrated_at=lambda x: maybe_apply(format_ts, x, reject_if=reject_nullish) or "---", 20 | id=lambda x: f"[bold green]{x}[/bold green]" if x else "---", 21 | previous_id=lambda x: f"[bold red]{x}[/bold red]" if x else "---", 22 | ) 23 | 24 | 25 | def dict_to_table(data: dict) -> Table: 26 | """Convert a dict into a rich.Table obj. 27 | Args: 28 | data (dict): A dictionary to be converted to a rich Table. 29 | Returns: 30 | Table: a rich Table instance populated with the dictionary values.""" 31 | 32 | rich_table: Table = Table(box=box.SQUARE) 33 | 34 | columns, values = list(zip(*data.items())) 35 | for column in columns: 36 | rich_table.add_column(str(column)) 37 | 38 | rich_table.add_row(*map(str, values)) 39 | 40 | return rich_table 41 | 42 | 43 | def df_to_table( 44 | pandas_dataframe: pd.DataFrame, 45 | show_index: bool = True, 46 | index_name: Optional[str] = None, 47 | ) -> Table: 48 | """Convert a pandas.DataFrame obj into a rich.Table obj. 49 | Args: 50 | pandas_dataframe (DataFrame): A Pandas DataFrame to be converted to a rich Table. 51 | show_index (bool): Add a column with a row count to the table. Defaults to True. 52 | index_name (str, optional): The column name to give to the index column. Defaults to None, showing no value. 53 | Returns: 54 | Table: a rich Table instance populated with the DataFrame values.""" 55 | 56 | rich_table: Table = Table(box=box.SQUARE) 57 | 58 | if show_index: 59 | index_name = str(index_name) if index_name else "" 60 | rich_table.add_column(index_name) 61 | 62 | for column in pandas_dataframe.columns: 63 | rich_table.add_column(str(column)) 64 | 65 | for index, value_list in enumerate(pandas_dataframe.values.tolist()): 66 | row = [str(index)] if show_index else [] 67 | row += [str(x) for x in value_list] 68 | rich_table.add_row(*row) 69 | 70 | return rich_table 71 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "attrs" 5 | version = "23.2.0" 6 | description = "Classes Without Boilerplate" 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, 11 | {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, 12 | ] 13 | 14 | [package.extras] 15 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 16 | dev = ["attrs[tests]", "pre-commit"] 17 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 18 | tests = ["attrs[tests-no-zope]", "zope-interface"] 19 | tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] 20 | tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] 21 | 22 | [[package]] 23 | name = "black" 24 | version = "23.12.1" 25 | description = "The uncompromising code formatter." 26 | optional = false 27 | python-versions = ">=3.8" 28 | files = [ 29 | {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, 30 | {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, 31 | {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, 32 | {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, 33 | {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, 34 | {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, 35 | {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, 36 | {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, 37 | {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, 38 | {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, 39 | {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, 40 | {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, 41 | {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, 42 | {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, 43 | {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, 44 | {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, 45 | {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, 46 | {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, 47 | {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, 48 | {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, 49 | {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, 50 | {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, 51 | ] 52 | 53 | [package.dependencies] 54 | click = ">=8.0.0" 55 | mypy-extensions = ">=0.4.3" 56 | packaging = ">=22.0" 57 | pathspec = ">=0.9.0" 58 | platformdirs = ">=2" 59 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 60 | typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} 61 | 62 | [package.extras] 63 | colorama = ["colorama (>=0.4.3)"] 64 | d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] 65 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 66 | uvloop = ["uvloop (>=0.15.2)"] 67 | 68 | [[package]] 69 | name = "certifi" 70 | version = "2024.2.2" 71 | description = "Python package for providing Mozilla's CA Bundle." 72 | optional = false 73 | python-versions = ">=3.6" 74 | files = [ 75 | {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, 76 | {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, 77 | ] 78 | 79 | [[package]] 80 | name = "charset-normalizer" 81 | version = "3.3.2" 82 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 83 | optional = false 84 | python-versions = ">=3.7.0" 85 | files = [ 86 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 87 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 88 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 89 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 90 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 91 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 92 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 93 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 94 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 95 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 96 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 97 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 98 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 99 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 100 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 101 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 102 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 103 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 104 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 105 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 106 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 107 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 108 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 109 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 110 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 111 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 112 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 113 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 114 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 115 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 116 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 117 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 118 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 119 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 120 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 121 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 122 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 123 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 124 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 125 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 126 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 127 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 128 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 129 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 130 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 131 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 132 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 133 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 134 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 135 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 136 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 137 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 138 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 139 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 140 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 141 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 142 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 143 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 144 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 145 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 146 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 147 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 148 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 149 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 150 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 151 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 152 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 153 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 154 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 155 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 156 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 157 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 158 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 159 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 160 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 161 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 162 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 163 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 164 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 165 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 166 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 167 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 168 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 169 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 170 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 171 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 172 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 173 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 174 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 175 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 176 | ] 177 | 178 | [[package]] 179 | name = "click" 180 | version = "8.1.7" 181 | description = "Composable command line interface toolkit" 182 | optional = false 183 | python-versions = ">=3.7" 184 | files = [ 185 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 186 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 187 | ] 188 | 189 | [package.dependencies] 190 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 191 | 192 | [[package]] 193 | name = "click-completion" 194 | version = "0.5.2" 195 | description = "Fish, Bash, Zsh and PowerShell completion for Click" 196 | optional = false 197 | python-versions = "*" 198 | files = [ 199 | {file = "click-completion-0.5.2.tar.gz", hash = "sha256:5bf816b81367e638a190b6e91b50779007d14301b3f9f3145d68e3cade7bce86"}, 200 | ] 201 | 202 | [package.dependencies] 203 | click = "*" 204 | jinja2 = "*" 205 | shellingham = "*" 206 | six = "*" 207 | 208 | [[package]] 209 | name = "click-default-group" 210 | version = "1.2.4" 211 | description = "click_default_group" 212 | optional = false 213 | python-versions = ">=2.7" 214 | files = [ 215 | {file = "click_default_group-1.2.4-py2.py3-none-any.whl", hash = "sha256:9b60486923720e7fc61731bdb32b617039aba820e22e1c88766b1125592eaa5f"}, 216 | {file = "click_default_group-1.2.4.tar.gz", hash = "sha256:eb3f3c99ec0d456ca6cd2a7f08f7d4e91771bef51b01bdd9580cc6450fe1251e"}, 217 | ] 218 | 219 | [package.dependencies] 220 | click = "*" 221 | 222 | [package.extras] 223 | test = ["pytest"] 224 | 225 | [[package]] 226 | name = "colorama" 227 | version = "0.4.6" 228 | description = "Cross-platform colored terminal text." 229 | optional = false 230 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 231 | files = [ 232 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 233 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 234 | ] 235 | 236 | [[package]] 237 | name = "cozo-embedded" 238 | version = "0.7.6" 239 | description = "Cozo database for python" 240 | optional = false 241 | python-versions = ">=3.7" 242 | files = [ 243 | {file = "cozo_embedded-0.7.6-cp37-abi3-macosx_10_14_x86_64.whl", hash = "sha256:d146e76736beb5e14e0cf73dc8babefadfbbc358b325c94c64a51b6d5b0031e9"}, 244 | {file = "cozo_embedded-0.7.6-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7341fa266369181bbc19ad9e68820b51900b0fe1c947318a3d860b570dca6e09"}, 245 | {file = "cozo_embedded-0.7.6-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80de79554138628967d4fd2636fc0a0a8dcca1c0c3bb527e638f1ee6cb763d7d"}, 246 | {file = "cozo_embedded-0.7.6-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7571f6521041c13b7e9ca8ab8809cf9c8eaad929726ed6190ffc25a5a3ab57a7"}, 247 | {file = "cozo_embedded-0.7.6-cp37-abi3-win_amd64.whl", hash = "sha256:c945ab7b350d0b79d3e643b68ebc8343fc02d223a02ab929eb0fb8e4e0df3542"}, 248 | ] 249 | 250 | [[package]] 251 | name = "cucumber-tag-expressions" 252 | version = "4.1.0" 253 | description = "Provides tag-expression parser for cucumber/behave" 254 | optional = false 255 | python-versions = ">=2.7" 256 | files = [ 257 | {file = "cucumber-tag-expressions-4.1.0.tar.gz", hash = "sha256:e314d5fed6eebb2f90380271f562248fb15e18636764faf40f4dde4b28b1f960"}, 258 | ] 259 | 260 | [package.extras] 261 | develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] 262 | 263 | [[package]] 264 | name = "idna" 265 | version = "3.6" 266 | description = "Internationalized Domain Names in Applications (IDNA)" 267 | optional = false 268 | python-versions = ">=3.5" 269 | files = [ 270 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 271 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 272 | ] 273 | 274 | [[package]] 275 | name = "immutabledict" 276 | version = "4.1.0" 277 | description = "Immutable wrapper around dictionaries (a fork of frozendict)" 278 | optional = false 279 | python-versions = ">=3.8,<4.0" 280 | files = [ 281 | {file = "immutabledict-4.1.0-py3-none-any.whl", hash = "sha256:c176e99aa90aedb81716ad35218bb2055d049b549626db4523dbe011cf2f32ac"}, 282 | {file = "immutabledict-4.1.0.tar.gz", hash = "sha256:93d100ccd2cd09a1fd3f136b9328c6e59529ba341de8bb499437f6819159fe8a"}, 283 | ] 284 | 285 | [[package]] 286 | name = "importlab" 287 | version = "0.8.1" 288 | description = "A library to calculate python dependency graphs." 289 | optional = false 290 | python-versions = ">=3.6.0" 291 | files = [ 292 | {file = "importlab-0.8.1-py2.py3-none-any.whl", hash = "sha256:124cfa00e8a34fefe8aac1a5e94f56c781b178c9eb61a1d3f60f7e03b77338d3"}, 293 | {file = "importlab-0.8.1.tar.gz", hash = "sha256:b3893853b1f6eb027da509c3b40e6787e95dd66b4b66f1b3613aad77556e1465"}, 294 | ] 295 | 296 | [package.dependencies] 297 | networkx = ">=2" 298 | 299 | [[package]] 300 | name = "jinja2" 301 | version = "3.1.3" 302 | description = "A very fast and expressive template engine." 303 | optional = false 304 | python-versions = ">=3.7" 305 | files = [ 306 | {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, 307 | {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, 308 | ] 309 | 310 | [package.dependencies] 311 | MarkupSafe = ">=2.0" 312 | 313 | [package.extras] 314 | i18n = ["Babel (>=2.7)"] 315 | 316 | [[package]] 317 | name = "libcst" 318 | version = "1.2.0" 319 | description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." 320 | optional = false 321 | python-versions = ">=3.9" 322 | files = [ 323 | {file = "libcst-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0149d24a455536ff2e41b3a48b16d3ebb245e28035013c91bd868def16592a0"}, 324 | {file = "libcst-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba24b8cf789db6b87c6e23a6c6365f5f73cb7306d929397581d5680149e9990c"}, 325 | {file = "libcst-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bbb4e442224da46b59a248d7d632ed335eae023a921dea1f5c72d2a059f6be9"}, 326 | {file = "libcst-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4919978c2b395079b64d8a654357854767adbabab13998b39c1f0bc67da8a7"}, 327 | {file = "libcst-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e54389abdea995b39ee96ad736ed1b0b8402ed30a7956b7a279c10baf0c0294"}, 328 | {file = "libcst-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d45718f7e7a1405a16fd8e7fc75c365120001b6928bfa3c4112f7e533990b9a"}, 329 | {file = "libcst-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f080e9af843ff609f8f35fc7275c8bf08b02c31115e7cd5b77ca3b6a56c75096"}, 330 | {file = "libcst-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3c7c0edfe3b878d64877671261c7b3ffe9d23181774bfad5d8fcbdbbbde9f064"}, 331 | {file = "libcst-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b5fecb2b26fa3c1efe6e05ef1420522bd31bb4dae239e4c41fdf3ddbd853aeb"}, 332 | {file = "libcst-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:968b93400e66e6711a29793291365e312d206dbafd3fc80219cfa717f0f01ad5"}, 333 | {file = "libcst-1.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e01879aa8cd478bb8b1e4285cfd0607e64047116f7ab52bc2a787cde584cd686"}, 334 | {file = "libcst-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:b4066dcadf92b183706f81ae0b4342e7624fc1d9c5ca2bf2b44066cb74bf863f"}, 335 | {file = "libcst-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5c0d548d92c6704bb07ce35d78c0e054cdff365def0645c1b57c856c8e112bb4"}, 336 | {file = "libcst-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82373a35711a8bb2a664dba2b7aeb20bbcce92a4db40af964e9cb2b976f989e7"}, 337 | {file = "libcst-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb92398236566f0b73a0c73f8a41a9c4906c793e8f7c2745f30e3fb141a34b5"}, 338 | {file = "libcst-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13ca9fe82326d82feb2c7b0f5a320ce7ed0d707c32919dd36e1f40792459bf6f"}, 339 | {file = "libcst-1.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dded0e4f2e18150c4b07fedd7ef84a9abc7f9bd2d47cc1c485248ee1ec58e5cc"}, 340 | {file = "libcst-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:dece0362540abfc39cd2cf5c98cde238b35fd74a1b0167e2563e4b8bb5f47489"}, 341 | {file = "libcst-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c80f36f4a02d530e28eac7073aabdea7c6795fc820773a02224021d79d164e8b"}, 342 | {file = "libcst-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b56130f18aca9a98b3bcaf5962b2b26c2dcdd6d5132decf3f0b0b635f4403ba"}, 343 | {file = "libcst-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4973a9d509cf1a59e07fac55a98f70bc4fd35e09781dffb3ec93ee32fc0de7af"}, 344 | {file = "libcst-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dd388c74c04434b41e3b25fc4a0fafa3e6abf91f97181df55e8f8327fd903cc"}, 345 | {file = "libcst-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38fbd56f885e1f77383a6d1d798a917ffbc6d28dc6b1271eddbf8511c194213e"}, 346 | {file = "libcst-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:f2342634f6c61fc9076dc0baf21e9cf5ef0195a06e1e95c0c9dc583ba3a30d00"}, 347 | {file = "libcst-1.2.0.tar.gz", hash = "sha256:71dd69fff76e7edaf8fae0f63ffcdbf5016e8cd83165b1d0688d6856aa48186a"}, 348 | ] 349 | 350 | [package.dependencies] 351 | pyyaml = ">=5.2" 352 | typing-extensions = ">=3.7.4.2" 353 | typing-inspect = ">=0.4.0" 354 | 355 | [package.extras] 356 | dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.3)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.5)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.3.0)", "usort (==1.0.7)"] 357 | 358 | [[package]] 359 | name = "markdown-it-py" 360 | version = "3.0.0" 361 | description = "Python port of markdown-it. Markdown parsing, done right!" 362 | optional = false 363 | python-versions = ">=3.8" 364 | files = [ 365 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 366 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 367 | ] 368 | 369 | [package.dependencies] 370 | mdurl = ">=0.1,<1.0" 371 | 372 | [package.extras] 373 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 374 | code-style = ["pre-commit (>=3.0,<4.0)"] 375 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 376 | linkify = ["linkify-it-py (>=1,<3)"] 377 | plugins = ["mdit-py-plugins"] 378 | profiling = ["gprof2dot"] 379 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 380 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 381 | 382 | [[package]] 383 | name = "markupsafe" 384 | version = "2.1.5" 385 | description = "Safely add untrusted strings to HTML/XML markup." 386 | optional = false 387 | python-versions = ">=3.7" 388 | files = [ 389 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, 390 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, 391 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, 392 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, 393 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, 394 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, 395 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, 396 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, 397 | {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, 398 | {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, 399 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, 400 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, 401 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, 402 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, 403 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, 404 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, 405 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, 406 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, 407 | {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, 408 | {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, 409 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, 410 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, 411 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, 412 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, 413 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, 414 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, 415 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, 416 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, 417 | {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, 418 | {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, 419 | {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, 420 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, 421 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, 422 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, 423 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, 424 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, 425 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, 426 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, 427 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, 428 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, 429 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, 430 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, 431 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, 432 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, 433 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, 434 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, 435 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, 436 | {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, 437 | {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, 438 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, 439 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, 440 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, 441 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, 442 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, 443 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, 444 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, 445 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, 446 | {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, 447 | {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, 448 | {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, 449 | ] 450 | 451 | [[package]] 452 | name = "mdurl" 453 | version = "0.1.2" 454 | description = "Markdown URL utilities" 455 | optional = false 456 | python-versions = ">=3.7" 457 | files = [ 458 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 459 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 460 | ] 461 | 462 | [[package]] 463 | name = "mypy-extensions" 464 | version = "1.0.0" 465 | description = "Type system extensions for programs checked with the mypy type checker." 466 | optional = false 467 | python-versions = ">=3.5" 468 | files = [ 469 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 470 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 471 | ] 472 | 473 | [[package]] 474 | name = "networkx" 475 | version = "3.1" 476 | description = "Python package for creating and manipulating graphs and networks" 477 | optional = false 478 | python-versions = ">=3.8" 479 | files = [ 480 | {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, 481 | {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, 482 | ] 483 | 484 | [package.extras] 485 | default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] 486 | developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] 487 | doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] 488 | extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] 489 | test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] 490 | 491 | [[package]] 492 | name = "ninja" 493 | version = "1.11.1.1" 494 | description = "Ninja is a small build system with a focus on speed" 495 | optional = false 496 | python-versions = "*" 497 | files = [ 498 | {file = "ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee"}, 499 | {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877"}, 500 | {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b"}, 501 | {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a"}, 502 | {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd"}, 503 | {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef"}, 504 | {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea"}, 505 | {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249"}, 506 | {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205"}, 507 | {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f"}, 508 | {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885"}, 509 | {file = "ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082"}, 510 | {file = "ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1"}, 511 | {file = "ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a"}, 512 | {file = "ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c"}, 513 | ] 514 | 515 | [package.extras] 516 | test = ["codecov (>=2.0.5)", "coverage (>=4.2)", "flake8 (>=3.0.4)", "pytest (>=4.5.0)", "pytest-cov (>=2.7.1)", "pytest-runner (>=5.1)", "pytest-virtualenv (>=1.7.0)", "virtualenv (>=15.0.3)"] 517 | 518 | [[package]] 519 | name = "numpy" 520 | version = "1.26.4" 521 | description = "Fundamental package for array computing in Python" 522 | optional = false 523 | python-versions = ">=3.9" 524 | files = [ 525 | {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, 526 | {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, 527 | {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, 528 | {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, 529 | {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, 530 | {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, 531 | {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, 532 | {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, 533 | {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, 534 | {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, 535 | {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, 536 | {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, 537 | {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, 538 | {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, 539 | {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, 540 | {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, 541 | {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, 542 | {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, 543 | {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, 544 | {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, 545 | {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, 546 | {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, 547 | {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, 548 | {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, 549 | {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, 550 | {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, 551 | {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, 552 | {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, 553 | {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, 554 | {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, 555 | {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, 556 | {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, 557 | {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, 558 | {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, 559 | {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, 560 | {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, 561 | ] 562 | 563 | [[package]] 564 | name = "packaging" 565 | version = "23.2" 566 | description = "Core utilities for Python packages" 567 | optional = false 568 | python-versions = ">=3.7" 569 | files = [ 570 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, 571 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, 572 | ] 573 | 574 | [[package]] 575 | name = "pandas" 576 | version = "2.2.1" 577 | description = "Powerful data structures for data analysis, time series, and statistics" 578 | optional = false 579 | python-versions = ">=3.9" 580 | files = [ 581 | {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, 582 | {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, 583 | {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, 584 | {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, 585 | {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, 586 | {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, 587 | {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, 588 | {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, 589 | {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, 590 | {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, 591 | {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, 592 | {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, 593 | {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, 594 | {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, 595 | {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, 596 | {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, 597 | {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, 598 | {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, 599 | {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, 600 | {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, 601 | {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, 602 | {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, 603 | {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, 604 | {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, 605 | {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, 606 | {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, 607 | {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, 608 | {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, 609 | {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, 610 | ] 611 | 612 | [package.dependencies] 613 | numpy = [ 614 | {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, 615 | {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, 616 | {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, 617 | ] 618 | python-dateutil = ">=2.8.2" 619 | pytz = ">=2020.1" 620 | tzdata = ">=2022.7" 621 | 622 | [package.extras] 623 | all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] 624 | aws = ["s3fs (>=2022.11.0)"] 625 | clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] 626 | compression = ["zstandard (>=0.19.0)"] 627 | computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] 628 | consortium-standard = ["dataframe-api-compat (>=0.1.7)"] 629 | excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] 630 | feather = ["pyarrow (>=10.0.1)"] 631 | fss = ["fsspec (>=2022.11.0)"] 632 | gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] 633 | hdf5 = ["tables (>=3.8.0)"] 634 | html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] 635 | mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] 636 | output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] 637 | parquet = ["pyarrow (>=10.0.1)"] 638 | performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] 639 | plot = ["matplotlib (>=3.6.3)"] 640 | postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] 641 | pyarrow = ["pyarrow (>=10.0.1)"] 642 | spss = ["pyreadstat (>=1.2.0)"] 643 | sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] 644 | test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] 645 | xml = ["lxml (>=4.9.2)"] 646 | 647 | [[package]] 648 | name = "pastel" 649 | version = "0.2.1" 650 | description = "Bring colors to your terminal." 651 | optional = false 652 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 653 | files = [ 654 | {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, 655 | {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, 656 | ] 657 | 658 | [[package]] 659 | name = "pathspec" 660 | version = "0.12.1" 661 | description = "Utility library for gitignore style pattern matching of file paths." 662 | optional = false 663 | python-versions = ">=3.8" 664 | files = [ 665 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 666 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 667 | ] 668 | 669 | [[package]] 670 | name = "platformdirs" 671 | version = "4.2.0" 672 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 673 | optional = false 674 | python-versions = ">=3.8" 675 | files = [ 676 | {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, 677 | {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, 678 | ] 679 | 680 | [package.extras] 681 | docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] 682 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] 683 | 684 | [[package]] 685 | name = "pluggy" 686 | version = "1.4.0" 687 | description = "plugin and hook calling mechanisms for python" 688 | optional = false 689 | python-versions = ">=3.8" 690 | files = [ 691 | {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, 692 | {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, 693 | ] 694 | 695 | [package.extras] 696 | dev = ["pre-commit", "tox"] 697 | testing = ["pytest", "pytest-benchmark"] 698 | 699 | [[package]] 700 | name = "poethepoet" 701 | version = "0.24.4" 702 | description = "A task runner that works well with poetry." 703 | optional = false 704 | python-versions = ">=3.8" 705 | files = [ 706 | {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, 707 | {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"}, 708 | ] 709 | 710 | [package.dependencies] 711 | pastel = ">=0.2.1,<0.3.0" 712 | tomli = ">=1.2.2" 713 | 714 | [package.extras] 715 | poetry-plugin = ["poetry (>=1.0,<2.0)"] 716 | 717 | [[package]] 718 | name = "pprintpp" 719 | version = "0.4.0" 720 | description = "A drop-in replacement for pprint that's actually pretty" 721 | optional = false 722 | python-versions = "*" 723 | files = [ 724 | {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, 725 | {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, 726 | ] 727 | 728 | [[package]] 729 | name = "pycnite" 730 | version = "2023.10.11" 731 | description = "Python bytecode utilities" 732 | optional = false 733 | python-versions = ">=3.8" 734 | files = [ 735 | {file = "pycnite-2023.10.11-py3-none-any.whl", hash = "sha256:7d02eb0ec4b405d8812ce053434dacfc2335dcd458ab58a1a8bf64f72d40bd76"}, 736 | {file = "pycnite-2023.10.11.tar.gz", hash = "sha256:ad8616982beecc39f2090999aa8fe0b044b1f6733ec39484cb5e0900b3c88aa1"}, 737 | ] 738 | 739 | [[package]] 740 | name = "pycozo" 741 | version = "0.7.6" 742 | description = "Python client for the Cozo database" 743 | optional = false 744 | python-versions = "*" 745 | files = [ 746 | {file = "pycozo-0.7.6-py3-none-any.whl", hash = "sha256:8930de5f82277d6481998a585c79aa898991cfb0692e168bde8b0a4558d579cf"}, 747 | {file = "pycozo-0.7.6.tar.gz", hash = "sha256:e4be9a091ba71e9d4465179bbf7557d47af84c8114d4889bd5fa13c731d57a95"}, 748 | ] 749 | 750 | [package.extras] 751 | client = ["requests"] 752 | embedded = ["cozo-embedded (==0.7.6)"] 753 | pandas = ["ipython", "pandas"] 754 | 755 | [[package]] 756 | name = "pydot" 757 | version = "2.0.0" 758 | description = "Python interface to Graphviz's Dot" 759 | optional = false 760 | python-versions = ">=3.7" 761 | files = [ 762 | {file = "pydot-2.0.0-py3-none-any.whl", hash = "sha256:408a47913ea7bd5d2d34b274144880c1310c4aee901f353cf21fe2e526a4ea28"}, 763 | {file = "pydot-2.0.0.tar.gz", hash = "sha256:60246af215123fa062f21cd791be67dda23a6f280df09f68919e637a1e4f3235"}, 764 | ] 765 | 766 | [package.dependencies] 767 | pyparsing = ">=3" 768 | 769 | [package.extras] 770 | dev = ["black", "chardet"] 771 | release = ["zest.releaser[recommended]"] 772 | tests = ["black", "chardet", "tox"] 773 | 774 | [[package]] 775 | name = "pygments" 776 | version = "2.17.2" 777 | description = "Pygments is a syntax highlighting package written in Python." 778 | optional = false 779 | python-versions = ">=3.7" 780 | files = [ 781 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 782 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 783 | ] 784 | 785 | [package.extras] 786 | plugins = ["importlib-metadata"] 787 | windows-terminal = ["colorama (>=0.4.6)"] 788 | 789 | [[package]] 790 | name = "pyparsing" 791 | version = "3.1.1" 792 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 793 | optional = false 794 | python-versions = ">=3.6.8" 795 | files = [ 796 | {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, 797 | {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, 798 | ] 799 | 800 | [package.extras] 801 | diagrams = ["jinja2", "railroad-diagrams"] 802 | 803 | [[package]] 804 | name = "python-dateutil" 805 | version = "2.8.2" 806 | description = "Extensions to the standard Python datetime module" 807 | optional = false 808 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 809 | files = [ 810 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 811 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 812 | ] 813 | 814 | [package.dependencies] 815 | six = ">=1.5" 816 | 817 | [[package]] 818 | name = "pytype" 819 | version = "2024.2.27" 820 | description = "Python type inferencer" 821 | optional = false 822 | python-versions = ">=3.8" 823 | files = [ 824 | {file = "pytype-2024.2.27-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:a8bba18a6b80c238386d3b3d7054799bd6afb8d9d2c5f3b5ea3c5b13900b0adb"}, 825 | {file = "pytype-2024.2.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae69f272256ef76b4b66a18a40160df048b163e09f552d8e81d7833676fb5782"}, 826 | {file = "pytype-2024.2.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0485a90b71baf894ead5072fa9cff77d40cafc92f6fd40f7ccdeac83aed5ed"}, 827 | {file = "pytype-2024.2.27-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea833547b9df5b12b0f87a9382c11a62c8c1d5650a71cb1a3c6053815b555659"}, 828 | {file = "pytype-2024.2.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36f00b6e2b6646a5573dbfbb3ff7e58ecf136cc721555366fef97a7f5631d983"}, 829 | {file = "pytype-2024.2.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a7c3eb3fe72cfb6c4c70e7de82c906c80397fe54a8b24b756e07a1d995d6e79"}, 830 | {file = "pytype-2024.2.27-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:d40dc7416b1bb2d1b30f3f4ae725eec74ca64d2cbdabc1bfafcfcda632212611"}, 831 | {file = "pytype-2024.2.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8156210439e06e52f0479d4057298e47887133ca65a7d92918e8ac14a112fbfc"}, 832 | {file = "pytype-2024.2.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:012415bc9c446f107addc99a13e9b8eec9345eb00f65d8fe1c52375fbee44273"}, 833 | {file = "pytype-2024.2.27-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ac9d1f101308d293b6ea2670eae59de478b24156532665dfebce6a5b6af64214"}, 834 | {file = "pytype-2024.2.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2604c209b71e567af865aed568659f7b13a01345138fa3c2df9e3154a3252216"}, 835 | {file = "pytype-2024.2.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b57689893fcd0ebbad8913d8e3ef90d77fb8b87e8845c86e5fe130d3dea0f432"}, 836 | {file = "pytype-2024.2.27.tar.gz", hash = "sha256:68cb0ecc3acee571f73d4f376c5c1a218771de039d18fd55eb903ee5d407f027"}, 837 | ] 838 | 839 | [package.dependencies] 840 | attrs = ">=21.4.0" 841 | immutabledict = ">=4.1.0" 842 | importlab = ">=0.8" 843 | jinja2 = ">=3.1.2" 844 | libcst = ">=1.0.1" 845 | networkx = "<3.2" 846 | ninja = ">=1.10.0.post2" 847 | pycnite = ">=2023.10.11" 848 | pydot = ">=1.4.2" 849 | tabulate = ">=0.8.10" 850 | toml = ">=0.10.2" 851 | typing-extensions = ">=4.3.0" 852 | 853 | [[package]] 854 | name = "pytz" 855 | version = "2024.1" 856 | description = "World timezone definitions, modern and historical" 857 | optional = false 858 | python-versions = "*" 859 | files = [ 860 | {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, 861 | {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, 862 | ] 863 | 864 | [[package]] 865 | name = "pyyaml" 866 | version = "6.0.1" 867 | description = "YAML parser and emitter for Python" 868 | optional = false 869 | python-versions = ">=3.6" 870 | files = [ 871 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, 872 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, 873 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, 874 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, 875 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, 876 | {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, 877 | {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, 878 | {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, 879 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, 880 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, 881 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, 882 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, 883 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, 884 | {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, 885 | {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, 886 | {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, 887 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, 888 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, 889 | {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, 890 | {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, 891 | {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, 892 | {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, 893 | {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, 894 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, 895 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, 896 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, 897 | {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, 898 | {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, 899 | {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, 900 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, 901 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, 902 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, 903 | {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, 904 | {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, 905 | {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, 906 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, 907 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, 908 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, 909 | {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, 910 | {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, 911 | {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, 912 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, 913 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, 914 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, 915 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, 916 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, 917 | {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, 918 | {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, 919 | {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, 920 | {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, 921 | ] 922 | 923 | [[package]] 924 | name = "requests" 925 | version = "2.31.0" 926 | description = "Python HTTP for Humans." 927 | optional = false 928 | python-versions = ">=3.7" 929 | files = [ 930 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 931 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 932 | ] 933 | 934 | [package.dependencies] 935 | certifi = ">=2017.4.17" 936 | charset-normalizer = ">=2,<4" 937 | idna = ">=2.5,<4" 938 | urllib3 = ">=1.21.1,<3" 939 | 940 | [package.extras] 941 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 942 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 943 | 944 | [[package]] 945 | name = "rich" 946 | version = "13.7.1" 947 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 948 | optional = false 949 | python-versions = ">=3.7.0" 950 | files = [ 951 | {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, 952 | {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, 953 | ] 954 | 955 | [package.dependencies] 956 | markdown-it-py = ">=2.2.0" 957 | pygments = ">=2.13.0,<3.0.0" 958 | 959 | [package.extras] 960 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 961 | 962 | [[package]] 963 | name = "ruff" 964 | version = "0.1.15" 965 | description = "An extremely fast Python linter and code formatter, written in Rust." 966 | optional = false 967 | python-versions = ">=3.7" 968 | files = [ 969 | {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, 970 | {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, 971 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, 972 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, 973 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, 974 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, 975 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, 976 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, 977 | {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, 978 | {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, 979 | {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, 980 | {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, 981 | {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, 982 | {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, 983 | {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, 984 | {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, 985 | {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, 986 | ] 987 | 988 | [[package]] 989 | name = "shellingham" 990 | version = "1.5.4" 991 | description = "Tool to Detect Surrounding Shell" 992 | optional = false 993 | python-versions = ">=3.7" 994 | files = [ 995 | {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, 996 | {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, 997 | ] 998 | 999 | [[package]] 1000 | name = "six" 1001 | version = "1.16.0" 1002 | description = "Python 2 and 3 compatibility utilities" 1003 | optional = false 1004 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1005 | files = [ 1006 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1007 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "tabulate" 1012 | version = "0.9.0" 1013 | description = "Pretty-print tabular data" 1014 | optional = false 1015 | python-versions = ">=3.7" 1016 | files = [ 1017 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, 1018 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, 1019 | ] 1020 | 1021 | [package.extras] 1022 | widechars = ["wcwidth"] 1023 | 1024 | [[package]] 1025 | name = "toml" 1026 | version = "0.10.2" 1027 | description = "Python Library for Tom's Obvious, Minimal Language" 1028 | optional = false 1029 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 1030 | files = [ 1031 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1032 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "tomli" 1037 | version = "2.0.1" 1038 | description = "A lil' TOML parser" 1039 | optional = false 1040 | python-versions = ">=3.7" 1041 | files = [ 1042 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1043 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "typer" 1048 | version = "0.9.0" 1049 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 1050 | optional = false 1051 | python-versions = ">=3.6" 1052 | files = [ 1053 | {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, 1054 | {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, 1055 | ] 1056 | 1057 | [package.dependencies] 1058 | click = ">=7.1.1,<9.0.0" 1059 | typing-extensions = ">=3.7.4.3" 1060 | 1061 | [package.extras] 1062 | all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] 1063 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] 1064 | doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] 1065 | test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] 1066 | 1067 | [[package]] 1068 | name = "typing-extensions" 1069 | version = "4.10.0" 1070 | description = "Backported and Experimental Type Hints for Python 3.8+" 1071 | optional = false 1072 | python-versions = ">=3.8" 1073 | files = [ 1074 | {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, 1075 | {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "typing-inspect" 1080 | version = "0.9.0" 1081 | description = "Runtime inspection utilities for typing module." 1082 | optional = false 1083 | python-versions = "*" 1084 | files = [ 1085 | {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, 1086 | {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, 1087 | ] 1088 | 1089 | [package.dependencies] 1090 | mypy-extensions = ">=0.3.0" 1091 | typing-extensions = ">=3.7.4" 1092 | 1093 | [[package]] 1094 | name = "tzdata" 1095 | version = "2024.1" 1096 | description = "Provider of IANA time zone data" 1097 | optional = false 1098 | python-versions = ">=2" 1099 | files = [ 1100 | {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, 1101 | {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "urllib3" 1106 | version = "2.2.1" 1107 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1108 | optional = false 1109 | python-versions = ">=3.8" 1110 | files = [ 1111 | {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, 1112 | {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, 1113 | ] 1114 | 1115 | [package.extras] 1116 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 1117 | h2 = ["h2 (>=4,<5)"] 1118 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1119 | zstd = ["zstandard (>=0.18.0)"] 1120 | 1121 | [[package]] 1122 | name = "ward" 1123 | version = "0.68.0b0" 1124 | description = "A modern Python testing framework" 1125 | optional = false 1126 | python-versions = ">=3.7.8,<4.0.0" 1127 | files = [ 1128 | {file = "ward-0.68.0b0-py3-none-any.whl", hash = "sha256:0847e6b95db9d2b83c7d1b9cea9bcb7ac3b8e8f6d341b8dc8920d6afb05458b1"}, 1129 | {file = "ward-0.68.0b0.tar.gz", hash = "sha256:d8aafa4ddb81f4d5787d95bdb2f7ba69a2e89f183feec78d8afcc64b2cd19ee9"}, 1130 | ] 1131 | 1132 | [package.dependencies] 1133 | click = ">=7,<9" 1134 | click-completion = ">=0.5.2,<0.6.0" 1135 | click-default-group = ">=1.2.2,<2.0.0" 1136 | cucumber-tag-expressions = ">=2.0.0,<5.0.0" 1137 | pluggy = ">=0.13.1,<2.0.0" 1138 | pprintpp = ">=0.4.0,<0.5.0" 1139 | rich = ">=12.2.0" 1140 | tomli = ">=1.0.0,<3.0.0" 1141 | 1142 | [metadata] 1143 | lock-version = "2.0" 1144 | python-versions = "^3.10" 1145 | content-hash = "4a640899feb290c94034b423b85c64dec767b30db41f7aa186ae2affa7c98331" 1146 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "cozo-migrate" 3 | version = "0.2.4" 4 | description = "A simple utility for migrations for cozo db" 5 | authors = ["Diwank Singh Tomer "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | typer = ">=0.9.0" 12 | rich = ">=13.0.0,<14.0.0" 13 | pycozo = "^0.7.6" 14 | colorama = ">=0.4.0,<0.6.0" 15 | shellingham = ">=1.4.0,<2.0.0" 16 | pandas = ">=2.0.0,<2.5.0" 17 | cozo-embedded = "^0.7.6" 18 | requests = ">=2.0.0,<3.0.0" 19 | 20 | 21 | [tool.poetry.group.dev.dependencies] 22 | ruff = "^0.1.11" 23 | ward = "^0.68.0b0" 24 | black = "^23.12.1" 25 | pytype = "^2024.1.5" 26 | poethepoet = "^0.24.4" 27 | 28 | [tool.poetry.scripts] 29 | cozo-migrate = "cozo_migrate:cli" 30 | 31 | [build-system] 32 | requires = ["poetry-core"] 33 | build-backend = "poetry.core.masonry.api" 34 | 35 | [tool.poe.poetry_hooks] 36 | pre_build = "check" 37 | 38 | [tool.poe.tasks] 39 | prettify = "black ." 40 | lint = "ruff **/*.py --fix --unsafe-fixes" 41 | typecheck = "pytype cozo_migrate" 42 | check = [ 43 | "prettify", 44 | "lint", 45 | "typecheck", 46 | ] 47 | 48 | test = [ 49 | { cmd = "cozo-migrate -f cozo.db init" }, 50 | { cmd = "cozo-migrate -d dummy/migrations create hello -y" }, 51 | { cmd = "cozo-migrate -f cozo.db -d test/migrations apply -y --compact" }, 52 | { cmd = "cozo-migrate -f cozo.db status" }, 53 | { cmd = "cozo-migrate -f cozo.db show" }, 54 | { cmd = "cozo-migrate -f cozo.db -d test/migrations apply -ay --down" }, 55 | { cmd = "cozo-migrate -f cozo.db history" }, 56 | { cmd = "rm -rf cozo.db dummy/migrations" }, 57 | ] 58 | -------------------------------------------------------------------------------- /test/migrations/migrate_1704506472_init.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python3 2 | 3 | MIGRATION_ID = "init" 4 | CREATED_AT = 1704506472.363848 5 | 6 | 7 | def up(client): 8 | client.run( 9 | """ 10 | :create test { 11 | name: String, 12 | } 13 | """ 14 | ) 15 | 16 | 17 | def down(client): 18 | client.run( 19 | """ 20 | ::remove test 21 | """ 22 | ) 23 | -------------------------------------------------------------------------------- /test/migrations/migrate_1704519286_banana.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python3 2 | 3 | MIGRATION_ID = "banana" 4 | CREATED_AT = 1704519286.09414 5 | 6 | 7 | def up(client): 8 | pass 9 | 10 | 11 | def down(client): 12 | pass 13 | -------------------------------------------------------------------------------- /test/migrations/migrate_1704550975_apple.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python3 2 | 3 | MIGRATION_ID = "apple" 4 | CREATED_AT = 1704550975.963196 5 | 6 | 7 | def up(client): 8 | raise Exception("Not implemented") 9 | pass 10 | 11 | 12 | def down(client): 13 | pass 14 | -------------------------------------------------------------------------------- /test/migrations/migrate_1704669102_orange.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python3 2 | 3 | MIGRATION_ID = "orange" 4 | CREATED_AT = 1704669102.228528 5 | 6 | 7 | def up(client): 8 | pass 9 | 10 | 11 | def down(client): 12 | pass 13 | --------------------------------------------------------------------------------