diff --git a/mipdb/commands.py b/mipdb/commands.py index d7e38bb..d30c238 100644 --- a/mipdb/commands.py +++ b/mipdb/commands.py @@ -1,590 +1,530 @@ +from __future__ import annotations + import ipaddress from pathlib import Path +from typing import Any, Dict, Optional import click as cl -import os -import glob -from mipdb.databases import credentials_from_config -from mipdb.databases.monetdb import MonetDB +from mipdb.credentials import credentials_from_config +from mipdb.logger import LOGGER +from mipdb.monetdb.monetdb import MonetDB +from mipdb.sqlite.sqlite import SQLiteDB from mipdb.reader import JsonFileReader -from mipdb.databases.sqlite import SQLiteDB from mipdb.usecases import ( AddDataModel, + AddPropertyToDataModel, + AddPropertyToDataset, Cleanup, - ValidateDatasetNoDatabase, + DeleteDataModel, + DeleteDataset, + DisableDataModel, + DisableDataset, + EnableDataModel, + EnableDataset, + ImportCSV, + InitDB, + ListDataModels, + ListDatasets, + RemovePropertyFromDataModel, + RemovePropertyFromDataset, + TagDataModel, + TagDataset, + UntagDataModel, + UntagDataset, ValidateDataModel, + ValidateDataset, + ValidateDatasetNoDatabase, ) -from mipdb.usecases import AddPropertyToDataModel -from mipdb.usecases import AddPropertyToDataset -from mipdb.usecases import DeleteDataModel -from mipdb.usecases import DeleteDataset -from mipdb.usecases import ImportCSV -from mipdb.usecases import InitDB -from mipdb.exceptions import handle_errors, UserInputError -from mipdb.usecases import DisableDataset -from mipdb.usecases import DisableDataModel -from mipdb.usecases import EnableDataset -from mipdb.usecases import EnableDataModel -from mipdb.usecases import ListDataModels -from mipdb.usecases import ListDatasets -from mipdb.usecases import RemovePropertyFromDataModel -from mipdb.usecases import RemovePropertyFromDataset -from mipdb.usecases import UntagDataModel -from mipdb.usecases import TagDataModel -from mipdb.usecases import TagDataset -from mipdb.usecases import UntagDataset -from mipdb.usecases import ValidateDataset - - -class NotRequiredIf(cl.Option): - def __init__(self, *args, **kwargs): - credentials = credentials_from_config() - option_to_env_var = { - "--ip": credentials["DB_IP"], - "--port": credentials["DB_PORT"], - "--username": credentials["MONETDB_ADMIN_USERNAME"], - "--password": credentials["MONETDB_LOCAL_PASSWORD"], - "--db_name": credentials["DB_NAME"], - "--sqlite_db_path": credentials["SQLITE_DB_PATH"], - } - option = args[0][0] - if option_to_env_var[option]: - kwargs["required"] = False - kwargs["default"] = option_to_env_var[option] - super(NotRequiredIf, self).__init__(*args, **kwargs) - - -_db_configs_options = [ - cl.option( - "--ip", - "ip", - required=True, - help="The ip of the database", - cls=NotRequiredIf, - ), - cl.option( - "--port", - "port", - required=True, - help="The port for the database", - cls=NotRequiredIf, - ), - cl.option( - "--username", - "username", - required=True, - help="The username for the database", - cls=NotRequiredIf, - ), - cl.option( - "--password", - "password", - required=True, - help="The password for the database", - cls=NotRequiredIf, - ), - cl.option( - "--db_name", - "db_name", - required=True, - help="The name of the database", - cls=NotRequiredIf, - ), - cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, - ), -] - - -def get_monetdb_config(ip, port, username, password, db_name): - try: - ipaddress.ip_address(ip) - except ValueError: - raise UserInputError("Invalid ip provided") - - config = { - "ip": ip, - "port": port, - "dbfarm": db_name, - "username": username, - "password": password, - } - return config - - -def db_configs_options(func): - for option in reversed(_db_configs_options): - func = option(func) - return func +from mipdb.exceptions import handle_errors -@cl.group() -def entry(): - pass +class IPAddressType(cl.ParamType): + name = "ip" + + def convert(self, value, param, ctx): # type: ignore[override] + if value in (None, ""): + return None + try: + ipaddress.ip_address(value) + return value + except ValueError: + self.fail(f"{value!r} is not a valid IP address", param, ctx) + + +IP_ADDRESS = IPAddressType() + + +def _open_sqlite(path: str | Path) -> SQLiteDB: + return SQLiteDB.from_config({"db_path": str(path)}) + + +def _open_monetdb(enabled: bool, cfg: Dict[str, Any]) -> Optional[MonetDB]: + if not enabled: + LOGGER.debug("MonetDB disabled – operating in SQLite‑only mode.") + return None + return MonetDB.from_config(cfg) + + +def with_dbs(func): + + @cl.pass_context + def _wrapper(ctx: cl.Context, *args, **kwargs): + kwargs.setdefault("sqlite_db", ctx.obj["sqlite_db"]) + kwargs.setdefault("monetdb", ctx.obj["monetdb"]) + return ctx.invoke(func, *args, **kwargs) + + return cl.decorators.update_wrapper(_wrapper, func) + + +def resolve_copy_flag( + copy_from_file: Optional[bool], monetdb: Optional[MonetDB] +) -> bool: + + if monetdb is None: + if copy_from_file is not None: + raise cl.BadParameter( + "--copy/--no-copy is only valid when MonetDB is enabled." + ) + return True + if copy_from_file is None: + copy_from_file = True + return copy_from_file + + +def _require( + name: str, + cli_val: str | None, + cfg_key: str, + cfg: dict[str, str], + *, + required: bool, +) -> str | None: + """ + Return the resolved value for *name* or raise click.BadParameter + if it's required but not provided in either CLI or config. + """ + val = cli_val if cli_val not in (None, "") else cfg.get(cfg_key) + if required and not val: + raise cl.BadParameter( + f"Missing required option --{name.replace('_', '-')} " + f"(also not set as {cfg_key} in config.toml)" + ) + return val -@entry.command() -@cl.argument("file", required=True) +@cl.group() @cl.option( - "--copy_from_file", - required=False, - default=True, - help="Copy the csvs from the filesystem instead of copying them through sockets." - "The same files should exist both in the mipdb script and the db.", + "--monetdb/--no-monetdb", + "monetdb_opt", + default=None, + help="Enable or disable MonetDB integration (overrides config).", ) -@db_configs_options +@cl.option("--ip", type=IP_ADDRESS, default=None, help="MonetDB host IP.") +@cl.option("--port", default=None, help="MonetDB port.") +@cl.option("--username", default=None, help="MonetDB admin username.") +@cl.option("--password", default=None, help="MonetDB admin password.") +@cl.option("--db-name", "db_name", default=None, help="MonetDB farm name.") +@cl.option("--sqlite", "sqlite_db_path", default=None, help="SQLite DB file path.") +@cl.pass_context +def cli( + ctx: cl.Context, + monetdb_opt: Optional[bool], + ip: Optional[str], + port: Optional[str], + username: Optional[str], + password: Optional[str], + db_name: Optional[str], + sqlite_db_path: Optional[str], +): + """ + Root command: resolves configuration and stores DB handles in ``ctx.obj``. + """ + + + cfg = credentials_from_config() + + monetdb_enabled: bool = ( + monetdb_opt + if monetdb_opt is not None + else bool(cfg.get("MONETDB_ENABLED", False)) + ) + + ip = _require("ip", ip, "DB_IP", cfg, required=monetdb_enabled) + port = _require("port", port, "DB_PORT", cfg, required=monetdb_enabled) + username = _require( + "username", username, "MONETDB_ADMIN_USERNAME", cfg, required=monetdb_enabled + ) + password = _require( + "password", password, "MONETDB_LOCAL_PASSWORD", cfg, required=monetdb_enabled + ) + db_name = _require("db_name", db_name, "DB_NAME", cfg, required=monetdb_enabled) + sqlite_db_path = _require( + "sqlite_db_path", sqlite_db_path, "SQLITE_DB_PATH", cfg, required=True + ) + + ctx.ensure_object(dict) + ctx.obj["sqlite_db"] = _open_sqlite(sqlite_db_path) + ctx.obj["monetdb"] = _open_monetdb( + monetdb_enabled, + { + "monetdb": monetdb_enabled, + "ip": ip, + "port": port, + "dbfarm": db_name, + "username": username, + "password": password, + }, + ) + + +@cli.command() @handle_errors +@with_dbs +def init(sqlite_db: SQLiteDB, **_): + InitDB(db=sqlite_db).execute() + LOGGER.info("Database initialized") + + +COPY_OPT = cl.option( + "--copy/--no-copy", + "copy_from_file", + default=None, + help="Use MonetDB COPY FROM FILE (MonetDB only).", +) + + +@cli.command() +@cl.argument("folder", type=cl.Path(exists=True, file_okay=False, path_type=Path)) +@handle_errors +def validate_folder(folder: Path): + + if not any(folder.iterdir()): + raise cl.UsageError(f"The directory {folder} is empty.") + + for meta_path in folder.rglob("CDEsMetadata.json"): + subdir = meta_path.parent + LOGGER.info("Validating data-model folder %s", subdir) + + meta_path = subdir / "CDEsMetadata.json" + if not meta_path.exists(): + LOGGER.warning(" • Skipped (missing CDEsMetadata.json)") + continue + + meta = JsonFileReader(meta_path).read() + code, ver = meta["code"], meta["version"] + + ValidateDataModel().execute(meta) + LOGGER.info(" • Data-model %s:%s OK", code, ver) + + for csv in subdir.glob("*.csv"): + ValidateDatasetNoDatabase().execute(csv, meta) + LOGGER.info(" • CSV %s OK", csv.name) + + LOGGER.info("Folder validation completed – all checked files are valid.") + + +@cli.command("load-folder") +@cl.argument("folder", type=cl.Path(exists=True, file_okay=False, path_type=Path)) +@COPY_OPT +@handle_errors +@with_dbs def load_folder( - file, copy_from_file, ip, port, username, password, db_name, sqlite_db_path + folder: Path, + copy_from_file: Optional[bool], + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], ): - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) + copy_final = resolve_copy_flag(copy_from_file, monetdb) Cleanup(sqlite_db, monetdb).execute() - if not os.path.exists(file): - print(f"The path {file} does not exist.") - return - elif not os.listdir(file): - print(f"The directory {file} is empty.") - return - - root = Path(file).resolve() - for subdir, dirs, files in os.walk(file): - if dirs: - continue - print(f"Data model '{subdir}' is being loaded...") - metadata_path = os.path.join(subdir, "CDEsMetadata.json") - reader = JsonFileReader(metadata_path) - data_model_metadata = reader.read() - code = data_model_metadata["code"] - version = data_model_metadata["version"] - AddDataModel(sqlite_db=sqlite_db, monetdb=monetdb).execute(data_model_metadata) - print(f"Data model '{code}' was successfully added.") - - for csv_path in glob.glob(subdir + "/*.csv"): - print(f"CSV '{csv_path}' is being loaded...") + + for meta_path in folder.rglob("CDEsMetadata.json"): + subdir = meta_path.parent + LOGGER.info("Processing data-model folder %s", subdir) + + meta = JsonFileReader(meta_path).read() + code, ver = meta["code"], meta["version"] + + ValidateDataModel().execute(meta) + AddDataModel(sqlite_db=sqlite_db, monetdb=monetdb).execute(meta) + for csv in subdir.glob("*.csv"): ValidateDataset(sqlite_db=sqlite_db, monetdb=monetdb).execute( - csv_path, copy_from_file, code, version + csv, copy_final, code, ver ) ImportCSV(sqlite_db=sqlite_db, monetdb=monetdb).execute( - csv_path, copy_from_file, code, version + csv, copy_final, code, ver ) - print(f"CSV '{csv_path}' was successfully added.") + LOGGER.info("Folder import finished successfully.") -@entry.command() -@cl.argument("file", required=True) -@handle_errors -def validate_folder(file): - if not os.path.exists(file): - print(f"The path {file} does not exist.") - return - elif not os.listdir(file): - print(f"The directory {file} is empty.") - return - - for subdir, dirs, files in os.walk(file): - if dirs: - continue - print(f"Data model '{subdir}' is being validated...") - metadata_path = os.path.join(subdir, "CDEsMetadata.json") - reader = JsonFileReader(metadata_path) - data_model_metadata = reader.read() - code = data_model_metadata["code"] - ValidateDataModel().execute(data_model_metadata) - print(f"Data model '{code}' was successfully validated.") - - for csv_path in glob.glob(subdir + "/*.csv"): - print(f"CSV '{csv_path}' is being validated...") - ValidateDatasetNoDatabase().execute(csv_path, data_model_metadata) - print(f"CSV '{csv_path}' was successfully validated.") - -@entry.command() -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("add-data-model") +@cl.argument("metadata", type=cl.Path(exists=True, dir_okay=False, path_type=Path)) @handle_errors -def init(sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - InitDB(db=sqlite_db).execute() - print("Database initialized") +@with_dbs +def add_data_model( + metadata: Path, + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], +): + data_model_meta = JsonFileReader(metadata).read() + ValidateDataModel().execute(data_model_meta) + AddDataModel(sqlite_db=sqlite_db, monetdb=monetdb).execute(data_model_meta) + + LOGGER.info( + "Data-model %s:%s registered.", + data_model_meta["code"], + data_model_meta["version"], + ) -@entry.command() -@cl.argument("file", required=True) -@db_configs_options +@cli.command("validate-dataset") +@cl.argument("csv", type=cl.Path(exists=True, dir_okay=False, path_type=Path)) +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) +@COPY_OPT @handle_errors -def add_data_model(file, ip, port, username, password, db_name, sqlite_db_path): - print(f"Data model '{file}' is being loaded...") - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - reader = JsonFileReader(file) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - data_model_metadata = reader.read() - ValidateDataModel().execute(data_model_metadata) - AddDataModel(sqlite_db=sqlite_db, monetdb=monetdb).execute(data_model_metadata) - print(f"Data model '{file}' was successfully added.") - - -@entry.command() -@cl.argument("csv_path", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--copy_from_file", - required=False, - default=True, - help="Copy the csvs from the filesystem instead of copying them through sockets." - "The same files should exist both in the mipdb script and the db.", -) -@db_configs_options -@handle_errors -def add_dataset( - csv_path, - data_model, - version, - copy_from_file, - ip, - port, - username, - password, - db_name, - sqlite_db_path, +@with_dbs +def validate_dataset( + csv: Path, + data_model: str, + version: str, + copy_from_file: Optional[bool], + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], ): - print(f"CSV '{csv_path}' is being loaded...") - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) + copy_final = resolve_copy_flag(copy_from_file, monetdb) ValidateDataset(sqlite_db=sqlite_db, monetdb=monetdb).execute( - csv_path, copy_from_file, data_model, version - ) - ImportCSV(sqlite_db=sqlite_db, monetdb=monetdb).execute( - csv_path, copy_from_file, data_model, version + csv, copy_final, data_model, version ) - print(f"CSV '{csv_path}' was successfully added.") + LOGGER.info("Dataset %s validated against %s:%s", csv.name, data_model, version) -@entry.command() -@cl.argument("csv_path", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--copy_from_file", - required=False, - default=True, - help="Copy the csvs from the filesystem instead of copying them through sockets." - "The same files should exist both in the mipdb script and the db.", -) -@db_configs_options +@cli.command("add-dataset") +@cl.argument("csv", type=cl.Path(exists=True, dir_okay=False, path_type=Path)) +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) +@COPY_OPT @handle_errors -def validate_dataset( - csv_path, - data_model, - version, - copy_from_file, - ip, - port, - username, - password, - db_name, - sqlite_db_path, +@with_dbs +def add_dataset( + csv: Path, + data_model: str, + version: str, + copy_from_file: Optional[bool], + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], ): - print(f"Dataset '{csv_path}' is being validated...") - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) + copy_final = resolve_copy_flag(copy_from_file, monetdb) ValidateDataset(sqlite_db=sqlite_db, monetdb=monetdb).execute( - csv_path, copy_from_file, data_model, version + csv, copy_final, data_model, version + ) + ImportCSV(sqlite_db=sqlite_db, monetdb=monetdb).execute( + csv, copy_final, data_model, version ) - print(f"Dataset '{csv_path}' has a valid structure.") + LOGGER.info("Dataset %s registered under %s:%s", csv.name, data_model, version) -@entry.command() -@cl.argument("name", required=True) -@cl.option("-v", "--version", required=True, help="The data model version") + +@cli.command("delete-data-model") +@cl.argument("name") +@cl.option("-v", "--version", required=True) @cl.option( - "--force", - "-f", - is_flag=True, - help="Force deletion of dataset that are based on the data model", + "-f", "--force", is_flag=True, help="Also drop datasets based on this model." ) -@db_configs_options @handle_errors +@with_dbs def delete_data_model( - name, version, force, ip, port, username, password, db_name, sqlite_db_path + name: str, + version: str, + force: bool, + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], ): - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) DeleteDataModel(sqlite_db=sqlite_db, monetdb=monetdb).execute(name, version, force) - print(f"Data model '{name}' was successfully removed.") + LOGGER.info("Data-model %s:%s deleted.", name, version) -@entry.command() -@cl.argument("dataset", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@db_configs_options +@cli.command("delete-dataset") +@cl.argument("dataset") +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) @handle_errors +@with_dbs def delete_dataset( - dataset, data_model, version, ip, port, username, password, db_name, sqlite_db_path + dataset: str, + data_model: str, + version: str, + sqlite_db: SQLiteDB, + monetdb: Optional[MonetDB], ): - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) DeleteDataset(sqlite_db=sqlite_db, monetdb=monetdb).execute( dataset, data_model, version ) - print(f"Dataset {dataset} was successfully removed.") + LOGGER.info("Dataset %s deleted from %s:%s.", dataset, data_model, version) -@entry.command() -@cl.argument("name", required=True) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("disable-data-model") +@cl.argument("name") +@cl.option("-v", "--version", required=True) @handle_errors -def enable_data_model(name, version, sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - EnableDataModel(db=sqlite_db).execute(name, version) - print(f"Data model {name} was successfully enabled.") +@with_dbs +def disable_data_model( + name: str, + version: str, + sqlite_db: SQLiteDB, + **_, +): + DisableDataModel(db=sqlite_db).execute(name, version) + LOGGER.info("Data-model %s:%s disabled.", name, version) -@entry.command() -@cl.argument("name", required=True) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("disable-dataset") +@cl.argument("dataset") +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) @handle_errors -def disable_data_model(name, version, sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - DisableDataModel(db=sqlite_db).execute(name, version) - print(f"Data model {name} was successfully disabled.") +@with_dbs +def disable_dataset( + dataset: str, + data_model: str, + version: str, + sqlite_db: SQLiteDB, + **_, +): + DisableDataset(db=sqlite_db).execute(dataset, data_model, version) + LOGGER.info("Dataset %s disabled in %s:%s.", dataset, data_model, version) -@entry.command() -@cl.argument("dataset", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("enable-data-model") +@cl.argument("name") +@cl.option("-v", "--version", required=True) @handle_errors -def enable_dataset(dataset, data_model, version, sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - EnableDataset(db=sqlite_db).execute(dataset, data_model, version) - print(f"Dataset {dataset} was successfully enabled.") +@with_dbs +def enable_data_model( + name: str, + version: str, + sqlite_db: SQLiteDB, + **_, +): + EnableDataModel(db=sqlite_db).execute(name, version) + LOGGER.info("Data-model %s:%s enabled.", name, version) -@entry.command() -@cl.argument("dataset", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("enable-dataset") +@cl.argument("dataset") +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) @handle_errors -def disable_dataset(dataset, data_model, version, sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - DisableDataset(sqlite_db).execute(dataset, data_model, version) - print(f"Dataset {dataset} was successfully disabled.") +@with_dbs +def enable_dataset( + dataset: str, + data_model: str, + version: str, + sqlite_db: SQLiteDB, + **_, +): + EnableDataset(db=sqlite_db).execute(dataset, data_model, version) + LOGGER.info("Dataset %s enabled in %s:%s.", dataset, data_model, version) -@entry.command() -@cl.argument("name", required=True) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "-t", - "--tag", - default=None, - required=True, - help="A tag to be added/removed", -) -@cl.option( - "-r", - "--remove", - is_flag=True, - required=False, - help="A flag that determines if the tag/key_value will be removed", -) -@cl.option( - "--force", - "-f", - is_flag=True, - help="Force overwrite on property", -) -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) -@handle_errors -def tag_data_model(name, version, tag, remove, force, sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) +def _parse_tag(tag: str, remove: bool, force: bool, target_name: str, version: str): if "=" in tag: - key, value = tag.split("=") + key, value = tag.split("=", 1) if remove: - RemovePropertyFromDataModel(db=sqlite_db).execute(name, version, key, value) - print(f"Property was successfully removed from data model {name}.") - else: - AddPropertyToDataModel(db=sqlite_db).execute( - name, version, key, value, force + return lambda db: ( + RemovePropertyFromDataModel(db=db).execute( + target_name, version, key, value + ) ) - print(f"Property was successfully added to data model {name}.") - else: - if remove: - UntagDataModel(db=sqlite_db).execute(name, version, tag) - print(f"Data model {name} was successfully untagged.") - else: - TagDataModel(db=sqlite_db).execute(name, version, tag) - print(f"Data model {name} was successfully tagged.") - - -@entry.command() -@cl.argument("dataset", required=True) -@cl.option( - "-d", - "--data-model", - required=True, - help="The data model to which the dataset is added", -) -@cl.option("-v", "--version", required=True, help="The data model version") -@cl.option( - "-t", - "--tag", - default=None, - required=True, - help="A tag to be added/removed", -) -@cl.option( - "-r", - "--remove", - is_flag=True, - required=False, - help="A flag that determines if the tag/key_value will be removed", -) -@cl.option( - "--force", - "-f", - is_flag=True, - help="Force overwrite on property", -) -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) + return lambda db: ( + AddPropertyToDataModel(db=db).execute( + target_name, version, key, value, force + ) + ) + if remove: + return lambda db: (UntagDataModel(db=db).execute(target_name, version, tag)) + return lambda db: (TagDataModel(db=db).execute(target_name, version, tag)) + + +@cli.command("tag-data-model") +@cl.argument("name") +@cl.option("-v", "--version", required=True) +@cl.option("-t", "--tag", required=True, help="Bare tag or key=value property.") +@cl.option("-r", "--remove", is_flag=True, help="Remove tag / property instead.") +@cl.option("-f", "--force", is_flag=True, help="Overwrite existing property.") @handle_errors +@with_dbs +def tag_data_model( + name: str, + version: str, + tag: str, + remove: bool, + force: bool, + sqlite_db: SQLiteDB, + **_, +): + action = _parse_tag(tag, remove, force, target_name=name, version=version) + action(sqlite_db) + LOGGER.info("Tag operation completed for data-model %s:%s.", name, version) + + +@cli.command("tag-dataset") +@cl.argument("dataset") +@cl.option("-d", "--data-model", "data_model", required=True) +@cl.option("-v", "--version", required=True) +@cl.option("-t", "--tag", required=True, help="Bare tag or key=value property.") +@cl.option("-r", "--remove", is_flag=True, help="Remove tag / property instead.") +@cl.option("-f", "--force", is_flag=True, help="Overwrite existing property.") +@handle_errors +@with_dbs def tag_dataset( - dataset, - data_model, - version, - tag, - remove, - force, - sqlite_db_path, + dataset: str, + data_model: str, + version: str, + tag: str, + remove: bool, + force: bool, + sqlite_db: SQLiteDB, + **_, ): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - - if "=" in tag: - key, value = tag.split("=") - if remove: - RemovePropertyFromDataset(db=sqlite_db).execute( - dataset, data_model, version, key, value - ) - print(f"Property was successfully removed from dataset {dataset}.") + def _dataset_action(db): + if "=" in tag: + key, value = tag.split("=", 1) + if remove: + RemovePropertyFromDataset(db=db).execute( + dataset, data_model, version, key, value + ) + else: + AddPropertyToDataset(db=db).execute( + dataset, data_model, version, key, value, force + ) else: - AddPropertyToDataset(db=sqlite_db).execute( - dataset, data_model, version, key, value, force - ) - print(f"Property was successfully added to dataset {dataset}.") - else: - if remove: - UntagDataset(db=sqlite_db).execute(dataset, data_model, version, tag) - print(f"Dataset {dataset} was successfully untagged.") - else: - TagDataset(db=sqlite_db).execute(dataset, data_model, version, tag) - print(f"Dataset {dataset} was successfully tagged.") + if remove: + UntagDataset(db=db).execute(dataset, data_model, version, tag) + else: + TagDataset(db=db).execute(dataset, data_model, version, tag) + + _dataset_action(sqlite_db) + LOGGER.info( + "Tag operation completed for dataset %s (%s:%s).", dataset, data_model, version + ) -@entry.command() -@cl.option( - "--sqlite_db_path", - "sqlite_db_path", - required=True, - help="The path for the sqlite database", - cls=NotRequiredIf, -) +@cli.command("list-data-models") @handle_errors -def list_data_models(sqlite_db_path): - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) +@with_dbs +def list_data_models(sqlite_db: SQLiteDB, **_): ListDataModels(db=sqlite_db).execute() -@entry.command() -@db_configs_options +@cli.command("list-datasets") @handle_errors -def list_datasets(ip, port, username, password, db_name, sqlite_db_path): - dbconfig = get_monetdb_config(ip, port, username, password, db_name) - monetdb = MonetDB.from_config(dbconfig) - sqlite_db = SQLiteDB.from_config({"db_path": sqlite_db_path}) - ListDatasets(sqlite_db=sqlite_db, monetdb=monetdb).execute() +@with_dbs +def list_datasets(sqlite_db: SQLiteDB, **_): + ListDatasets(sqlite_db=sqlite_db).execute() + + +entry = cli diff --git a/mipdb/databases/__init__.py b/mipdb/credentials.py similarity index 72% rename from mipdb/databases/__init__.py rename to mipdb/credentials.py index 233e890..c5d7d18 100644 --- a/mipdb/databases/__init__.py +++ b/mipdb/credentials.py @@ -1,17 +1,20 @@ import os - +from typing import Dict, Any import toml CONFIG_PATH = "/home/config.toml" -def credentials_from_config(): +def credentials_from_config() -> Dict[str, Any]: + """Return a dict of credentials from *config.toml* or sensible fall‑backs.""" + try: return toml.load(os.getenv("CONFIG_PATH", CONFIG_PATH)) except FileNotFoundError: return { "DB_IP": "", "DB_PORT": "", + "MONETDB_ENABLED": False, "MONETDB_ADMIN_USERNAME": "", "MONETDB_LOCAL_USERNAME": "", "MONETDB_LOCAL_PASSWORD": "", diff --git a/mipdb/exceptions.py b/mipdb/exceptions.py index a995817..45b4bbd 100644 --- a/mipdb/exceptions.py +++ b/mipdb/exceptions.py @@ -3,7 +3,7 @@ from functools import wraps from contextlib import contextmanager -sys.tracebacklimit = 0 +# sys.tracebacklimit = 0 class DataBaseError(Exception): diff --git a/mipdb/logger.py b/mipdb/logger.py new file mode 100644 index 0000000..b527516 --- /dev/null +++ b/mipdb/logger.py @@ -0,0 +1,7 @@ +import logging +import sys + +logging.basicConfig( + stream=sys.stderr, level=logging.INFO, format="%(levelname)s: %(message)s" +) +LOGGER = logging.getLogger(__name__) diff --git a/mipdb/monetdb/__init__.py b/mipdb/monetdb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mipdb/databases/monetdb.py b/mipdb/monetdb/monetdb.py similarity index 100% rename from mipdb/databases/monetdb.py rename to mipdb/monetdb/monetdb.py diff --git a/mipdb/databases/monetdb_tables.py b/mipdb/monetdb/monetdb_tables.py similarity index 97% rename from mipdb/databases/monetdb_tables.py rename to mipdb/monetdb/monetdb_tables.py index 0e508fe..1a5f6c2 100644 --- a/mipdb/databases/monetdb_tables.py +++ b/mipdb/monetdb/monetdb_tables.py @@ -6,11 +6,11 @@ import sqlalchemy as sql from sqlalchemy import MetaData +from mipdb.credentials import credentials_from_config from mipdb.data_frame import DATASET_COLUMN_NAME -from mipdb.databases import credentials_from_config from mipdb.dataelements import CommonDataElement from mipdb.exceptions import UserInputError -from mipdb.schema import Schema +from mipdb.monetdb.schema import Schema RECORDS_PER_COPY = 100000 @@ -158,8 +158,8 @@ def __init__(self, dataframe_sql_type_per_column, db): self._table.create(bind=db.get_executor()) def create(self, db): - db.execute(f'DROP TABLE IF EXISTS "{self.table.name}"') - self.table.create(bind=db.get_executor()) + db.execute(f'DROP TABLE IF EXISTS "{self.table.name}"') + self.table.create(bind=db.get_executor()) def validate_csv(self, csv_path, cdes_with_min_max, cdes_with_enumerations, db): validated_datasets = [] diff --git a/mipdb/schema.py b/mipdb/monetdb/schema.py similarity index 100% rename from mipdb/schema.py rename to mipdb/monetdb/schema.py diff --git a/mipdb/sqlite/__init__.py b/mipdb/sqlite/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mipdb/databases/sqlite.py b/mipdb/sqlite/sqlite.py similarity index 77% rename from mipdb/databases/sqlite.py rename to mipdb/sqlite/sqlite.py index 263588b..1e835de 100644 --- a/mipdb/databases/sqlite.py +++ b/mipdb/sqlite/sqlite.py @@ -84,7 +84,9 @@ def execute_fetchall(self, query: str, *args, **kwargs) -> List[Any]: result = conn.execute(text(query), *args, **kwargs) return result.fetchall() - def insert_values_to_table(self, table: sql.Table, values: List[Dict[str, Any]]) -> None: + def insert_values_to_table( + self, table: sql.Table, values: List[Dict[str, Any]] + ) -> None: session = self.Session() try: session.execute(table.insert(), values) @@ -96,8 +98,9 @@ def get_data_model_status(self, data_model_id: int) -> Any: session = self.Session() try: result = session.execute( - select(Base.metadata.tables["data_models"].c.status) - .where(Base.metadata.tables["data_models"].c.data_model_id == data_model_id) + select(Base.metadata.tables["data_models"].c.status).where( + Base.metadata.tables["data_models"].c.data_model_id == data_model_id + ) ).scalar_one_or_none() finally: session.close() @@ -108,7 +111,9 @@ def update_data_model_status(self, status: str, data_model_id: int) -> None: try: session.execute( sql.update(Base.metadata.tables["data_models"]) - .where(Base.metadata.tables["data_models"].c.data_model_id == data_model_id) + .where( + Base.metadata.tables["data_models"].c.data_model_id == data_model_id + ) .values(status=status) ) session.commit() @@ -119,8 +124,9 @@ def get_dataset_status(self, dataset_id: int) -> Any: session = self.Session() try: result = session.execute( - select(Base.metadata.tables["datasets"].c.status) - .where(Base.metadata.tables["datasets"].c.dataset_id == dataset_id) + select(Base.metadata.tables["datasets"].c.status).where( + Base.metadata.tables["datasets"].c.dataset_id == dataset_id + ) ).scalar_one_or_none() finally: session.close() @@ -158,7 +164,9 @@ def get_dataset(self, dataset_id: int, columns: List[str]) -> Any: try: cols = [getattr(Base.metadata.tables["datasets"].c, col) for col in columns] result = session.execute( - select(*cols).where(Base.metadata.tables["datasets"].c.dataset_id == dataset_id) + select(*cols).where( + Base.metadata.tables["datasets"].c.dataset_id == dataset_id + ) ).one_or_none() finally: session.close() @@ -167,9 +175,13 @@ def get_dataset(self, dataset_id: int, columns: List[str]) -> Any: def get_data_model(self, data_model_id: int, columns: List[str]) -> Any: session = self.Session() try: - cols = [getattr(Base.metadata.tables["data_models"].c, col) for col in columns] + cols = [ + getattr(Base.metadata.tables["data_models"].c, col) for col in columns + ] result = session.execute( - select(*cols).where(Base.metadata.tables["data_models"].c.data_model_id == data_model_id) + select(*cols).where( + Base.metadata.tables["data_models"].c.data_model_id == data_model_id + ) ).one_or_none() finally: session.close() @@ -180,10 +192,10 @@ def get_data_model(self, data_model_id: int, columns: List[str]) -> Any: # ... def get_values( - self, - table: sql.Table, - columns: List[str] | None = None, - where_conditions: Dict[str, Any] | None = None, + self, + table: sql.Table, + columns: List[str] | None = None, + where_conditions: Dict[str, Any] | None = None, ) -> List[sql.Row]: """Return rows (SQLAlchemy Row objects) respecting an optional WHERE.""" stmt = select( @@ -199,7 +211,9 @@ def get_values( def get_data_models(self, columns: List[str]) -> List[Dict[str, Any]]: session = self.Session() try: - cols = [getattr(Base.metadata.tables["data_models"].c, col) for col in columns] + cols = [ + getattr(Base.metadata.tables["data_models"].c, col) for col in columns + ] rows = session.execute(select(*cols)).all() finally: session.close() @@ -208,13 +222,12 @@ def get_data_models(self, columns: List[str]) -> List[Dict[str, Any]]: def get_dataset_count_by_data_model_id(self) -> List[Dict[str, Any]]: session = self.Session() try: - stmt = ( - select( - Base.metadata.tables["datasets"].c.data_model_id, - func.count(Base.metadata.tables["datasets"].c.data_model_id).label("count"), - ) - .group_by(Base.metadata.tables["datasets"].c.data_model_id) - ) + stmt = select( + Base.metadata.tables["datasets"].c.data_model_id, + func.count(Base.metadata.tables["datasets"].c.data_model_id).label( + "count" + ), + ).group_by(Base.metadata.tables["datasets"].c.data_model_id) rows = session.execute(stmt).all() finally: session.close() @@ -223,7 +236,9 @@ def get_dataset_count_by_data_model_id(self) -> List[Dict[str, Any]]: def get_row_count(self, table_name: str) -> int: session = self.Session() try: - count = session.execute(select(func.count()).select_from(text(table_name))).scalar_one() + count = session.execute( + select(func.count()).select_from(text(table_name)) + ).scalar_one() finally: session.close() return count @@ -253,8 +268,9 @@ def get_dataset_properties(self, dataset_id: int) -> Any: session = self.Session() try: result = session.execute( - select(Base.metadata.tables["datasets"].c.properties) - .where(Base.metadata.tables["datasets"].c.dataset_id == dataset_id) + select(Base.metadata.tables["datasets"].c.properties).where( + Base.metadata.tables["datasets"].c.dataset_id == dataset_id + ) ).scalar_one_or_none() finally: session.close() @@ -264,26 +280,33 @@ def get_data_model_properties(self, data_model_id: int) -> Any: session = self.Session() try: result = session.execute( - select(Base.metadata.tables["data_models"].c.properties) - .where(Base.metadata.tables["data_models"].c.data_model_id == data_model_id) + select(Base.metadata.tables["data_models"].c.properties).where( + Base.metadata.tables["data_models"].c.data_model_id == data_model_id + ) ).scalar_one_or_none() finally: session.close() return result or {} - def set_data_model_properties(self, properties: Dict[str, Any], data_model_id: int) -> None: + def set_data_model_properties( + self, properties: Dict[str, Any], data_model_id: int + ) -> None: session = self.Session() try: session.execute( sql.update(Base.metadata.tables["data_models"]) - .where(Base.metadata.tables["data_models"].c.data_model_id == data_model_id) + .where( + Base.metadata.tables["data_models"].c.data_model_id == data_model_id + ) .values(properties=properties) ) session.commit() finally: session.close() - def set_dataset_properties(self, properties: Dict[str, Any], dataset_id: int) -> None: + def set_dataset_properties( + self, properties: Dict[str, Any], dataset_id: int + ) -> None: session = self.Session() try: session.execute( @@ -298,20 +321,21 @@ def set_dataset_properties(self, properties: Dict[str, Any], dataset_id: int) -> def get_data_model_id(self, code: str, version: str) -> int: session = self.Session() try: - stmt = ( - select(Base.metadata.tables["data_models"].c.data_model_id) - .where( - Base.metadata.tables["data_models"].c.code == code, - Base.metadata.tables["data_models"].c.version == version, - ) + stmt = select(Base.metadata.tables["data_models"].c.data_model_id).where( + Base.metadata.tables["data_models"].c.code == code, + Base.metadata.tables["data_models"].c.version == version, ) data_model_id = session.execute(stmt).scalar_one_or_none() except MultipleResultsFound: - raise DataBaseError(f"Got more than one data_model ids for code={code} and version={version}.") + raise DataBaseError( + f"Got more than one data_model ids for code={code} and version={version}." + ) finally: session.close() if not data_model_id: - raise DataBaseError(f"Data_models table doesn't have a record with code={code}, version={version}") + raise DataBaseError( + f"Data_models table doesn't have a record with code={code}, version={version}" + ) return data_model_id def get_max_data_model_id(self) -> int: @@ -337,20 +361,21 @@ def get_max_dataset_id(self) -> int: def get_dataset_id(self, code: str, data_model_id: int) -> int: session = self.Session() try: - stmt = ( - select(Base.metadata.tables["datasets"].c.dataset_id) - .where( - Base.metadata.tables["datasets"].c.code == code, - Base.metadata.tables["datasets"].c.data_model_id == data_model_id, - ) + stmt = select(Base.metadata.tables["datasets"].c.dataset_id).where( + Base.metadata.tables["datasets"].c.code == code, + Base.metadata.tables["datasets"].c.data_model_id == data_model_id, ) dataset_id = session.execute(stmt).scalar_one_or_none() except MultipleResultsFound: - raise DataBaseError(f"Got more than one dataset ids for code={code} and data_model_id={data_model_id}.") + raise DataBaseError( + f"Got more than one dataset ids for code={code} and data_model_id={data_model_id}." + ) finally: session.close() if not dataset_id: - raise DataBaseError(f"Datasets table doesn't have a record with code={code}, data_model_id={data_model_id}") + raise DataBaseError( + f"Datasets table doesn't have a record with code={code}, data_model_id={data_model_id}" + ) return dataset_id def table_exists(self, table: sql.Table) -> bool: diff --git a/mipdb/databases/sqlite_tables.py b/mipdb/sqlite/sqlite_tables.py similarity index 93% rename from mipdb/databases/sqlite_tables.py rename to mipdb/sqlite/sqlite_tables.py index 8bbcd66..0f00150 100644 --- a/mipdb/databases/sqlite_tables.py +++ b/mipdb/sqlite/sqlite_tables.py @@ -3,13 +3,13 @@ import sqlalchemy as sql from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import Integer, String, JSON, select +from sqlalchemy import Integer, String, JSON, select from typing import List from mipdb.dataelements import CommonDataElement from mipdb.exceptions import DataBaseError -from mipdb.databases.sqlite import DataModel, Dataset +from mipdb.sqlite.sqlite import DataModel, Dataset METADATA_TABLE = "variables_metadata" PRIMARYDATA_TABLE = "primary_data" @@ -77,6 +77,7 @@ def get_column_distinct(self, column, db): def drop(self, db): db.drop_table(self._table) + class DataModelTable(Table): def __init__(self): self._table = DataModel.__table__ @@ -111,7 +112,7 @@ def delete_data_model(self, code, version, db): db.delete_from(self._table, where_conditions={"code": code, "version": version}) def get_next_data_model_id(self, db) -> int: - res = db.execute_fetchall('SELECT MAX(data_model_id) FROM data_models;') + res = db.execute_fetchall("SELECT MAX(data_model_id) FROM data_models;") max_id = res[0][0] if res and res[0][0] is not None else 0 return max_id + 1 @@ -123,14 +124,14 @@ def __init__(self): def get_datasets(self, db, columns: list = None): return db.get_values(table=self._table, columns=columns, where_conditions={}) - def get_dataset_codes(self, db, columns: List[str] = None, data_model_id: int = None) -> List[str]: - cols = columns or ['code'] + def get_dataset_codes( + self, db, columns: List[str] = None, data_model_id: int = None + ) -> List[str]: + cols = columns or ["code"] stmt = select(*[self.table.c[col] for col in cols]) if data_model_id is not None: stmt = stmt.where(self.table.c.data_model_id == data_model_id) - compiled_sql = str( - stmt.compile(compile_kwargs={"literal_binds": True}) - ) + compiled_sql = str(stmt.compile(compile_kwargs={"literal_binds": True})) rows = db.execute_fetchall(compiled_sql) codes: List[str] = [] for row in rows: diff --git a/mipdb/usecases.py b/mipdb/usecases.py index 52bbce0..836af34 100644 --- a/mipdb/usecases.py +++ b/mipdb/usecases.py @@ -1,13 +1,14 @@ import copy -import os from abc import ABC, abstractmethod +from typing import Optional import pandas as pd -from mipdb.databases.monetdb import MonetDB +from mipdb.logger import LOGGER +from mipdb.monetdb.monetdb import MonetDB from mipdb.data_frame_schema import DataFrameSchema from mipdb.exceptions import ForeignKeyError, InvalidDatasetError, UserInputError -from mipdb.databases.monetdb_tables import ( +from mipdb.monetdb.monetdb_tables import ( PrimaryDataTable, TemporaryTable, RECORDS_PER_COPY, @@ -23,9 +24,9 @@ get_cdes_with_enumerations, get_dataset_enums, ) -from mipdb.schema import Schema -from mipdb.databases.sqlite import SQLiteDB -from mipdb.databases.sqlite_tables import DataModelTable, DatasetsTable, MetadataTable +from mipdb.monetdb.schema import Schema +from mipdb.sqlite.sqlite import SQLiteDB +from mipdb.sqlite.sqlite_tables import DataModelTable, DatasetsTable, MetadataTable from mipdb.data_frame import DataFrame, DATASET_COLUMN_NAME LONGITUDINAL = "longitudinal" @@ -57,61 +58,80 @@ def execute(self) -> None: class AddDataModel(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db + self.monetdb = monetdb is_db_initialized(sqlite_db) - def execute(self, data_model_metadata) -> None: + def execute(self, data_model_metadata: dict) -> None: code, version = data_model_metadata["code"], data_model_metadata["version"] data_model = get_data_model_fullname(code, version) + cdes = flatten_cdes(copy.deepcopy(data_model_metadata)) - data_model_table = DataModelTable() - data_model_id = data_model_table.get_next_data_model_id(self.sqlite_db) self._create_primary_data_table(data_model, cdes) + self._create_metadata_table(data_model, cdes) - properties = Properties( - data_model_table.get_data_model_properties(data_model_id, self.sqlite_db) - ) - properties.add_property("cdes", data_model_metadata, True) - values = dict( - data_model_id=data_model_id, - code=code, - version=version, - label=data_model_metadata["label"], - status="ENABLED", - properties=properties.properties, - ) - data_model_table.insert_values(values, self.sqlite_db) + + self._insert_data_model_row(data_model_metadata) + self._tag_longitudinal_if_needed(data_model_metadata, code, version) - def _create_primary_data_table(self, data_model, cdes): + def _create_primary_data_table(self, data_model: str, cdes: list) -> None: + if self.monetdb is None: + LOGGER.debug("MonetDB disabled – skipping primary data table creation.") + return + with self.monetdb.begin() as conn: schema = Schema(data_model) schema.create(conn) PrimaryDataTable.from_cdes(schema, cdes).create(conn) - def _create_metadata_table(self, data_model, cdes): + def _create_metadata_table(self, data_model: str, cdes: list) -> None: metadata_table = MetadataTable(data_model) metadata_table.create(self.sqlite_db) - values = metadata_table.get_values_from_cdes(cdes) - metadata_table.insert_values(values, self.sqlite_db) + metadata_table.insert_values( + metadata_table.get_values_from_cdes(cdes), self.sqlite_db + ) - def _tag_longitudinal_if_needed(self, data_model_metadata, code, version): - if LONGITUDINAL in data_model_metadata: - longitudinal = data_model_metadata[LONGITUDINAL] - if not isinstance(longitudinal, bool): - raise UserInputError( - f"Longitudinal flag should be boolean, value given: {longitudinal}" - ) - if longitudinal: - TagDataModel(self.sqlite_db).execute( - code=code, version=version, tag=LONGITUDINAL - ) + def _insert_data_model_row(self, data_model_metadata: dict) -> None: + code, version = data_model_metadata["code"], data_model_metadata["version"] + dm_table = DataModelTable() + new_id = dm_table.get_next_data_model_id(self.sqlite_db) + + props = Properties(dm_table.get_data_model_properties(new_id, self.sqlite_db)) + props.add_property("cdes", data_model_metadata, force=True) + dm_table.insert_values( + dict( + data_model_id=new_id, + code=code, + version=version, + label=data_model_metadata["label"], + status="ENABLED", + properties=props.properties, + ), + self.sqlite_db, + ) + + def _tag_longitudinal_if_needed(self, meta: dict, code: str, version: str) -> None: + if LONGITUDINAL not in meta: + return + + longitudinal = meta[LONGITUDINAL] + if not isinstance(longitudinal, bool): + raise UserInputError( + f"Longitudinal flag should be boolean, value given: {longitudinal}" + ) + + if longitudinal: + TagDataModel(self.sqlite_db).execute( + code=code, version=version, tag=LONGITUDINAL + ) class ValidateDataModel(UseCase): def execute(self, data_model_metadata) -> None: + if "version" not in data_model_metadata: raise UserInputError( "You need to include a version on the CDEsMetadata.json" @@ -129,12 +149,13 @@ def execute(self, data_model_metadata) -> None: class DeleteDataModel(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db self.monetdb = monetdb is_db_initialized(sqlite_db) def execute(self, code, version, force) -> None: + name = get_data_model_fullname(code, version) schema = Schema(name) data_model_table = DataModelTable() @@ -145,9 +166,16 @@ def execute(self, code, version, force) -> None: self._validate_data_model_deletion(name, data_model_id) MetadataTable(data_model=name).drop(self.sqlite_db) self._delete_datasets(data_model_id, code, version) + self._drop_schema(schema) + data_model_table.delete_data_model(code, version, self.sqlite_db) + + def _drop_schema(self, schema): + if self.monetdb is None: + LOGGER.debug("MonetDB disabled – skipping primary data table creation.") + return + with self.monetdb.begin() as conn: schema.drop(conn) - data_model_table.delete_data_model(code, version, self.sqlite_db) def _validate_data_model_deletion(self, data_model_name, data_model_id): datasets = DatasetsTable().get_dataset_codes( @@ -172,7 +200,7 @@ def _delete_datasets(self, data_model_id, data_model_code, data_model_version): class ImportCSV(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db self.monetdb = monetdb is_db_initialized(sqlite_db) @@ -180,6 +208,7 @@ def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: def execute( self, csv_path, copy_from_file, data_model_code, data_model_version ) -> None: + data_model_name = get_data_model_fullname( code=data_model_code, version=data_model_version ) @@ -190,16 +219,10 @@ def execute( metadata_table = MetadataTable.from_db(data_model_name, self.sqlite_db) cdes = metadata_table.table dataset_enumerations = get_dataset_enums(cdes) - sql_type_per_column = get_sql_type_per_column(cdes) - with self.monetdb.begin() as monetdb_conn: - imported_datasets = ( - self.import_csv_with_volume( - csv_path, sql_type_per_column, data_model, monetdb_conn - ) - if copy_from_file - else self._import_csv(csv_path, data_model, monetdb_conn) - ) + imported_datasets = self._import_datasets( + csv_path, data_model, cdes, copy_from_file + ) existing_datasets = DatasetsTable().get_dataset_codes( columns=["code"], data_model_id=data_model_id, db=self.sqlite_db @@ -211,13 +234,32 @@ def execute( dataset_id=dataset_id, code=dataset, label=dataset_enumerations[dataset], - csv_path=csv_path if copy_from_file else None, + csv_path=str(csv_path) if copy_from_file else None, status="ENABLED", properties=None, ) DatasetsTable().insert_values(values, self.sqlite_db) dataset_id += 1 + def _import_datasets(self, csv_path, data_model, cdes, copy_from_file): + sql_type_per_column = get_sql_type_per_column(cdes) + + if self.monetdb is None: + LOGGER.debug("MonetDB disabled – skipping primary data table creation.") + df = pd.read_csv(csv_path, usecols=["dataset"]) + unique_datasets = df["dataset"].unique().tolist() + return unique_datasets + + with self.monetdb.begin() as monetdb_conn: + imported_datasets = ( + self.import_csv_with_volume( + csv_path, sql_type_per_column, data_model, monetdb_conn + ) + if copy_from_file + else self._import_csv(csv_path, data_model, monetdb_conn) + ) + return imported_datasets + def _get_next_dataset_id(self): return DatasetsTable().get_next_dataset_id(self.sqlite_db) @@ -329,7 +371,7 @@ class ValidateDataset(UseCase): Database constraints must NOT be used as part of the validation process since that could result in partially imported csvs. """ - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db self.monetdb = monetdb is_db_initialized(sqlite_db) @@ -337,23 +379,39 @@ def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: def execute( self, csv_path, copy_from_file, data_model_code, data_model_version ) -> None: + print(f"validate{csv_path}") + data_model = get_data_model_fullname( code=data_model_code, version=data_model_version ) + + metadata_table = MetadataTable.from_db(data_model, self.sqlite_db) + cdes = metadata_table.table + + dataset_enumerations = get_dataset_enums(cdes) + if self.is_data_model_longitudinal(data_model_code, data_model_version): + are_data_valid_longitudinal(csv_path) + validated_datasets = self._validate_datasets(csv_path, cdes, copy_from_file) + self.verify_datasets_exist_in_enumerations( + validated_datasets, dataset_enumerations + ) + + def _validate_datasets(self, csv_path, cdes, copy_from_file): csv_columns = pd.read_csv(csv_path, nrows=0).columns.tolist() if DATASET_COLUMN_NAME not in csv_columns: raise InvalidDatasetError( "The 'dataset' column is required to exist in the csv." ) - metadata_table = MetadataTable.from_db(data_model, self.sqlite_db) - cdes = metadata_table.table + + if self.monetdb is None: + LOGGER.debug("MonetDB disabled – skipping primary data table creation.") + df = pd.read_csv(csv_path, usecols=["dataset"]) + unique_datasets = df["dataset"].unique().tolist() + return unique_datasets + sql_type_per_column = get_sql_type_per_column(cdes) cdes_with_min_max = get_cdes_with_min_max(cdes, csv_columns) cdes_with_enumerations = get_cdes_with_enumerations(cdes, csv_columns) - dataset_enumerations = get_dataset_enums(cdes) - if self.is_data_model_longitudinal(data_model_code, data_model_version): - are_data_valid_longitudinal(csv_path) - if copy_from_file: with self.monetdb.begin() as monetdb_conn: validated_datasets = self.validate_csv_with_volume( @@ -367,9 +425,7 @@ def execute( validated_datasets = self.validate_csv( csv_path, sql_type_per_column, cdes_with_min_max, cdes_with_enumerations ) - self.verify_datasets_exist_in_enumerations( - validated_datasets, dataset_enumerations - ) + return validated_datasets def is_data_model_longitudinal(self, data_model_code, data_model_version): data_model_id = DataModelTable().get_data_model_id( @@ -507,7 +563,7 @@ def verify_datasets_exist_in_enumerations(self, datasets, dataset_enumerations): class DeleteDataset(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db self.monetdb = monetdb is_db_initialized(sqlite_db) @@ -516,11 +572,7 @@ def execute(self, dataset_code, data_model_code, data_model_version) -> None: data_model_fullname = get_data_model_fullname( code=data_model_code, version=data_model_version ) - with self.monetdb.begin() as conn: - primary_data_table = PrimaryDataTable.from_db( - Schema(data_model_fullname), conn - ) - primary_data_table.remove_dataset(dataset_code, data_model_fullname, conn) + self._remove_dataset(data_model_fullname, dataset_code) data_model_id = DataModelTable().get_data_model_id( data_model_code, data_model_version, self.sqlite_db ) @@ -529,6 +581,17 @@ def execute(self, dataset_code, data_model_code, data_model_version) -> None: ) DatasetsTable().delete_dataset(dataset_id, data_model_id, self.sqlite_db) + def _remove_dataset(self, data_model_fullname, dataset_code): + if self.monetdb is None: + LOGGER.debug("MonetDB disabled – skipping primary data table creation.") + return + + with self.monetdb.begin() as conn: + primary_data_table = PrimaryDataTable.from_db( + Schema(data_model_fullname), conn + ) + primary_data_table.remove_dataset(dataset_code, data_model_fullname, conn) + class EnableDataModel(UseCase): def __init__(self, db: SQLiteDB) -> None: @@ -768,9 +831,8 @@ def execute(self) -> None: class ListDatasets(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB) -> None: self.sqlite_db = sqlite_db - self.monetdb = monetdb def execute(self) -> None: dataset_table = DatasetsTable() @@ -781,7 +843,7 @@ def execute(self) -> None: datasets_info = [] for row in dataset_rows: - datasets_info.append(list(row)) + datasets_info.append(list(row)) if not datasets_info: print("There are no datasets.") @@ -792,7 +854,7 @@ def execute(self) -> None: class Cleanup(UseCase): - def __init__(self, sqlite_db: SQLiteDB, monetdb: MonetDB) -> None: + def __init__(self, sqlite_db: SQLiteDB, monetdb: Optional[MonetDB] | None) -> None: self.sqlite_db = sqlite_db self.monetdb = monetdb @@ -803,6 +865,7 @@ def execute(self) -> None: ) for code, version in data_model_rows: + DeleteDataModel(sqlite_db=self.sqlite_db, monetdb=self.monetdb).execute( code=code, version=version, force=True ) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index b7a2f82..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1833 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "astroid" -version = "3.3.11" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.9.0" -files = [ - {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, - {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} - -[[package]] -name = "asttokens" -version = "3.0.0" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = ">=3.8" -files = [ - {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, - {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, -] - -[package.extras] -astroid = ["astroid (>=2,<4)"] -test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2025.7.14" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.7" -files = [ - {file = "certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2"}, - {file = "certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, -] - -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.9.2" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912"}, - {file = "coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf"}, - {file = "coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2"}, - {file = "coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e"}, - {file = "coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e"}, - {file = "coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c"}, - {file = "coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba"}, - {file = "coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:326802760da234baf9f2f85a39e4a4b5861b94f6c8d95251f699e4f73b1835dc"}, - {file = "coverage-7.9.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e7be4cfec248df38ce40968c95d3952fbffd57b400d4b9bb580f28179556d2"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0b4a4cb73b9f2b891c1788711408ef9707666501ba23684387277ededab1097c"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2c8937fa16c8c9fbbd9f118588756e7bcdc7e16a470766a9aef912dd3f117dbd"}, - {file = "coverage-7.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42da2280c4d30c57a9b578bafd1d4494fa6c056d4c419d9689e66d775539be74"}, - {file = "coverage-7.9.2-cp311-cp311-win32.whl", hash = "sha256:14fa8d3da147f5fdf9d298cacc18791818f3f1a9f542c8958b80c228320e90c6"}, - {file = "coverage-7.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:549cab4892fc82004f9739963163fd3aac7a7b0df430669b75b86d293d2df2a7"}, - {file = "coverage-7.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:c2667a2b913e307f06aa4e5677f01a9746cd08e4b35e14ebcde6420a9ebb4c62"}, - {file = "coverage-7.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae9eb07f1cfacd9cfe8eaee6f4ff4b8a289a668c39c165cd0c8548484920ffc0"}, - {file = "coverage-7.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ce85551f9a1119f02adc46d3014b5ee3f765deac166acf20dbb851ceb79b6f3"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8f6389ac977c5fb322e0e38885fbbf901743f79d47f50db706e7644dcdcb6e1"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d9eae8cdfcd58fe7893b88993723583a6ce4dfbfd9f29e001922544f95615"}, - {file = "coverage-7.9.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae939811e14e53ed8a9818dad51d434a41ee09df9305663735f2e2d2d7d959b"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:31991156251ec202c798501e0a42bbdf2169dcb0f137b1f5c0f4267f3fc68ef9"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d0d67963f9cbfc7c7f96d4ac74ed60ecbebd2ea6eeb51887af0f8dce205e545f"}, - {file = "coverage-7.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:49b752a2858b10580969ec6af6f090a9a440a64a301ac1528d7ca5f7ed497f4d"}, - {file = "coverage-7.9.2-cp312-cp312-win32.whl", hash = "sha256:88d7598b8ee130f32f8a43198ee02edd16d7f77692fa056cb779616bbea1b355"}, - {file = "coverage-7.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:9dfb070f830739ee49d7c83e4941cc767e503e4394fdecb3b54bfdac1d7662c0"}, - {file = "coverage-7.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:4e2c058aef613e79df00e86b6d42a641c877211384ce5bd07585ed7ba71ab31b"}, - {file = "coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038"}, - {file = "coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14"}, - {file = "coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d"}, - {file = "coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868"}, - {file = "coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a"}, - {file = "coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b"}, - {file = "coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694"}, - {file = "coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5"}, - {file = "coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8"}, - {file = "coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e"}, - {file = "coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac"}, - {file = "coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926"}, - {file = "coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd"}, - {file = "coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb"}, - {file = "coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce"}, - {file = "coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a"}, - {file = "coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193"}, - {file = "coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed"}, - {file = "coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7"}, - {file = "coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441"}, - {file = "coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050"}, - {file = "coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4"}, - {file = "coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "decorator" -version = "5.2.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.8" -files = [ - {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, - {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, -] - -[[package]] -name = "dill" -version = "0.4.0" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, - {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "distlib" -version = "0.3.9" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, -] - -[[package]] -name = "docker" -version = "7.1.0" -description = "A Python library for the Docker Engine API." -optional = false -python-versions = ">=3.8" -files = [ - {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, - {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, -] - -[package.dependencies] -pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} -requests = ">=2.26.0" -urllib3 = ">=1.26.0" - -[package.extras] -dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] -docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] -ssh = ["paramiko (>=2.4.3)"] -websockets = ["websocket-client (>=1.3.0)"] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "executing" -version = "2.2.0" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.8" -files = [ - {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, - {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - -[[package]] -name = "fancycompleter" -version = "0.11.1" -description = "colorful TAB completion for Python prompt" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fancycompleter-0.11.1-py3-none-any.whl", hash = "sha256:44243d7fab37087208ca5acacf8f74c0aa4d733d04d593857873af7513cdf8a6"}, - {file = "fancycompleter-0.11.1.tar.gz", hash = "sha256:5b4ad65d76b32b1259251516d0f1cb2d82832b1ff8506697a707284780757f69"}, -] - -[package.dependencies] -pyreadline3 = {version = "*", markers = "platform_system == \"Windows\" and python_version < \"3.13\""} -pyrepl = {version = ">=0.11.3", markers = "python_version < \"3.13\""} - -[package.extras] -dev = ["fancycompleter[tests]", "mypy", "ruff (==0.11.8)"] -tests = ["pytest", "pytest-cov"] - -[[package]] -name = "filelock" -version = "3.18.0" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.9" -files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] - -[[package]] -name = "greenlet" -version = "3.2.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.9" -files = [ - {file = "greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, - {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac"}, - {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a433dbc54e4a37e4fff90ef34f25a8c00aed99b06856f0119dcf09fbafa16392"}, - {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:72e77ed69312bab0434d7292316d5afd6896192ac4327d44f3d613ecb85b037c"}, - {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:68671180e3849b963649254a882cd544a3c75bfcd2c527346ad8bb53494444db"}, - {file = "greenlet-3.2.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49c8cfb18fb419b3d08e011228ef8a25882397f3a859b9fe1436946140b6756b"}, - {file = "greenlet-3.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:efc6dc8a792243c31f2f5674b670b3a95d46fa1c6a912b8e310d6f542e7b0712"}, - {file = "greenlet-3.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:731e154aba8e757aedd0781d4b240f1225b075b4409f1bb83b05ff410582cf00"}, - {file = "greenlet-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:96c20252c2f792defe9a115d3287e14811036d51e78b3aaddbee23b69b216302"}, - {file = "greenlet-3.2.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822"}, - {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83"}, - {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf"}, - {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b"}, - {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147"}, - {file = "greenlet-3.2.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5"}, - {file = "greenlet-3.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc"}, - {file = "greenlet-3.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba"}, - {file = "greenlet-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34"}, - {file = "greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d"}, - {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b"}, - {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d"}, - {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264"}, - {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688"}, - {file = "greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb"}, - {file = "greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c"}, - {file = "greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163"}, - {file = "greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849"}, - {file = "greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad"}, - {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef"}, - {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3"}, - {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95"}, - {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb"}, - {file = "greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b"}, - {file = "greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0"}, - {file = "greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36"}, - {file = "greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3"}, - {file = "greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86"}, - {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97"}, - {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728"}, - {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a"}, - {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892"}, - {file = "greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141"}, - {file = "greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a"}, - {file = "greenlet-3.2.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:42efc522c0bd75ffa11a71e09cd8a399d83fafe36db250a87cf1dacfaa15dc64"}, - {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d760f9bdfe79bff803bad32b4d8ffb2c1d2ce906313fc10a83976ffb73d64ca7"}, - {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8324319cbd7b35b97990090808fdc99c27fe5338f87db50514959f8059999805"}, - {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8c37ef5b3787567d322331d5250e44e42b58c8c713859b8a04c6065f27efbf72"}, - {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce539fb52fb774d0802175d37fcff5c723e2c7d249c65916257f0a940cee8904"}, - {file = "greenlet-3.2.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:003c930e0e074db83559edc8705f3a2d066d4aa8c2f198aff1e454946efd0f26"}, - {file = "greenlet-3.2.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7e70ea4384b81ef9e84192e8a77fb87573138aa5d4feee541d8014e452b434da"}, - {file = "greenlet-3.2.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:22eb5ba839c4b2156f18f76768233fe44b23a31decd9cc0d4cc8141c211fd1b4"}, - {file = "greenlet-3.2.3-cp39-cp39-win32.whl", hash = "sha256:4532f0d25df67f896d137431b13f4cdce89f7e3d4a96387a41290910df4d3a57"}, - {file = "greenlet-3.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:aaa7aae1e7f75eaa3ae400ad98f8644bb81e1dc6ba47ce8a93d3f17274e08322"}, - {file = "greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - -[[package]] -name = "identify" -version = "2.6.12" -description = "File identification library for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, - {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "ipython" -version = "8.37.0" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.10" -files = [ - {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, - {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt_toolkit = ">=3.0.41,<3.1.0" -pygments = ">=2.4.0" -stack_data = "*" -traitlets = ">=5.13.0" -typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} - -[package.extras] -all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] -kernel = ["ipykernel"] -matplotlib = ["matplotlib"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] - -[[package]] -name = "isort" -version = "6.0.1" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.9.0" -files = [ - {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, - {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, -] - -[package.extras] -colors = ["colorama"] -plugins = ["setuptools"] - -[[package]] -name = "jedi" -version = "0.19.2" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, - {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, -] - -[package.dependencies] -parso = ">=0.8.4,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] - -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "numpy" -version = "2.2.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -files = [ - {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, - {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, - {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, - {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, - {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, - {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, - {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, - {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, - {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, - {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, - {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, - {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, -] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pandas" -version = "2.3.1" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pandas-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22c2e866f7209ebc3a8f08d75766566aae02bcc91d196935a1d9e59c7b990ac9"}, - {file = "pandas-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3583d348546201aff730c8c47e49bc159833f971c2899d6097bce68b9112a4f1"}, - {file = "pandas-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f951fbb702dacd390561e0ea45cdd8ecfa7fb56935eb3dd78e306c19104b9b0"}, - {file = "pandas-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd05b72ec02ebfb993569b4931b2e16fbb4d6ad6ce80224a3ee838387d83a191"}, - {file = "pandas-2.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1b916a627919a247d865aed068eb65eb91a344b13f5b57ab9f610b7716c92de1"}, - {file = "pandas-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fe67dc676818c186d5a3d5425250e40f179c2a89145df477dd82945eaea89e97"}, - {file = "pandas-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eb789ae0274672acbd3c575b0598d213345660120a257b47b5dafdc618aec83"}, - {file = "pandas-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2b0540963d83431f5ce8870ea02a7430adca100cec8a050f0811f8e31035541b"}, - {file = "pandas-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fe7317f578c6a153912bd2292f02e40c1d8f253e93c599e82620c7f69755c74f"}, - {file = "pandas-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6723a27ad7b244c0c79d8e7007092d7c8f0f11305770e2f4cd778b3ad5f9f85"}, - {file = "pandas-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3462c3735fe19f2638f2c3a40bd94ec2dc5ba13abbb032dd2fa1f540a075509d"}, - {file = "pandas-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:98bcc8b5bf7afed22cc753a28bc4d9e26e078e777066bc53fac7904ddef9a678"}, - {file = "pandas-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d544806b485ddf29e52d75b1f559142514e60ef58a832f74fb38e48d757b299"}, - {file = "pandas-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b3cd4273d3cb3707b6fffd217204c52ed92859533e31dc03b7c5008aa933aaab"}, - {file = "pandas-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:689968e841136f9e542020698ee1c4fbe9caa2ed2213ae2388dc7b81721510d3"}, - {file = "pandas-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:025e92411c16cbe5bb2a4abc99732a6b132f439b8aab23a59fa593eb00704232"}, - {file = "pandas-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7ff55f31c4fcb3e316e8f7fa194566b286d6ac430afec0d461163312c5841e"}, - {file = "pandas-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dcb79bf373a47d2a40cf7232928eb7540155abbc460925c2c96d2d30b006eb4"}, - {file = "pandas-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56a342b231e8862c96bdb6ab97170e203ce511f4d0429589c8ede1ee8ece48b8"}, - {file = "pandas-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ca7ed14832bce68baef331f4d7f294411bed8efd032f8109d690df45e00c4679"}, - {file = "pandas-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ac942bfd0aca577bef61f2bc8da8147c4ef6879965ef883d8e8d5d2dc3e744b8"}, - {file = "pandas-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9026bd4a80108fac2239294a15ef9003c4ee191a0f64b90f170b40cfb7cf2d22"}, - {file = "pandas-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6de8547d4fdb12421e2d047a2c446c623ff4c11f47fddb6b9169eb98ffba485a"}, - {file = "pandas-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782647ddc63c83133b2506912cc6b108140a38a37292102aaa19c81c83db2928"}, - {file = "pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9"}, - {file = "pandas-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e5635178b387bd2ba4ac040f82bc2ef6e6b500483975c4ebacd34bec945fda12"}, - {file = "pandas-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f3bf5ec947526106399a9e1d26d40ee2b259c66422efdf4de63c848492d91bb"}, - {file = "pandas-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c78cf43c8fde236342a1cb2c34bcff89564a7bfed7e474ed2fffa6aed03a956"}, - {file = "pandas-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8dfc17328e8da77be3cf9f47509e5637ba8f137148ed0e9b5241e1baf526e20a"}, - {file = "pandas-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ec6c851509364c59a5344458ab935e6451b31b818be467eb24b0fe89bd05b6b9"}, - {file = "pandas-2.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:911580460fc4884d9b05254b38a6bfadddfcc6aaef856fb5859e7ca202e45275"}, - {file = "pandas-2.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f4d6feeba91744872a600e6edbbd5b033005b431d5ae8379abee5bcfa479fab"}, - {file = "pandas-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe37e757f462d31a9cd7580236a82f353f5713a80e059a29753cf938c6775d96"}, - {file = "pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444"}, - {file = "pandas-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4645f770f98d656f11c69e81aeb21c6fca076a44bed3dcbb9396a4311bc7f6d8"}, - {file = "pandas-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:342e59589cc454aaff7484d75b816a433350b3d7964d7847327edda4d532a2e3"}, - {file = "pandas-2.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d12f618d80379fde6af007f65f0c25bd3e40251dbd1636480dfffce2cf1e6da"}, - {file = "pandas-2.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd71c47a911da120d72ef173aeac0bf5241423f9bfea57320110a978457e069e"}, - {file = "pandas-2.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09e3b1587f0f3b0913e21e8b32c3119174551deb4a4eba4a89bc7377947977e7"}, - {file = "pandas-2.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2323294c73ed50f612f67e2bf3ae45aea04dce5690778e08a09391897f35ff88"}, - {file = "pandas-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4b0de34dc8499c2db34000ef8baad684cfa4cbd836ecee05f323ebfba348c7d"}, - {file = "pandas-2.3.1.tar.gz", hash = "sha256:0a95b9ac964fe83ce317827f80304d37388ea77616b1425f0ae41c9d2d0d7bb2"}, -] - -[package.dependencies] -numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" - -[package.extras] -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)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -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)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] - -[[package]] -name = "pandera" -version = "0.25.0" -description = "A light-weight and flexible data validation and testing tool for statistical data objects." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pandera-0.25.0-py3-none-any.whl", hash = "sha256:365a555accc46404466641203e297722d424d74a1315f077ab899e1344f82303"}, - {file = "pandera-0.25.0.tar.gz", hash = "sha256:af3bbaa163672c91b83d59d70715f25c4134dbccfc8bc89a642a2f0e23db951e"}, -] - -[package.dependencies] -packaging = ">=20.0" -pydantic = "*" -typeguard = "*" -typing_extensions = "*" -typing_inspect = ">=0.6.0" - -[package.extras] -all = ["black", "dask[dataframe]", "distributed", "fastapi", "frictionless (<=4.40.8)", "geopandas (<1.1.0)", "hypothesis (>=6.92.7)", "ibis-framework (>=9.0.0)", "modin", "pandas-stubs", "polars (>=0.20.0)", "pyspark[connect] (>=3.2.0,<4.0.0)", "pyyaml (>=5.1)", "ray", "scipy", "shapely"] -dask = ["dask[dataframe]", "distributed"] -fastapi = ["fastapi"] -geopandas = ["geopandas (<1.1.0)", "shapely"] -hypotheses = ["scipy"] -ibis = ["ibis-framework (>=9.0.0)"] -io = ["black", "frictionless (<=4.40.8)", "pyyaml (>=5.1)"] -modin = ["dask[dataframe]", "distributed", "modin", "ray"] -modin-dask = ["dask[dataframe]", "distributed", "modin"] -modin-ray = ["modin", "ray"] -mypy = ["pandas-stubs"] -pandas = ["numpy (>=1.24.4)", "pandas (>=2.1.1)"] -polars = ["polars (>=0.20.0)"] -pyspark = ["pyspark[connect] (>=3.2.0,<4.0.0)"] -strategies = ["hypothesis (>=6.92.7)"] - -[[package]] -name = "parso" -version = "0.8.4" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, -] - -[package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["docopt", "pytest"] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pdbpp" -version = "0.11.6" -description = "pdb++, a drop-in replacement for pdb" -optional = false -python-versions = "*" -files = [ - {file = "pdbpp-0.11.6-py3-none-any.whl", hash = "sha256:8e024d36bd2f35a3b19d8732524c696b8b4aef633250d28547198e746cd81ccb"}, - {file = "pdbpp-0.11.6.tar.gz", hash = "sha256:36a73c5bcf0c3c35034be4cf99e6106e3ee0c8f5e0faafc2cf9be5f1481eb4b7"}, -] - -[package.dependencies] -fancycompleter = ">=0.11.0" -pygments = "*" - -[package.extras] -testing = ["ipython", "pexpect", "pytest", "pytest-cov"] - -[[package]] -name = "pexpect" -version = "4.9.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pluggy" -version = "1.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "4.2.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, - {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "prompt-toolkit" -version = "3.0.51" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, - {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, - {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pydantic" -version = "2.11.7" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, - {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pygments" -version = "2.19.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pylint" -version = "3.3.7" -description = "python code static checker" -optional = false -python-versions = ">=3.9.0" -files = [ - {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, - {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, -] - -[package.dependencies] -astroid = ">=3.3.8,<=3.4.0.dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.2", markers = "python_version < \"3.11\""} -isort = ">=4.2.5,<5.13 || >5.13,<7" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2" -tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pymonetdb" -version = "1.8.4" -description = "Native MonetDB client Python API" -optional = false -python-versions = "*" -files = [ - {file = "pymonetdb-1.8.4-py2.py3-none-any.whl", hash = "sha256:6d196da769566f4052135c372251e4a9f20727599e711960153dfbb296081244"}, - {file = "pymonetdb-1.8.4.tar.gz", hash = "sha256:f266573a4303ba2f9cf7c7349883c72b8a7fbff7f9b8080b70b64c2f2ff2afa1"}, -] - -[package.extras] -doc = ["sphinx", "sphinx-rtd-theme"] -test = ["mypy", "pycodestyle", "pytest", "types-setuptools"] - -[[package]] -name = "pyreadline3" -version = "3.5.4" -description = "A python implementation of GNU readline." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, - {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, -] - -[package.extras] -dev = ["build", "flake8", "mypy", "pytest", "twine"] - -[[package]] -name = "pyrepl" -version = "0.11.3.post1" -description = "A library for building flexible command line interfaces" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyrepl-0.11.3.post1-py3-none-any.whl", hash = "sha256:59fcd67588892731dc6e7aff106c380d303d54324ff028827804a2b056223d92"}, - {file = "pyrepl-0.11.3.post1.tar.gz", hash = "sha256:0ca7568c8be919b69f99644d29d31738a5b1a87750d06dd36564bcfad278d402"}, -] - -[package.extras] -dev = ["pyrepl[tests]", "ruff (==0.9.3)"] -tests = ["pexpect", "pytest", "pytest-coverage", "pytest-timeout"] - -[[package]] -name = "pytest" -version = "8.4.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, - {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, -] - -[package.dependencies] -colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} -iniconfig = ">=1" -packaging = ">=20" -pluggy = ">=1.5,<2" -pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, - {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, -] - -[package.dependencies] -coverage = {version = ">=7.5", extras = ["toml"]} -pluggy = ">=1.2" -pytest = ">=6.2.5" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-xdist" -version = "3.8.0" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, - {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, -] - -[package.dependencies] -execnet = ">=2.1" -pytest = ">=7.0.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytoolconfig" -version = "1.3.1" -description = "Python tool configuration" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytoolconfig-1.3.1-py3-none-any.whl", hash = "sha256:5d8cea8ae1996938ec3eaf44567bbc5ef1bc900742190c439a44a704d6e1b62b"}, - {file = "pytoolconfig-1.3.1.tar.gz", hash = "sha256:51e6bd1a6f108238ae6aab6a65e5eed5e75d456be1c2bf29b04e5c1e7d7adbae"}, -] - -[package.dependencies] -packaging = ">=23.2" -platformdirs = {version = ">=3.11.0", optional = true, markers = "extra == \"global\""} -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -doc = ["sphinx (>=7.1.2)", "tabulate (>=0.9.0)"] -gendocs = ["pytoolconfig[doc]", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-rtd-theme (>=2.0.0)"] -global = ["platformdirs (>=3.11.0)"] -validation = ["pydantic (>=2.5.3)"] - -[[package]] -name = "pytz" -version = "2025.2" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, - {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, -] - -[[package]] -name = "pywin32" -version = "311" -description = "Python for Window Extensions" -optional = false -python-versions = "*" -files = [ - {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, - {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, - {file = "pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b"}, - {file = "pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151"}, - {file = "pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503"}, - {file = "pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2"}, - {file = "pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31"}, - {file = "pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067"}, - {file = "pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852"}, - {file = "pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d"}, - {file = "pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d"}, - {file = "pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a"}, - {file = "pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee"}, - {file = "pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87"}, - {file = "pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42"}, - {file = "pywin32-311-cp38-cp38-win32.whl", hash = "sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c"}, - {file = "pywin32-311-cp38-cp38-win_amd64.whl", hash = "sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd"}, - {file = "pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b"}, - {file = "pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91"}, - {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "requests" -version = "2.32.4" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -files = [ - {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, - {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rope" -version = "1.14.0" -description = "a python refactoring library..." -optional = false -python-versions = ">=3.8" -files = [ - {file = "rope-1.14.0-py3-none-any.whl", hash = "sha256:00a7ea8c0c376fc0b053b2f2f8ef3bfb8b50fecf1ebf3eb80e4f8bd7f1941918"}, - {file = "rope-1.14.0.tar.gz", hash = "sha256:8803e3b667315044f6270b0c69a10c0679f9f322ed8efe6245a93ceb7658da69"}, -] - -[package.dependencies] -pytoolconfig = {version = ">=1.2.2", extras = ["global"]} - -[package.extras] -dev = ["build (>=0.7.0)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-cov (>=4.1.0)", "pytest-timeout (>=2.1.0)"] -doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] -release = ["pip-tools (>=6.12.1)", "toml (>=0.10.2)", "twine (>=4.0.2)"] - -[[package]] -name = "setuptools" -version = "80.9.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -files = [ - {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, - {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "sqlalchemy" -version = "2.0.41" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4af17bda11e907c51d10686eda89049f9ce5669b08fbe71a29747f1e876036"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c0b0e5e1b5d9f3586601048dd68f392dc0cc99a59bb5faf18aab057ce00d00b2"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0b3dbf1e7e9bc95f4bac5e2fb6d3fb2f083254c3fdd20a1789af965caf2d2348"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-win32.whl", hash = "sha256:1e3f196a0c59b0cae9a0cd332eb1a4bda4696e863f4f1cf84ab0347992c548c2"}, - {file = "SQLAlchemy-2.0.41-cp37-cp37m-win_amd64.whl", hash = "sha256:6ab60a5089a8f02009f127806f777fca82581c49e127f08413a66056bd9166dd"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b1f09b6821406ea1f94053f346f28f8215e293344209129a9c0fcc3578598d7b"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1936af879e3db023601196a1684d28e12f19ccf93af01bf3280a3262c4b6b4e5"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2ac41acfc8d965fb0c464eb8f44995770239668956dc4cdf502d1b1ffe0d747"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c24e0c0fde47a9723c81d5806569cddef103aebbf79dbc9fcbb617153dea30"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23a8825495d8b195c4aa9ff1c430c28f2c821e8c5e2d98089228af887e5d7e29"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:60c578c45c949f909a4026b7807044e7e564adf793537fc762b2489d522f3d11"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-win32.whl", hash = "sha256:118c16cd3f1b00c76d69343e38602006c9cfb9998fa4f798606d28d63f23beda"}, - {file = "sqlalchemy-2.0.41-cp310-cp310-win_amd64.whl", hash = "sha256:7492967c3386df69f80cf67efd665c0f667cee67032090fe01d7d74b0e19bb08"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6375cd674fe82d7aa9816d1cb96ec592bac1726c11e0cafbf40eeee9a4516b5f"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f8c9fdd15a55d9465e590a402f42082705d66b05afc3ffd2d2eb3c6ba919560"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f9dc8c44acdee06c8fc6440db9eae8b4af8b01e4b1aee7bdd7241c22edff4f"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c11ceb9a1f482c752a71f203a81858625d8df5746d787a4786bca4ffdf71c6"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:911cc493ebd60de5f285bcae0491a60b4f2a9f0f5c270edd1c4dbaef7a38fc04"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03968a349db483936c249f4d9cd14ff2c296adfa1290b660ba6516f973139582"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-win32.whl", hash = "sha256:293cd444d82b18da48c9f71cd7005844dbbd06ca19be1ccf6779154439eec0b8"}, - {file = "sqlalchemy-2.0.41-cp311-cp311-win_amd64.whl", hash = "sha256:3d3549fc3e40667ec7199033a4e40a2f669898a00a7b18a931d3efb4c7900504"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-win32.whl", hash = "sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6"}, - {file = "sqlalchemy-2.0.41-cp312-cp312-win_amd64.whl", hash = "sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-win32.whl", hash = "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f"}, - {file = "sqlalchemy-2.0.41-cp313-cp313-win_amd64.whl", hash = "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:90144d3b0c8b139408da50196c5cad2a6909b51b23df1f0538411cd23ffa45d3"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:023b3ee6169969beea3bb72312e44d8b7c27c75b347942d943cf49397b7edeb5"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725875a63abf7c399d4548e686debb65cdc2549e1825437096a0af1f7e374814"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81965cc20848ab06583506ef54e37cf15c83c7e619df2ad16807c03100745dea"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dd5ec3aa6ae6e4d5b5de9357d2133c07be1aff6405b136dad753a16afb6717dd"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ff8e80c4c4932c10493ff97028decfdb622de69cae87e0f127a7ebe32b4069c6"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-win32.whl", hash = "sha256:4d44522480e0bf34c3d63167b8cfa7289c1c54264c2950cc5fc26e7850967e45"}, - {file = "sqlalchemy-2.0.41-cp38-cp38-win_amd64.whl", hash = "sha256:81eedafa609917040d39aa9332e25881a8e7a0862495fcdf2023a9667209deda"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9a420a91913092d1e20c86a2f5f1fc85c1a8924dbcaf5e0586df8aceb09c9cc2"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:906e6b0d7d452e9a98e5ab8507c0da791856b2380fdee61b765632bb8698026f"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a373a400f3e9bac95ba2a06372c4fd1412a7cee53c37fc6c05f829bf672b8769"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:087b6b52de812741c27231b5a3586384d60c353fbd0e2f81405a814b5591dc8b"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:34ea30ab3ec98355235972dadc497bb659cc75f8292b760394824fab9cf39826"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8280856dd7c6a68ab3a164b4a4b1c51f7691f6d04af4d4ca23d6ecf2261b7923"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-win32.whl", hash = "sha256:b50eab9994d64f4a823ff99a0ed28a6903224ddbe7fef56a6dd865eec9243440"}, - {file = "sqlalchemy-2.0.41-cp39-cp39-win_amd64.whl", hash = "sha256:5e22575d169529ac3e0a120cf050ec9daa94b6a9597993d1702884f6954a7d71"}, - {file = "sqlalchemy-2.0.41-py3-none-any.whl", hash = "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576"}, - {file = "sqlalchemy-2.0.41.tar.gz", hash = "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9"}, -] - -[package.dependencies] -greenlet = {version = ">=1", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] -aioodbc = ["aioodbc", "greenlet (>=1)"] -aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (>=1)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - -[[package]] -name = "sqlalchemy-monetdb" -version = "2.0.0" -description = "SQLAlchemy dialect for MonetDB" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sqlalchemy_monetdb-2.0.0-py2.py3-none-any.whl", hash = "sha256:3fcd816fb596c8ffa1ea9970ca98c084dcb1ed27de75d760192bc44f52607d9c"}, - {file = "sqlalchemy_monetdb-2.0.0.tar.gz", hash = "sha256:0a0e9ebfb4ab5b2a748a92f2a536aa88c77384b84d573c4c9035b05c5b6a32eb"}, -] - -[package.dependencies] -pymonetdb = ">=1.8.2" -sqlalchemy = ">=2.0.34" - -[package.extras] -test = ["coverage (>=7.3.0)", "flake8 (>=6.1.0)", "mypy (>=1.5.1)", "pytest (>=7.4.0)", "pytest-cov (>=4.1.0)"] - -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] -name = "tomlkit" -version = "0.13.3" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, - {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, - {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] - -[[package]] -name = "typeguard" -version = "4.4.4" -description = "Run-time type checker for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "typeguard-4.4.4-py3-none-any.whl", hash = "sha256:b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e"}, - {file = "typeguard-4.4.4.tar.gz", hash = "sha256:3a7fd2dffb705d4d0efaed4306a704c89b9dee850b688f060a8b1615a79e5f74"}, -] - -[package.dependencies] -typing_extensions = ">=4.14.0" - -[[package]] -name = "typing-extensions" -version = "4.14.1" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -files = [ - {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, - {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, -] - -[[package]] -name = "typing-inspect" -version = "0.9.0" -description = "Runtime inspection utilities for typing module." -optional = false -python-versions = "*" -files = [ - {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, - {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, -] - -[package.dependencies] -mypy-extensions = ">=0.3.0" -typing-extensions = ">=3.7.4" - -[[package]] -name = "typing-inspection" -version = "0.4.1" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -files = [ - {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, - {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[[package]] -name = "tzdata" -version = "2025.2" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, - {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "virtualenv" -version = "20.31.2" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.8" -files = [ - {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, - {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "~3.10" -content-hash = "ed1883041e1dd692ce6426268a7a98f6a8b93275da5c79133bd61d339988c1fc" diff --git a/pyproject.toml b/pyproject.toml index 901eed4..51d1b0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "mipdb" -version = "3.0.8" +version = "3.1.0" description = "" authors = ["Your Name "] @@ -11,7 +11,7 @@ sqlalchemy_monetdb = "~2.0" pymonetdb = "~1.8" pandas = "~2.3" pandera = "~0.25" -click = "~8.2" +click = "~8.1" toml = "~0.10" numpy = "~2.2" setuptools = "~80.9" diff --git a/tests/conftest.py b/tests/conftest.py index 4231b15..b0e8a87 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,11 +4,10 @@ import pytest import docker -from mipdb.commands import get_monetdb_config -from mipdb.databases.monetdb import MonetDB -from mipdb.databases.monetdb_tables import User +from mipdb.monetdb.monetdb import MonetDB +from mipdb.monetdb.monetdb_tables import User from mipdb.reader import JsonFileReader -from mipdb.databases.sqlite import SQLiteDB +from mipdb.sqlite.sqlite import SQLiteDB TEST_DIR = os.path.dirname(os.path.realpath(__file__)) SQLiteDB_PATH = f"{TEST_DIR}/sqlite.db" @@ -29,8 +28,24 @@ USERNAME = "admin" PASSWORD = "executor" DB_NAME = "db" -SQLiteDB_OPTION = ["--sqlite_db_path", SQLiteDB_PATH] +SQLiteDB_OPTION = ["--sqlite", SQLiteDB_PATH] + +NO_MONETDB_OPTIONS = [ + "--no-monetdb", + "--ip", + IP, + "--port", + PORT, + "--username", + USERNAME, + "--password", + PASSWORD, + "--db-name", + DB_NAME, +] + MONETDB_OPTIONS = [ + "--monetdb", "--ip", IP, "--port", @@ -39,7 +54,7 @@ USERNAME, "--password", PASSWORD, - "--db_name", + "--db-name", DB_NAME, ] @@ -98,10 +113,17 @@ def sqlite_db(): @pytest.fixture(scope="function") def monetdb(): - dbconfig = get_monetdb_config(IP, PORT, USERNAME, PASSWORD, DB_NAME) + dbconfig = { + "ip": IP, + "port": PORT, + "dbfarm": DB_NAME, + "username": USERNAME, + "password": PASSWORD, + } return MonetDB.from_config(dbconfig) +@pytest.fixture(scope="function") def cleanup_monetdb(monetdb): schemas = monetdb.get_schemas() for schema in schemas: @@ -109,15 +131,9 @@ def cleanup_monetdb(monetdb): monetdb.drop_schema(schema) +@pytest.fixture(scope="function") def cleanup_sqlite(sqlite_db): sqlite_tables = sqlite_db.get_all_tables() if sqlite_tables: for table in sqlite_tables: sqlite_db.execute(f'DROP TABLE "{table}";') - - -@pytest.fixture(scope="function") -def cleanup_db(sqlite_db, monetdb): - yield - cleanup_sqlite(sqlite_db) - cleanup_monetdb(monetdb) diff --git a/tests/test_commands.py b/tests/test_commands.py index 77805d1..d85ff77 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -2,26 +2,10 @@ import pytest from click.testing import CliRunner - -from mipdb import init -from mipdb import add_data_model -from mipdb import delete_data_model -from mipdb import add_dataset -from mipdb import delete_dataset -from mipdb import disable_dataset -from mipdb import disable_data_model -from mipdb import enable_dataset -from mipdb import enable_data_model -from mipdb import load_folder -from mipdb import tag_dataset -from mipdb import tag_data_model -from mipdb import list_data_models -from mipdb import list_datasets -from mipdb import validate_dataset -from mipdb.commands import validate_folder from mipdb.exceptions import ExitCode -from mipdb.databases.sqlite import Dataset, DataModel -from mipdb.databases.sqlite_tables import DataModelTable +from mipdb.sqlite.sqlite import Dataset, DataModel +from mipdb.sqlite.sqlite_tables import DataModelTable +from mipdb.commands import entry as cli from tests.conftest import ( DATASET_FILE, ABSOLUTE_PATH_DATASET_FILE, @@ -30,36 +14,91 @@ ABSOLUTE_PATH_FAIL_DATA_FOLDER, MONETDB_OPTIONS, SQLiteDB_OPTION, - ABSOLUTE_PATH_DATASET_FILE_MULTIPLE_DATASET, + ABSOLUTE_PATH_DATASET_FILE_MULTIPLE_DATASET, NO_MONETDB_OPTIONS, ) from tests.conftest import DATA_MODEL_FILE +import pytest + +def _bootstrap_data_model(sqlite_db): + run_cli_command("init") + # Test + result = run_cli_command("add-data-model", DATA_MODEL_FILE) + assert result.exit_code == ExitCode.OK + +def _bootstrap_dataset(sqlite_db): + _bootstrap_data_model(sqlite_db) + res = run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", + ) + assert res.exit_code == ExitCode.OK + assert _get_status(sqlite_db, "datasets") == "ENABLED" + + +def _bootstrap_dataset_without_monetdb(sqlite_db): + run_cli_command_without_monetdb("init") + run_cli_command_without_monetdb("add-data-model", DATA_MODEL_FILE) + res = run_cli_command_without_monetdb( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", + ) + assert res.exit_code == ExitCode.OK + assert _get_status(sqlite_db, "datasets") == "ENABLED" + + +def _bootstrap_disabled_dataset(sqlite_db): + _bootstrap_dataset(sqlite_db) + res = run_cli_command( + "disable-dataset", + "dataset", + "-d", + "data_model", + "-v", + "1.0", + ) + assert res.exit_code == ExitCode.OK + assert _get_status(sqlite_db, "datasets") == "DISABLED" + + +def run_cli_command(command_name, *args): + runner = CliRunner() + return runner.invoke(cli, [*SQLiteDB_OPTION, *MONETDB_OPTIONS, command_name, *args]) + + +def run_cli_command_without_monetdb(command_name, *args): + runner = CliRunner() + return runner.invoke(cli, [*SQLiteDB_OPTION, *NO_MONETDB_OPTIONS, command_name, *args]) + @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("cleanup_sqlite") def test_init(sqlite_db): - # Setup - runner = CliRunner() + data_model_table = DataModelTable() assert not data_model_table.exists(sqlite_db) - result = runner.invoke(init, SQLiteDB_OPTION) + result = run_cli_command("init") assert result.exit_code == ExitCode.OK assert sqlite_db.execute_fetchall(f"select * from data_models") == [] @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_data_model(sqlite_db): - # Setup - runner = CliRunner() + # Check data_model not present already - runner.invoke(init, SQLiteDB_OPTION) - # Test - result = runner.invoke( - add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS - ) - assert result.exit_code == ExitCode.OK + _bootstrap_data_model(sqlite_db) data_models = sqlite_db.execute_fetchall(f"select * from data_models") data_model_id, code, version, desc, status, properties = data_models[0] @@ -85,47 +124,36 @@ def test_add_data_model(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_data_model(sqlite_db): - # Setup - runner = CliRunner() + # Check data_model not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert sqlite_db.get_data_models(["data_model_id"])[0]["data_model_id"] == 1 # Test - result = runner.invoke( - delete_data_model, - ["data_model", "-v", "1.0", "-f"] + SQLiteDB_OPTION + MONETDB_OPTIONS, - ) + result = run_cli_command("delete-data-model", "data_model", "-v", "1.0", "-f") + assert result.exit_code == ExitCode.OK @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset_with_volume(sqlite_db, monetdb): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - add_dataset, - [ - ABSOLUTE_PATH_DATASET_FILE, - "--data-model", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "add-dataset", + ABSOLUTE_PATH_DATASET_FILE, + "--data-model", + "data_model", + "-v", + "1.0", ) assert result.exit_code == ExitCode.OK @@ -140,31 +168,18 @@ def test_add_dataset_with_volume(sqlite_db, monetdb): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset(sqlite_db, monetdb): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - add_dataset, - [ - DATASET_FILE, - "--data-model", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "add-dataset", DATASET_FILE, "--data-model", "data_model", "-v", "1.0", "--no-copy" ) + assert result.exit_code == ExitCode.OK assert ( @@ -178,76 +193,52 @@ def test_add_dataset(sqlite_db, monetdb): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_two_datasets_with_same_name_different_data_model(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_data_model, - ["tests/data/success/data_model1_v_1_0/CDEsMetadata.json"] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + _bootstrap_data_model(sqlite_db) + run_cli_command( + "add-data-model", "tests/data/success/data_model1_v_1_0/CDEsMetadata.json" ) # Test - result = runner.invoke( - add_dataset, - [ - ABSOLUTE_PATH_SUCCESS_DATA_FOLDER + "/data_model_v_1_0/dataset10.csv", - "--data-model", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + run_cli_command( + "add-dataset", + ABSOLUTE_PATH_SUCCESS_DATA_FOLDER + "/data_model_v_1_0/dataset10.csv", + "--data-model", + "data_model", + "-v", + "1.0", ) - result = runner.invoke( - add_dataset, - [ - ABSOLUTE_PATH_SUCCESS_DATA_FOLDER + "/data_model1_v_1_0/dataset10.csv", - "--data-model", - "data_model1", - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "add-dataset", + ABSOLUTE_PATH_SUCCESS_DATA_FOLDER + "/data_model1_v_1_0/dataset10.csv", + "--data-model", + "data_model1", + "-v", + "1.0", ) + assert result.exit_code == ExitCode.OK assert [(1, "dataset10"), (2, "dataset10")] == sqlite_db.get_values( Dataset.__table__, columns=["data_model_id", "code"] ) - @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_validate_dataset_with_volume(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - validate_dataset, - [ - ABSOLUTE_PATH_DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + + result = run_cli_command( + "validate-dataset", ABSOLUTE_PATH_DATASET_FILE, "-d", "data_model", "-v", "1.0" ) + assert result.exit_code == ExitCode.OK @@ -293,36 +284,24 @@ def test_validate_dataset_with_volume(sqlite_db): ] -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") @pytest.mark.parametrize("data_model,dataset,exception_message", dataset_files) def test_invalid_dataset_error_cases(data_model, dataset, exception_message): - runner = CliRunner() + run_cli_command("init") - runner.invoke(init, SQLiteDB_OPTION) - result = runner.invoke( - add_data_model, - [ - ABSOLUTE_PATH_FAIL_DATA_FOLDER - + "/" - + data_model - + "_v_1_0/CDEsMetadata.json", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "add-data-model", + ABSOLUTE_PATH_FAIL_DATA_FOLDER + "/" + data_model + "_v_1_0/CDEsMetadata.json", ) - assert result.exit_code == ExitCode.OK - validation_result = runner.invoke( - validate_dataset, - [ - ABSOLUTE_PATH_FAIL_DATA_FOLDER + "/" + data_model + "_v_1_0/" + dataset, - "-d", - data_model, - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + assert result.exit_code == ExitCode.OK + validation_result = run_cli_command( + "validate-dataset", + ABSOLUTE_PATH_FAIL_DATA_FOLDER + "/" + data_model + "_v_1_0/" + dataset, + "-d", + data_model, + "-v", + "1.0", ) assert ( @@ -332,61 +311,41 @@ def test_invalid_dataset_error_cases(data_model, dataset, exception_message): def test_validate_no_db(): - runner = CliRunner() - - validation_result = runner.invoke(validate_folder, [ABSOLUTE_PATH_FAIL_DATA_FOLDER]) + validation_result = run_cli_command( + "validate-folder", ABSOLUTE_PATH_FAIL_DATA_FOLDER + ) assert validation_result.exit_code != ExitCode.OK @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_validate_dataset(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - validate_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "validate-dataset", DATASET_FILE, "-d", "data_model", "-v", "1.0", "--no-copy" ) + assert result.exit_code == ExitCode.OK @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_dataset_with_volume(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - ABSOLUTE_PATH_DATASET_FILE, - "--data-model", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + _bootstrap_data_model(sqlite_db) + run_cli_command( + "add-dataset", + ABSOLUTE_PATH_DATASET_FILE, + "--data-model", + "data_model", + "-v", + "1.0", ) assert ( "dataset" @@ -394,11 +353,8 @@ def test_delete_dataset_with_volume(sqlite_db): ) # Test - result = runner.invoke( - delete_dataset, - ["dataset", "-d", "data_model", "-v", "1.0"] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + result = run_cli_command( + "delete-dataset", "dataset", "-d", "data_model", "-v", "1.0" ) assert result.exit_code == ExitCode.OK @@ -406,20 +362,16 @@ def test_delete_dataset_with_volume(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_load_folder_with_volume(sqlite_db, monetdb): - # Setup - runner = CliRunner() # Check dataset not present already - result = runner.invoke(init, SQLiteDB_OPTION) + result = run_cli_command("init") assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - load_folder, - [ABSOLUTE_PATH_SUCCESS_DATA_FOLDER] + SQLiteDB_OPTION + MONETDB_OPTIONS, - ) + + result = run_cli_command("load-folder", ABSOLUTE_PATH_SUCCESS_DATA_FOLDER, ) assert result.exit_code == ExitCode.OK datasets = sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) @@ -440,22 +392,15 @@ def test_load_folder_with_volume(sqlite_db, monetdb): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_load_folder(sqlite_db, monetdb): - # Setup - runner = CliRunner() # Check dataset not present already - result = runner.invoke(init, SQLiteDB_OPTION) + result = run_cli_command("init") assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) # Test - result = runner.invoke( - load_folder, - [SUCCESS_DATA_FOLDER, "--copy_from_file", False] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) + result = run_cli_command("load-folder", SUCCESS_DATA_FOLDER, "--no-copy") assert result.exit_code == ExitCode.OK datasets = sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) @@ -474,26 +419,70 @@ def test_load_folder(sqlite_db, monetdb): ).fetchall() assert list(range(1, len(row_ids) + 1)) == [row[0] for row in row_ids] +@pytest.mark.usefixtures("cleanup_sqlite") +def test_load_folder_no_monetdb(sqlite_db, monetdb): + + # Check dataset not present already + result = run_cli_command_without_monetdb("init") + assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) + + # Test + result = run_cli_command_without_monetdb("load-folder", SUCCESS_DATA_FOLDER) + assert result.exit_code == ExitCode.OK + + datasets = sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) + dataset_codes = [code for code, *_ in datasets] + expected = [ + "dataset", + "dataset1", + "dataset2", + "dataset10", + "dataset20", + "dataset_longitudinal", + ] + assert set(expected) == set(dataset_codes) + @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") +def test_load_folder_monetdb_deployed_not_used_monetdb(sqlite_db, monetdb): + + # Check dataset not present already + result = run_cli_command_without_monetdb("init") + assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) + + # Test + result = run_cli_command_without_monetdb("load-folder", SUCCESS_DATA_FOLDER) + assert result.exit_code == ExitCode.OK + + datasets = sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) + dataset_codes = [code for code, *_ in datasets] + expected = [ + "dataset", + "dataset1", + "dataset2", + "dataset10", + "dataset20", + "dataset_longitudinal", + ] + assert set(expected) == set(dataset_codes) + + + + + +@pytest.mark.database +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_load_folder_twice_with_volume(sqlite_db, monetdb): - # Setup - runner = CliRunner() # Check dataset not present already - result = runner.invoke(init, SQLiteDB_OPTION) + result = run_cli_command("init") assert not sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) - result = runner.invoke( - load_folder, - [ABSOLUTE_PATH_SUCCESS_DATA_FOLDER] + SQLiteDB_OPTION + MONETDB_OPTIONS, - ) + result = run_cli_command("load-folder", ABSOLUTE_PATH_SUCCESS_DATA_FOLDER) + assert result.exit_code == ExitCode.OK # Test - result = runner.invoke( - load_folder, - [ABSOLUTE_PATH_SUCCESS_DATA_FOLDER] + SQLiteDB_OPTION + MONETDB_OPTIONS, - ) + result = run_cli_command("load-folder", ABSOLUTE_PATH_SUCCESS_DATA_FOLDER) assert result.exit_code == ExitCode.OK datasets = sqlite_db.get_values(table=Dataset.__table__, columns=["code"]) @@ -514,19 +503,13 @@ def test_load_folder_twice_with_volume(sqlite_db, monetdb): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_tag_data_model(sqlite_db): - # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) # Test - result = runner.invoke( - tag_data_model, - ["data_model", "-t", "tag", "-v", "1.0"] + SQLiteDB_OPTION, - ) + result = run_cli_command("tag-data-model", "data_model", "-t", "tag", "-v", "1.0") assert result.exit_code == ExitCode.OK result = sqlite_db.get_values(table=DataModel.__table__, columns=["properties"]) properties = result[0][0] @@ -534,23 +517,16 @@ def test_tag_data_model(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_untag_data_model(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - tag_data_model, - ["data_model", "-t", "tag", "-v", "1.0"] + SQLiteDB_OPTION, - ) + _bootstrap_data_model(sqlite_db) + result = run_cli_command("tag-data-model", "data_model", "-t", "tag", "-v", "1.0") # Test - result = runner.invoke( - tag_data_model, - ["data_model", "-t", "tag", "-v", "1.0", "-r"] + SQLiteDB_OPTION, + result = run_cli_command( + "tag-data-model", "data_model", "-t", "tag", "-v", "1.0", "-r" ) assert result.exit_code == ExitCode.OK result = sqlite_db.get_values(table=DataModel.__table__, columns=["properties"]) @@ -559,18 +535,14 @@ def test_untag_data_model(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_property_data_model_addition(sqlite_db): - # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) # Test - result = runner.invoke( - tag_data_model, - ["data_model", "-t", "key=value", "-v", "1.0"] + SQLiteDB_OPTION, + result = run_cli_command( + "tag-data-model", "data_model", "-t", "key=value", "-v", "1.0" ) assert result.exit_code == ExitCode.OK result = sqlite_db.get_values(table=DataModel.__table__, columns=["properties"]) @@ -581,75 +553,42 @@ def test_property_data_model_addition(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_property_data_model_deletion(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - tag_data_model, - ["data_model", "-t", "key=value", "-v", "1.0"] + SQLiteDB_OPTION, - ) + _bootstrap_data_model(sqlite_db) + run_cli_command("tag-data-model", "data_model", "-t", "key=value", "-v", "1.0") # Test - result = runner.invoke( - tag_data_model, - [ - "data_model", - "-t", - "key=value", - "-v", - "1.0", - "-r", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-data-model", "data_model", "-t", "key=value", "-v", "1.0", "-r" ) + assert result.exit_code == ExitCode.OK result = sqlite_db.get_values(table=DataModel.__table__, columns=["properties"]) properties = result[0][0] assert "key" not in properties["properties"] - @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_tag_dataset(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "--data-model", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + _bootstrap_data_model(sqlite_db) + run_cli_command( + "add-dataset", + DATASET_FILE, + "--data-model", + "data_model", + "-v", + "1.0", + "--no-copy", ) # Test - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "tag", - "-d", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-dataset", "dataset", "-t", "tag", "-d", "data_model", "-v", "1.0" ) assert result.exit_code == ExitCode.OK properties = sqlite_db.get_values(table=Dataset.__table__, columns=["properties"]) @@ -658,62 +597,31 @@ def test_tag_dataset(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_untag_dataset(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - result = runner.invoke( - add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) + result = run_cli_command( + "add-dataset", + DATASET_FILE, + "--data-model", + "data_model", + "-v", + "1.0", + "--no-copy", ) - assert result.exit_code == ExitCode.OK - result = runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) assert result.exit_code == ExitCode.OK - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "tag", - "-d", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-dataset", "dataset", "-t", "tag", "-d", "data_model", "-v", "1.0" ) assert result.exit_code == ExitCode.OK # Test - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "tag", - "-d", - "data_model", - "-v", - "1.0", - "-r", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-dataset", "dataset", "-t", "tag", "-d", "data_model", "-v", "1.0", "-r" ) assert result.exit_code == ExitCode.OK properties = sqlite_db.get_values(table=Dataset.__table__, columns=["properties"]) @@ -722,42 +630,24 @@ def test_untag_dataset(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_property_dataset_addition(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + _bootstrap_data_model(sqlite_db) + run_cli_command( + "add-dataset", + DATASET_FILE, + "--data-model", + "data_model", + "-v", + "1.0", + "--no-copy", ) # Test - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "key=value", - "-d", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-dataset", "dataset", "-t", "key=value", "-d", "data_model", "-v", "1.0" ) assert result.exit_code == ExitCode.OK properties = sqlite_db.get_values(table=Dataset.__table__, columns=["properties"]) @@ -766,62 +656,19 @@ def test_property_dataset_addition(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_property_dataset_deletion(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - result = runner.invoke( - add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS - ) - assert result.exit_code == ExitCode.OK - - result = runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) - assert result.exit_code == ExitCode.OK - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "key=value", - "-d", - "data_model", - "-v", - "1.0", - ] - + SQLiteDB_OPTION, + _bootstrap_dataset(sqlite_db) + result = run_cli_command( + "tag-dataset", "dataset", "-t", "key=value", "-d", "data_model", "-v", "1.0" ) assert result.exit_code == ExitCode.OK # Test - result = runner.invoke( - tag_dataset, - [ - "dataset", - "-t", - "key=value", - "-d", - "data_model", - "-v", - "1.0", - "-r", - ] - + SQLiteDB_OPTION, + result = run_cli_command( + "tag-dataset", "dataset", "-t", "key=value", "-d", "data_model", "-v", "1.0", "-r" ) assert result.exit_code == ExitCode.OK properties = sqlite_db.get_values(table=Dataset.__table__, columns=["properties"]) @@ -830,147 +677,91 @@ def test_property_dataset_deletion(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_data_model(sqlite_db): - # Setup - runner = CliRunner() # Check status is disabled - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - result = runner.invoke( - disable_data_model, ["data_model", "-v", "1.0"] + SQLiteDB_OPTION - ) + _bootstrap_data_model(sqlite_db) + run_cli_command("disable-data-model", "data_model", "-v", "1.0") assert _get_status(sqlite_db, "data_models") == "DISABLED" # Test - result = runner.invoke( - enable_data_model, ["data_model", "-v", "1.0"] + SQLiteDB_OPTION - ) + result = run_cli_command("enable-data-model", "data_model", "-v", "1.0") assert result.exit_code == ExitCode.OK assert _get_status(sqlite_db, "data_models") == "ENABLED" @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_data_model(sqlite_db): - # Setup - runner = CliRunner() - # Check status is enabled - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + _bootstrap_data_model(sqlite_db) assert _get_status(sqlite_db, "data_models") == "ENABLED" # Test - result = runner.invoke( - disable_data_model, ["data_model", "-v", "1.0"] + SQLiteDB_OPTION - ) + result = run_cli_command("disable-data-model", "data_model", "-v", "1.0") assert result.exit_code == ExitCode.OK assert _get_status(sqlite_db, "data_models") == "DISABLED" @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_dataset(sqlite_db): - # Setup - runner = CliRunner() - - # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) - result = runner.invoke( - disable_dataset, - ["dataset", "-d", "data_model", "-v", "1.0"] + SQLiteDB_OPTION, + _bootstrap_disabled_dataset(sqlite_db) + result = run_cli_command( + "enable-dataset", + "dataset", + "-d", + "data_model", + "-v", + "1.0", ) - assert _get_status(sqlite_db, "datasets") == "DISABLED" - # Test - result = runner.invoke( - enable_dataset, - ["dataset", "-d", "data_model", "-v", "1.0"] + SQLiteDB_OPTION, - ) assert result.exit_code == ExitCode.OK assert _get_status(sqlite_db, "datasets") == "ENABLED" @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_dataset(sqlite_db): - # Setup - runner = CliRunner() # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) - assert _get_status(sqlite_db, "datasets") == "ENABLED" + _bootstrap_dataset(sqlite_db) # Test - result = runner.invoke( - disable_dataset, - ["dataset", "-d", "data_model", "-v", "1.0"] + SQLiteDB_OPTION, + result = run_cli_command( + "disable-dataset", + "dataset", + "-d", + "data_model", + "-v", + "1.0", ) assert _get_status(sqlite_db, "datasets") == "DISABLED" assert result.exit_code == ExitCode.OK @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_list_data_models(): - # Setup - runner = CliRunner() # Check data_model not present already - runner.invoke(init, SQLiteDB_OPTION) - result = runner.invoke(list_data_models, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - result_with_data_model = runner.invoke(list_data_models, SQLiteDB_OPTION) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "--data-model", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) - result_with_data_model_and_dataset = runner.invoke( - list_data_models, SQLiteDB_OPTION + run_cli_command("init") + result = run_cli_command("list-data-models") + run_cli_command("add-data-model", DATA_MODEL_FILE) + result_with_data_model = run_cli_command("list-data-models") + run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", ) + + result_with_data_model_and_dataset = run_cli_command("list-data-models") # Test assert result.exit_code == ExitCode.OK assert result.stdout == "There are no data models.\n" @@ -995,32 +786,21 @@ def test_list_data_models(): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") -def test_list_datasets(): - # Setup - runner = CliRunner() - +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") +def test_list_datasets(sqlite_db): # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - result = runner.invoke(list_datasets, SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - ABSOLUTE_PATH_DATASET_FILE_MULTIPLE_DATASET, - "--data-model", - "data_model", - "-v", - "1.0", - "--copy_from_file", - True, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, - ) - result_with_dataset = runner.invoke( - list_datasets, SQLiteDB_OPTION + MONETDB_OPTIONS + _bootstrap_data_model(sqlite_db) + result = run_cli_command("list-datasets") + run_cli_command( + "add-dataset", + ABSOLUTE_PATH_DATASET_FILE_MULTIPLE_DATASET, + "--data-model", + "data_model", + "-v", + "1.0", + "--no-copy", ) + result_with_dataset = run_cli_command("list-datasets") # Test assert result.exit_code == ExitCode.OK @@ -1035,10 +815,9 @@ def test_list_datasets(): assert "dataset1 Dataset 1 ENABLED".strip( " " ) in result_with_dataset.stdout.strip(" ") - assert "dataset Dataset ENABLED".strip( + assert "dataset Dataset ENABLED".strip(" ") in result_with_dataset.stdout.strip( " " - ) in result_with_dataset.stdout.strip(" ") - + ) def _get_status(db, schema_name): (status, *_), *_ = db.execute_fetchall(f"SELECT status FROM {schema_name}") diff --git a/tests/test_database.py b/tests/test_database.py index f17a242..9eb2c6c 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,29 +1,25 @@ -from click.testing import CliRunner - -from mipdb import add_dataset -from mipdb import add_data_model -from mipdb import init import pytest from mipdb.exceptions import DataBaseError -from mipdb.databases.sqlite import DataModel, Dataset +from mipdb.sqlite.sqlite import DataModel, Dataset from tests.conftest import DATASET_FILE, MONETDB_OPTIONS, SQLiteDB_OPTION from tests.conftest import DATA_MODEL_FILE +from tests.test_commands import run_cli_command @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_update_data_model_status(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) # Check the status of data model is disabled result = sqlite_db.get_values( table=DataModel.__table__, columns=["status"], where_conditions={"data_model_id": 1}, ) + print(result) assert result[0][0] == "ENABLED" # Test @@ -37,25 +33,19 @@ def test_update_data_model_status(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def update_dataset_status(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) + result = run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", ) # Check the status of dataset is disabled @@ -78,27 +68,20 @@ def update_dataset_status(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_datasets_with_db(sqlite_db): # Setup - runner = CliRunner() - - # Check dataset not present already - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", ) # Check dataset present @@ -107,12 +90,11 @@ def test_get_datasets_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_data_model_id_with_db(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) # Test success data_model_id = sqlite_db.get_data_model_id("data_model", "1.0") @@ -120,11 +102,10 @@ def test_get_data_model_id_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_data_model_id_not_found_error(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) + run_cli_command("init") # Test when there is no schema in the database with the specific code and version with pytest.raises(DataBaseError): @@ -132,12 +113,11 @@ def test_get_data_model_id_not_found_error(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_data_model_id_duplication_error(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) sqlite_db.insert_values_to_table( DataModel.__table__, { @@ -155,25 +135,20 @@ def test_get_data_model_id_duplication_error(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_dataset_id_with_db(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", ) # Test @@ -182,25 +157,20 @@ def test_get_dataset_id_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_dataset_id_duplication_error(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) - runner.invoke( - add_dataset, - [ - DATASET_FILE, - "-d", - "data_model", - "-v", - "1.0", - "--copy_from_file", - False, - ] - + SQLiteDB_OPTION - + MONETDB_OPTIONS, + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command("add-data-model", DATA_MODEL_FILE) + run_cli_command( + "add-dataset", + DATASET_FILE, + "-d", + "data_model", + "-v", + "1.0", + "--no-copy", ) sqlite_db.insert_values_to_table( @@ -221,12 +191,11 @@ def test_get_dataset_id_duplication_error(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_get_dataset_id_not_found_error(sqlite_db): # Setup - runner = CliRunner() - runner.invoke(init, SQLiteDB_OPTION) - runner.invoke(add_data_model, [DATA_MODEL_FILE] + SQLiteDB_OPTION + MONETDB_OPTIONS) + run_cli_command("init") + run_cli_command("add-data-model", DATA_MODEL_FILE) # Test when there is no dataset in the database with the specific code and data_model_id with pytest.raises(DataBaseError): diff --git a/tests/test_schema.py b/tests/test_schema.py index 5e2993e..388b37a 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -3,8 +3,8 @@ import pytest from mipdb.exceptions import UserInputError -from mipdb.databases.monetdb import MonetDB -from mipdb.schema import Schema +from mipdb.monetdb.monetdb import MonetDB +from mipdb.monetdb.schema import Schema @pytest.fixture diff --git a/tests/test_tables.py b/tests/test_tables.py index 858f5be..8d61773 100644 --- a/tests/test_tables.py +++ b/tests/test_tables.py @@ -2,9 +2,9 @@ import pytest -from mipdb.databases.monetdb_tables import PrimaryDataTable -from mipdb.schema import Schema -from mipdb.databases.sqlite_tables import ( +from mipdb.monetdb.monetdb_tables import PrimaryDataTable +from mipdb.monetdb.schema import Schema +from mipdb.sqlite.sqlite_tables import ( DataModelTable, MetadataTable, ) @@ -17,7 +17,7 @@ def cdes(data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_data_models_table_realdb(sqlite_db): # Test DataModelTable().create(sqlite_db) @@ -26,7 +26,7 @@ def test_data_models_table_realdb(sqlite_db): class TestVariablesMetadataTable: @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_create_table_with_db(self, sqlite_db): # Setup @@ -37,7 +37,7 @@ def test_create_table_with_db(self, sqlite_db): assert res == {} @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_insert_values_with_db(self, sqlite_db, data_model_metadata): # Setup @@ -66,7 +66,7 @@ def test_get_values_from_cdes_full_schema_data(self, data_model_metadata): assert len(result) == 6 @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_load_from_db(self, data_model_metadata, sqlite_db): # Setup @@ -86,7 +86,7 @@ def test_load_from_db(self, data_model_metadata, sqlite_db): class TestPrimaryDataTable: @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_create_table_with_db(self, cdes, monetdb): # Setup schema = Schema("schema:1.0") @@ -98,7 +98,7 @@ def test_create_table_with_db(self, cdes, monetdb): assert res == [] @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_drop_table_with_db(self, cdes, monetdb): # Setup schema = Schema("schema:1.0") @@ -112,7 +112,7 @@ def test_drop_table_with_db(self, cdes, monetdb): monetdb.execute('SELECT * FROM "schema:1.0".primary_data').fetchall() @pytest.mark.database - @pytest.mark.usefixtures("monetdb_container", "cleanup_db") + @pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_reflect_table_from_db(self, cdes, monetdb): # Setup schema = Schema("schema:1.0") diff --git a/tests/test_usecases.py b/tests/test_usecases.py index 83d08fd..0c30e61 100644 --- a/tests/test_usecases.py +++ b/tests/test_usecases.py @@ -3,11 +3,11 @@ import pandas as pd import pytest -from mipdb.databases.monetdb import MonetDB +from mipdb.monetdb.monetdb import MonetDB from mipdb.exceptions import ForeignKeyError, DataBaseError, InvalidDatasetError from mipdb.exceptions import UserInputError -from mipdb.databases.sqlite import Dataset -from mipdb.databases.sqlite_tables import DataModelTable, DatasetsTable +from mipdb.sqlite.sqlite import Dataset +from mipdb.sqlite.sqlite_tables import DataModelTable, DatasetsTable from mipdb.usecases import ( AddPropertyToDataset, check_unique_longitudinal_dataset_primary_keys, @@ -44,7 +44,7 @@ @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_init_with_db(db): # Setup InitDB(sqlite_db).execute() @@ -58,21 +58,21 @@ def test_init_with_db(db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_is_db_initialized_with_db_fail(db): with pytest.raises(UserInputError): is_db_initialized(db=db) @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_is_db_initialized_with_db_fail(sqlite_db): InitDB(sqlite_db).execute() is_db_initialized(db=sqlite_db) @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_init_with_db(sqlite_db): # Setup InitDB(sqlite_db).execute() @@ -86,7 +86,7 @@ def test_init_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_re_init_with_missing_schema_with_db(sqlite_db): # Setup InitDB(sqlite_db).execute() @@ -99,7 +99,7 @@ def test_re_init_with_missing_schema_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_re_init_with_missing_actions_table_with_db(sqlite_db): # Setup InitDB(sqlite_db).execute() @@ -117,7 +117,7 @@ def test_re_init_with_missing_actions_table_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_re_init_with_missing_data_models_table_with_db(sqlite_db): # Setup InitDB(sqlite_db).execute() @@ -136,7 +136,7 @@ def test_re_init_with_missing_data_models_table_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_re_init_with_missing_datasets_table_with_db(sqlite_db): # Setup InitDB(sqlite_db).execute() @@ -156,7 +156,7 @@ def test_re_init_with_missing_datasets_table_with_db(sqlite_db): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_data_model_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -166,7 +166,7 @@ def test_add_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_data_model_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -181,7 +181,7 @@ def test_delete_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_data_model_with_db_with_force(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -196,7 +196,7 @@ def test_delete_data_model_with_db_with_force(sqlite_db, monetdb, data_model_met @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_data_model_with_datasets_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -221,7 +221,7 @@ def test_delete_data_model_with_datasets_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_data_model_with_datasets_with_db_with_force( sqlite_db, monetdb, data_model_metadata ): @@ -245,7 +245,7 @@ def test_delete_data_model_with_datasets_with_db_with_force( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -266,7 +266,7 @@ def test_add_dataset(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset_with_db_with_multiple_datasets( sqlite_db, monetdb, data_model_metadata ): @@ -288,12 +288,12 @@ def test_add_dataset_with_db_with_multiple_datasets( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset_with_small_record_copy(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) - with patch("mipdb.databases.monetdb_tables.RECORDS_PER_COPY", 1): + with patch("mipdb.monetdb.monetdb_tables.RECORDS_PER_COPY", 1): # Test ImportCSV(sqlite_db, monetdb).execute( csv_path=DATASET_FILE, @@ -308,14 +308,14 @@ def test_add_dataset_with_small_record_copy(sqlite_db, monetdb, data_model_metad @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_dataset_with_small_record_copy_with_volume( sqlite_db, monetdb, data_model_metadata ): # Setup InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) - with patch("mipdb.databases.monetdb_tables.RECORDS_PER_COPY", 1): + with patch("mipdb.monetdb.monetdb_tables.RECORDS_PER_COPY", 1): # Test ImportCSV(sqlite_db, monetdb).execute( csv_path=ABSOLUTE_PATH_DATASET_FILE, @@ -330,12 +330,12 @@ def test_add_dataset_with_small_record_copy_with_volume( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_csv_legnth_equals_records_per_copy(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) - with patch("mipdb.databases.monetdb_tables.RECORDS_PER_COPY", 5): + with patch("mipdb.monetdb.monetdb_tables.RECORDS_PER_COPY", 5): # Test ImportCSV(sqlite_db, monetdb).execute( csv_path=ABSOLUTE_PATH_DATASET_FILE, @@ -365,7 +365,7 @@ def test_check_duplicate_pairs_fail(): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_validate_dataset(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -380,7 +380,7 @@ def test_validate_dataset(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_delete_dataset_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -408,7 +408,7 @@ def test_delete_dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_data_model_with_db(sqlite_db, monetdb, data_model_metadata): InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata=data_model_metadata) @@ -425,7 +425,7 @@ def test_enable_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_data_model_already_enabled_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -441,7 +441,7 @@ def test_enable_data_model_already_enabled_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_data_model_with_db(sqlite_db, monetdb, data_model_metadata): InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) @@ -455,7 +455,7 @@ def test_disable_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_data_model_already_disabled_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -474,7 +474,7 @@ def test_disable_data_model_already_disabled_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_dataset_with_db(sqlite_db, monetdb, data_model_metadata): InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) @@ -501,7 +501,7 @@ def test_enable_dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_enable_dataset_already_enabled_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -525,7 +525,7 @@ def test_enable_dataset_already_enabled_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_dataset_with_db(sqlite_db, monetdb, data_model_metadata): InitDB(sqlite_db).execute() AddDataModel(sqlite_db, monetdb).execute(data_model_metadata) @@ -547,7 +547,7 @@ def test_disable_dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_disable_dataset_already_disabled_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -576,7 +576,7 @@ def test_disable_dataset_already_disabled_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_tag_data_model_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -594,7 +594,7 @@ def test_tag_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_untag_data_model_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -626,7 +626,7 @@ def test_untag_data_model_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_property2data_model_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -648,7 +648,7 @@ def test_add_property2data_model_with_db(sqlite_db, monetdb, data_model_metadata @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_property2data_model_with_force_and_db( sqlite_db, monetdb, data_model_metadata ): @@ -680,7 +680,7 @@ def test_add_property2data_model_with_force_and_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_remove_property_from_data_model_with_db( sqlite_db, monetdb, data_model_metadata ): @@ -717,7 +717,7 @@ def test_remove_property_from_data_model_with_db( @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_tag_dataset_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -737,7 +737,7 @@ def test_tag_dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_untag_dataset_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -775,7 +775,7 @@ def test_untag_dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_add_property2dataset_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -797,7 +797,7 @@ def test_add_property2dataset_with_db(sqlite_db, monetdb, data_model_metadata): @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_remove_property_from_dataset_with_db(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute() @@ -841,7 +841,7 @@ def test_remove_property_from_dataset_with_db(sqlite_db, monetdb, data_model_met @pytest.mark.database -@pytest.mark.usefixtures("monetdb_container", "cleanup_db") +@pytest.mark.usefixtures("monetdb_container", "cleanup_monetdb", "cleanup_sqlite") def test_grant_select_access_rights(sqlite_db, monetdb, data_model_metadata): # Setup InitDB(sqlite_db).execute()