diff --git a/.gitignore b/.gitignore index efb1ca95..47fc7ff3 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ htmlcov work/ # credentials -.credentials/ \ No newline at end of file +.credentials/ +config/clickhouse/config.d/s3.xml \ No newline at end of file diff --git a/config/clickhouse/config.d/config.xml b/config/clickhouse/config.d/config.xml index 265911f7..28728d92 100644 --- a/config/clickhouse/config.d/config.xml +++ b/config/clickhouse/config.d/config.xml @@ -2,10 +2,11 @@ warning - /var/log/clickhouse-server/clickhouse-server.log - /var/log/clickhouse-server/clickhouse-server.err.log 1000M 7 + true + + ot-genetics 8123 diff --git a/config/config.yaml b/config/config.yaml index ba87beb0..574c8f62 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -4,6 +4,7 @@ log_level: ${LOG_LEVEL} release_uri: ${RELEASE_URI} scratchpad: release: ${RELEASE} + product: ${PRODUCT} data_source: ${RELEASE_URI} # temp file locations local_data: release_data @@ -29,6 +30,7 @@ scratchpad: clickhouse_logs: /var/log/pos/clickhouse clickhouse_disk_name: ${CLICKHOUSE_DISK_NAME} clickhouse_disk_snapshot_name: ${CLICKHOUSE_DISK_SNAPSHOT_NAME} + clickhouse_backup_base_path: ${CLICKHOUSE_BACKUP_BASE_PATH} # bigquery bq_prod_project_id: open-targets-prod bq_parquet_path: ${BQ_DATA_SOURCE} @@ -220,6 +222,49 @@ steps: gcp_disk_name: ${clickhouse_disk_name} gcp_snapshot_name: ${clickhouse_disk_snapshot_name} gcp_disk_zone: europe-west1-d + clickhouse_backup_all: + - name: clickhouse_start data loading instance + volume_data: ${clickhouse_data} + volume_logs: ${clickhouse_logs} + clickhouse_version: ${clickhouse_version} + clickhouse_database: ${database_namespace} + - name: explode backup clickhouse tables + requires: + - clickhouse_start data loading instance + foreach: + - associations_otf_target + - associations_otf_disease + - intervals + - literature_index + - literature + - targets + - ml_w2v + do: + - name: clickhouse_backup table ${each} + requires: + - clickhouse_start data loading instance + clickhouse_database: ${database_namespace} + table: ${each} + gcs_base_path: ${clickhouse_backup_base_path} + clickhouse_restore_all: + - name: clickhouse_create_database for restoration + clickhouse_database: ${database_namespace} + - name: explode restore clickhouse tables + requires: + - clickhouse_create_database for restoration + foreach: + - associations_otf_target + - associations_otf_disease + - intervals + - literature_index + - literature + - targets + - ml_w2v + do: + - name: clickhouse_restore table ${each} + clickhouse_database: ${database_namespace} + table: ${each} + gcs_base_path: ${clickhouse_backup_base_path} clickhouse_stop: - name: clickhouse_stop data loading instance clickhouse_database_name: ${database_namespace} diff --git a/deployment/locals.tf b/deployment/locals.tf index a6d0d626..79acd76e 100644 --- a/deployment/locals.tf +++ b/deployment/locals.tf @@ -16,6 +16,7 @@ locals { LOG_LEVEL = var.pos_log_level RELEASE_URI = var.data_location_source RELEASE = var.release_id + PRODUCT = var.is_ppp == false ? "platform" : "ppp" RELEASE_FTP_OUTPUT = local.ftp_output_path RELEASE_GCS_OUTPUT = local.gcs_output_path OPENSEARCH_VERSION = var.open_search_image_tag @@ -30,6 +31,7 @@ locals { DATABASE_NAMESPACE = var.database_namespace CLICKHOUSE_DISK_NAME = google_compute_disk.clickhouse_data_disk.name CLICKHOUSE_DISK_SNAPSHOT_NAME = "${google_compute_disk.clickhouse_data_disk.name}" + CLICKHOUSE_BACKUP_BASE_PATH = var.clickhouse_backup_base_path BQ_DATA_SOURCE = var.data_location_production # For templating reasons, we need to substitute the following variables with $${var_name} release = "$${release}" @@ -55,6 +57,7 @@ locals { clickhouse_logs = "$${clickhouse_logs}" clickhouse_disk_name = "$${clickhouse_disk_name}" clickhouse_disk_snapshot_name = "$${clickhouse_disk_snapshot_name}" + clickhouse_backup_base_path = "$${clickhouse_backup_base_path}" bq_prod_project_id = "$${bq_prod_project_id}" bq_parquet_path = "$${bq_parquet_path}" each = "$${each}" diff --git a/deployment/main.tf b/deployment/main.tf index 06770c69..0e14c007 100644 --- a/deployment/main.tf +++ b/deployment/main.tf @@ -10,6 +10,11 @@ resource "tls_private_key" "posvm" { rsa_bits = 4096 } +#Create the HMAC key for the associated service account +resource "google_storage_hmac_key" "key" { + service_account_email = "pos-service-account@open-targets-eu-dev.iam.gserviceaccount.com" +} + // Create a disk volume for Clickhouse data resource "google_compute_disk" "clickhouse_data_disk" { project = "open-targets-eu-dev" @@ -88,7 +93,17 @@ resource "google_compute_instance" "posvm" { "pos_config.tftpl", local.yaml_config_variables ) - # pos_run_script = file("run.sh") + s3_config = templatefile( + "s3_config.tftpl", + { + GCS_BATH_PATH = var.clickhouse_backup_base_path + ACCESS_KEY = google_storage_hmac_key.key.access_id + SECRET_KEY = google_storage_hmac_key.key.secret + } + ) + + google_storage_hmac_key_access_id = google_storage_hmac_key.key.access_id + google_storage_hmac_key_secret = google_storage_hmac_key.key.secret } service_account { email = "pos-service-account@open-targets-eu-dev.iam.gserviceaccount.com" diff --git a/deployment/pos_config.tftpl b/deployment/pos_config.tftpl index ba87beb0..10bf569f 100644 --- a/deployment/pos_config.tftpl +++ b/deployment/pos_config.tftpl @@ -4,6 +4,7 @@ log_level: ${LOG_LEVEL} release_uri: ${RELEASE_URI} scratchpad: release: ${RELEASE} + product: ${PRODUCT} data_source: ${RELEASE_URI} # temp file locations local_data: release_data @@ -29,6 +30,7 @@ scratchpad: clickhouse_logs: /var/log/pos/clickhouse clickhouse_disk_name: ${CLICKHOUSE_DISK_NAME} clickhouse_disk_snapshot_name: ${CLICKHOUSE_DISK_SNAPSHOT_NAME} + clickhouse_backup_base_path: ${CLICKHOUSE_BACKUP_BASE_PATH} # bigquery bq_prod_project_id: open-targets-prod bq_parquet_path: ${BQ_DATA_SOURCE} @@ -220,6 +222,45 @@ steps: gcp_disk_name: ${clickhouse_disk_name} gcp_snapshot_name: ${clickhouse_disk_snapshot_name} gcp_disk_zone: europe-west1-d + clickhouse_backup_all: + - name: clickhouse_start data loading instance + volume_data: ${clickhouse_data} + volume_logs: ${clickhouse_logs} + clickhouse_version: ${clickhouse_version} + clickhouse_database: ${database_namespace} + - name: explode backup clickhouse tables + requires: + - clickhouse_start data loading instance + foreach: + - associations_otf_target + - associations_otf_disease + - intervals + - literature_index + - literature + - targets + - ml_w2v + do: + - name: clickhouse_backup table ${each} + requires: + - clickhouse_start data loading instance + clickhouse_database: ${database_namespace} + table: ${each} + gcs_base_path: ${clickhouse_backup_base_path} + clickhouse_restore_all: + - name: explode restore clickhouse tables + foreach: + - associations_otf_target + - associations_otf_disease + - intervals + - literature_index + - literature + - targets + - ml_w2v + do: + - name: clickhouse_restore table ${each} + clickhouse_database: ${database_namespace} + table: ${each} + gcs_base_path: ${clickhouse_backup_base_path} clickhouse_stop: - name: clickhouse_stop data loading instance clickhouse_database_name: ${database_namespace} diff --git a/deployment/s3_config.tftpl b/deployment/s3_config.tftpl new file mode 100644 index 00000000..dd90ccdf --- /dev/null +++ b/deployment/s3_config.tftpl @@ -0,0 +1,9 @@ + + + + ${GCS_BATH_PATH} + ${ACCESS_KEY} + ${SECRET_KEY} + + + \ No newline at end of file diff --git a/deployment/startup.sh b/deployment/startup.sh index b00904bf..8b1b55a0 100644 --- a/deployment/startup.sh +++ b/deployment/startup.sh @@ -67,6 +67,7 @@ function install_packages() { chgrp -R google-sudoers /opt/pos_run.sh && chmod g+x /opt/pos_run.sh create_dir_for_group /var/log/pos/opensearch google-sudoers rwx create_dir_for_group /var/log/pos/clickhouse google-sudoers rwx + curl "http://metadata.google.internal/computeMetadata/v1/instance/attributes/s3_config" -H "Metadata-Flavor: Google" > /opt/platform-output-support/config/clickhouse/config.d/s3_config.xml } @@ -121,7 +122,7 @@ function sync_data() { function opensearch_steps() { log "[INFO] Starting OpenSearch steps" uv_run opensearch_prep_all 300 && \ - uv_run opensearch_load_all 100 > /var/log/open_search_load.log 2>&1 && \ + uv_run opensearch_load_all 80 > /var/log/open_search_load.log 2>&1 && \ opensearch_summary && \ uv_run opensearch_stop 1 && \ sync && \ @@ -148,7 +149,7 @@ function clickhouse_steps() { function copy_clickhouse_configs() { log "[INFO] Syncing ClickHouse configs" - cp -vR /opt/platform-output-support/config/clickhouse/config.d /mnt/clickhouse/ + cp -vR /opt/platform-output-support/config/clickhouse/config.d/config.xml /mnt/clickhouse/config.d/config.xml cp -vR /opt/platform-output-support/config/clickhouse/users.d /mnt/clickhouse/ } diff --git a/deployment/variables.tf b/deployment/variables.tf index 0769b845..05101f92 100644 --- a/deployment/variables.tf +++ b/deployment/variables.tf @@ -1,7 +1,7 @@ variable "vm_pos_boot_disk_size" { description = "POS VM boot disk size, default '500GB'" type = string - default = 500 + default = 600 } variable "vm_pos_machine_type" { @@ -19,7 +19,7 @@ variable "pos_logs_path_root" { variable "clickhouse_data_disk_size" { description = "Clickhouse data disk size to deploy" type = string - default = "50" + default = "200" } variable "clickhouse_snapshot_source" { @@ -34,6 +34,12 @@ variable "clickhouse_tarball" { default = false } +variable "clickhouse_backup_base_path" { + description = "Base path in GCS bucket where ClickHouse backups will be stored" + type = string + default = "https://storage.googleapis.com/opentargets-backup/clickhouse/" +} + variable "database_namespace" { description = "Database namespace, default 'ot'" type = string @@ -43,7 +49,7 @@ variable "database_namespace" { variable "open_search_data_disk_size" { description = "Opensearch data disk size to deploy" type = string - default = "200" + default = "400" } variable "open_search_snapshot_source" { diff --git a/pyproject.toml b/pyproject.toml index 45e443dc..c13dc8b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ "google-cloud-bigquery>=3.34.0", "google-cloud-compute>=1.31.0", "opensearch-py>=3.0.0", - "opentargets-otter==25.0.2", + "opentargets-otter>=25.0.9", "orjson>=3.10.18", "ot-croissant", "polars>=1.31.0", diff --git a/src/pos/core.py b/src/pos/core.py index aaefffc8..f09704aa 100644 --- a/src/pos/core.py +++ b/src/pos/core.py @@ -3,5 +3,6 @@ def main() -> None: runner = Runner('pos') + runner.start() runner.register_tasks('pos.tasks') runner.run() diff --git a/src/pos/services/clickhouse.py b/src/pos/services/clickhouse.py index 5f732a0c..66dcdd26 100644 --- a/src/pos/services/clickhouse.py +++ b/src/pos/services/clickhouse.py @@ -1,6 +1,8 @@ """Clickhouse service module.""" +from dataclasses import asdict, dataclass from pathlib import Path +from string import Template import clickhouse_connect from clickhouse_connect.driver.client import Client @@ -11,6 +13,14 @@ from pos.services.containerized_service import ContainerizedService, ContainerizedServiceError, reset_timeout +@dataclass +class ClickhouseBackupQueryParameters: + database: str + table: str + backup_path: str + export_path: str + + class ClickhouseInstanceManagerError(Exception): """Base class for exceptions in this module.""" @@ -118,3 +128,95 @@ def is_healthy(self) -> bool: break self._wait(1) return healthy + + +def create_database(client: Client, database: str, exists_ok: bool = True) -> None: + """Create Clickhouse database if it does not exist. + + Args: + client: Clickhouse client + database: Database name + exists_ok: If True, do not raise an error if the database already exists (default: True) + """ + if exists_ok: + query = 'CREATE DATABASE IF NOT EXISTS {database:Identifier}' + else: + query = 'CREATE DATABASE {database:Identifier}' + client.query(query=query, parameters={'database': database}) + + +def get_table_engine(client: Client, database: str, table: str) -> str | None: + """Get the engine type of a ClickHouse table. + + Args: + client (Client): ClickHouse client instance. + database (str): Name of the database. + table (str): Name of the table. + + Returns: + str | None: The engine type of the table, or None if not found. + """ + query = Template( + """SELECT engine \ + FROM system.tables \ + WHERE database='${database}' AND name='${table}'""" + ).substitute({'database': database, 'table': table}) + table_engine_query = client.query(query=query).first_row + return table_engine_query[0] if table_engine_query else None + + +def backup_table(client: Client, parameters: ClickhouseBackupQueryParameters) -> None: + """Backup ClickHouse table to S3 compatible storage (GCS). + + Args: + client (Client): ClickHouse client instance. + parameters (ClickhouseBackupQueryParameters): Dataclass containing query parameters. + + """ + query = Template("BACKUP TABLE `${database}`.`${table}` TO S3('${backup_path}')").substitute(asdict(parameters)) + client.query(query=query) + + +def restore_table(client: Client, parameters: ClickhouseBackupQueryParameters) -> None: + """Restore ClickHouse table from S3 compatible storage (GCS). + + Args: + client (Client): ClickHouse client instance. + parameters (ClickhouseBackupQueryParameters): Dataclass containing query parameters. + """ + query = Template("RESTORE TABLE `${database}`.`${table}` FROM S3('${backup_path}')").substitute(asdict(parameters)) + client.query(query=query) + + +def export_to_s3(client: Client, parameters: ClickhouseBackupQueryParameters) -> None: + """Export ClickHouse table to S3 compatible storage (GCS). + + This is used for tables with the EmbeddedRocksDB engine which + is not supported by the BACKUP/RESTORE commands. + + Args: + client (Client): ClickHouse client instance. + parameters (ClickhouseBackupQueryParameters): Dataclass containing query parameters. + """ + query = Template( + """INSERT INTO FUNCTION s3(\ + '${export_path}', Parquet) \ + SELECT * FROM `${database}`.`${table}`""" + ).substitute(asdict(parameters)) + client.query(query) + + +def import_from_s3(client: Client, parameters: ClickhouseBackupQueryParameters) -> None: + """Import ClickHouse table from S3 compatible storage (GCS). + + This is used for tables with the EmbeddedRocksDB engine which + is not supported by the BACKUP/RESTORE commands. + + Args: + client (Client): ClickHouse client instance. + parameters (ClickhouseBackupQueryParameters): Dataclass containing query parameters. + """ + query = Template("INSERT INTO `${database}`.`${table}` SELECT * FROM s3('${export_path}')").substitute( + asdict(parameters) + ) + client.query(query) diff --git a/src/pos/tasks/clickhouse_backup.py b/src/pos/tasks/clickhouse_backup.py new file mode 100644 index 00000000..318c5644 --- /dev/null +++ b/src/pos/tasks/clickhouse_backup.py @@ -0,0 +1,67 @@ +# Clickhouse backup task +from urllib.parse import urljoin + +from clickhouse_connect.driver.exceptions import DatabaseError +from loguru import logger +from otter.task.model import Spec, Task, TaskContext +from otter.task.task_reporter import report +from otter.util.errors import OtterError + +from pos.services.clickhouse import ( + ClickhouseBackupQueryParameters, + ClickhouseInstanceManager, + backup_table, + export_to_s3, + get_table_engine, +) + + +class ClickhouseBackupError(OtterError): + """Base class for exceptions in this module.""" + + +class ClickhouseBackupSpec(Spec): + """Configuration fields for the backup Clickhouse task.""" + + service_name: str = 'ch-pos' + clickhouse_database: str = 'ot' + table: str + gcs_base_path: str + + +class ClickhouseBackup(Task): + def __init__(self, spec: ClickhouseBackupSpec, context: TaskContext) -> None: + super().__init__(spec, context) + self.spec: ClickhouseBackupSpec + self.backup_url = urljoin( + self.spec.gcs_base_path, + '/'.join([ + self.context.scratchpad.sentinel_dict.get('product'), + str(self.context.scratchpad.sentinel_dict.get('release')), + self.spec.table, + ]) + + '/', + ) + self.export_url = urljoin(self.backup_url, 'export.parquet.lz4') + + @report + def run(self) -> Task: + logger.debug('Backing up ClickHouse') + client = ClickhouseInstanceManager(name=self.spec.service_name, database=self.spec.clickhouse_database).client() + if not client: + raise ClickhouseBackupError(f'Clickhouse service {self.spec.service_name} failed to start') + parameters = ClickhouseBackupQueryParameters( + database=self.spec.clickhouse_database, + table=self.spec.table, + backup_path=self.backup_url, + export_path=self.export_url, + ) + try: + backup_table(client, parameters) + table_engine = get_table_engine(client, self.spec.clickhouse_database, self.spec.table) + if table_engine == 'EmbeddedRocksDB': + # insert into s3 table because BACKUP does not support this engine + export_to_s3(client, parameters) + except DatabaseError as db_err: + raise ClickhouseBackupError(f'Clickhouse backup failed: {db_err}') from db_err + return self diff --git a/src/pos/tasks/clickhouse_create_database.py b/src/pos/tasks/clickhouse_create_database.py new file mode 100644 index 00000000..87e837a8 --- /dev/null +++ b/src/pos/tasks/clickhouse_create_database.py @@ -0,0 +1,37 @@ +# Clickhouse start task +import clickhouse_connect +from clickhouse_connect.driver.exceptions import DatabaseError +from loguru import logger +from otter.task.model import Spec, Task, TaskContext +from otter.task.task_reporter import report +from otter.util.errors import OtterError + +from pos.services.clickhouse import create_database + + +class ClickhouseCreateDatabaseError(OtterError): + """Base class for exceptions in this module.""" + + +class ClickhouseCreateDatabaseSpec(Spec): + """Configuration fields for the Clickhouse create database task.""" + + host: str = 'localhost' + port: str = '8123' + clickhouse_database: str = 'ot' + + +class ClickhouseCreateDatabase(Task): + def __init__(self, spec: ClickhouseCreateDatabaseSpec, context: TaskContext) -> None: + super().__init__(spec, context) + self.spec: ClickhouseCreateDatabaseSpec + + @report + def run(self) -> Task: + logger.debug('Create Clickhouse database') + try: + client = clickhouse_connect.get_client(host=self.spec.host, port=self.spec.port, database='default') + create_database(client, self.spec.clickhouse_database, exists_ok=False) + except DatabaseError as db_err: + raise ClickhouseCreateDatabaseError(f'Create Clickhouse database error : {db_err}') from db_err + return self diff --git a/src/pos/tasks/clickhouse_load.py b/src/pos/tasks/clickhouse_load.py index 67c2f8eb..162aaaee 100644 --- a/src/pos/tasks/clickhouse_load.py +++ b/src/pos/tasks/clickhouse_load.py @@ -1,6 +1,5 @@ # Clickhouse load task from pathlib import Path -from typing import Self from clickhouse_connect.driver.tools import insert_file from loguru import logger diff --git a/src/pos/tasks/clickhouse_restore.py b/src/pos/tasks/clickhouse_restore.py new file mode 100644 index 00000000..e22390a9 --- /dev/null +++ b/src/pos/tasks/clickhouse_restore.py @@ -0,0 +1,71 @@ +# Clickhouse restore task +from urllib.parse import urljoin + +import clickhouse_connect +from clickhouse_connect.driver.exceptions import DatabaseError +from loguru import logger +from otter.task.model import Spec, Task, TaskContext +from otter.task.task_reporter import report +from otter.util.errors import OtterError + +from pos.services.clickhouse import ( + ClickhouseBackupQueryParameters, + get_table_engine, + import_from_s3, + restore_table, +) + + +class ClickhouseRestoreError(OtterError): + """Base class for exceptions in this module.""" + + +class ClickhouseRestoreSpec(Spec): + """Configuration fields for the restore Clickhouse task.""" + + host: str = 'localhost' + port: str = '8123' + clickhouse_database: str = 'ot' + table: str + gcs_base_path: str + + +class ClickhouseRestore(Task): + def __init__(self, spec: ClickhouseRestoreSpec, context: TaskContext) -> None: + super().__init__(spec, context) + self.spec: ClickhouseRestoreSpec + self.backup_url = urljoin( + self.spec.gcs_base_path, + '/'.join([ + self.context.scratchpad.sentinel_dict.get('product'), + str(self.context.scratchpad.sentinel_dict.get('release')), + self.spec.table, + ]) + + '/', + ) + self.export_url = urljoin(self.backup_url, 'export.parquet.lz4') + + @report + def run(self) -> Task: + logger.debug('Restore ClickHouse') + try: + client = clickhouse_connect.get_client( + host=self.spec.host, port=self.spec.port, database=self.spec.clickhouse_database + ) + except DatabaseError as db_err: + raise ClickhouseRestoreError(f'Clickhouse client connection failed: {db_err}') from db_err + parameters = ClickhouseBackupQueryParameters( + database=self.spec.clickhouse_database, + table=self.spec.table, + backup_path=self.backup_url, + export_path=self.export_url, + ) + try: + restore_table(client, parameters) + table_engine = get_table_engine(client, self.spec.clickhouse_database, self.spec.table) + if table_engine == 'EmbeddedRocksDB': + # insert into s3 table because BACKUP/RESTORE does not support this engine + import_from_s3(client, parameters) + except DatabaseError as db_err: + raise ClickhouseRestoreError(f'Clickhouse restore from S3 failed: {db_err}') from db_err + return self diff --git a/src/pos/tasks/clickhouse_start.py b/src/pos/tasks/clickhouse_start.py index 940ae464..87e0760e 100644 --- a/src/pos/tasks/clickhouse_start.py +++ b/src/pos/tasks/clickhouse_start.py @@ -1,12 +1,10 @@ # Clickhouse start task -from typing import Self - from loguru import logger from otter.task.model import Spec, Task, TaskContext from otter.task.task_reporter import report from otter.util.errors import OtterError -from pos.services.clickhouse import ClickhouseInstanceManager +from pos.services.clickhouse import ClickhouseInstanceManager, create_database class ClickhouseStartError(OtterError): @@ -36,6 +34,8 @@ def run(self) -> Task: ) clickhouse.start(self.spec.volume_data, self.spec.volume_logs) client = clickhouse.client() - parameters = {'table': self.spec.clickhouse_database} - client.query(query='CREATE DATABASE IF NOT EXISTS {table:Identifier}', parameters=parameters) + if client: + create_database(client, self.spec.clickhouse_database) + else: + raise ClickhouseStartError(f'Clickhouse service {self.spec.service_name} failed instantiate client') return self diff --git a/src/pos/tasks/clickhouse_stop.py b/src/pos/tasks/clickhouse_stop.py index cd0ff741..9499c9e8 100644 --- a/src/pos/tasks/clickhouse_stop.py +++ b/src/pos/tasks/clickhouse_stop.py @@ -1,6 +1,5 @@ # Clickhouse stop task -from typing import Self - +from loguru import logger from otter.task.model import Spec, Task, TaskContext from otter.task.task_reporter import report from otter.util.errors import OtterError @@ -25,6 +24,7 @@ def __init__(self, spec: ClickhouseStopSpec, context: TaskContext) -> None: @report def run(self) -> Task: + logger.debug(f'stopping clickhouse instance {self.spec.service_name}') clickhouse = ClickhouseInstanceManager(name=self.spec.service_name) clickhouse.stop() return self diff --git a/uv.lock b/uv.lock index 43f64410..a7fe9abd 100644 --- a/uv.lock +++ b/uv.lock @@ -231,11 +231,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.17.0" +version = "3.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027, upload-time = "2025-01-21T20:04:49.099Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164, upload-time = "2025-01-21T20:04:47.734Z" }, + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, ] [[package]] @@ -331,7 +331,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.0.0" +version = "3.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -341,9 +341,9 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/d7/dfa74049c4faa3b4d68fa1a10a7eab5a76c57d0788b47c27f927bedc606d/google_cloud_storage-3.0.0.tar.gz", hash = "sha256:2accb3e828e584888beff1165e5f3ac61aa9088965eb0165794a82d8c7f95297", size = 7665253, upload-time = "2025-01-29T20:59:39.839Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/84/6afc2ffdf31f6247a6bab6ba070e073fb05e0fda56adf59ce52ac591a033/google_cloud_storage-3.1.1.tar.gz", hash = "sha256:f9c8f965cafd1d38509f8e2b070339e0e9e5bf050774653bf36213d4ea6104c0", size = 7668109, upload-time = "2025-06-18T11:06:52.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/ae/1a50f07161301e40a30b2e40744a7b85ffab7add16e044417925eccf9bbf/google_cloud_storage-3.0.0-py2.py3-none-any.whl", hash = "sha256:f85fd059650d2dbb0ac158a9a6b304b66143b35ed2419afec2905ca522eb2c6a", size = 173860, upload-time = "2025-01-29T20:59:36.944Z" }, + { url = "https://files.pythonhosted.org/packages/89/4f/b922e919f6e1ea5905f1427fadf1a3f56a85e79e2b0037fec182f6b437dd/google_cloud_storage-3.1.1-py3-none-any.whl", hash = "sha256:ba7e6ae2be5a7a08742f001e23ec6a0c17d78c620f63bf8e0e7c2cbdddb407de", size = 175464, upload-time = "2025-06-18T11:06:51.043Z" }, ] [[package]] @@ -615,7 +615,7 @@ wheels = [ [[package]] name = "opentargets-otter" -version = "25.0.2" +version = "25.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -626,9 +626,9 @@ dependencies = [ { name = "requests" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/80/c545a56fad0de0e6c662da14e53bb76bab26b48bf70b11d3445db53be7e5/opentargets_otter-25.0.2.tar.gz", hash = "sha256:43bd07a29a8affc74d3c0951e3b61f9419e8524cc6118bbc1739bdb9896ffe6d", size = 74529, upload-time = "2025-05-01T15:08:22.872Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/7a/e2896c0809572fd128124065672f5334e83a858ead360629e6353d0f9898/opentargets_otter-25.0.9.tar.gz", hash = "sha256:4ff4eb931163b8268f7687ab324f5a794a8f7c00bfa0b78d3df9815f77ad1645", size = 82236, upload-time = "2025-10-07T14:06:00.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/7b/447c0f76e8d4a7b0dbcf8798ecef32c6b79b94011751b741afabc971ebd9/opentargets_otter-25.0.2-py3-none-any.whl", hash = "sha256:0b0552723bf84e747cf525ec5d20646a07df5a514806e5a6991aef049f27b8c7", size = 49500, upload-time = "2025-05-01T15:08:20.895Z" }, + { url = "https://files.pythonhosted.org/packages/6a/2c/af46ab2bd66ed86673bbb0ef85b4ceb535ad313eb68d407d3d8fdea5cb34/opentargets_otter-25.0.9-py3-none-any.whl", hash = "sha256:0e210345d4643929fe6f21c4dbe5702001fdb1b2ed036aaf53c3c6a496eb466b", size = 50289, upload-time = "2025-10-07T14:05:59.057Z" }, ] [[package]] @@ -788,7 +788,7 @@ requires-dist = [ { name = "google-cloud-bigquery", specifier = ">=3.34.0" }, { name = "google-cloud-compute", specifier = ">=1.31.0" }, { name = "opensearch-py", specifier = ">=3.0.0" }, - { name = "opentargets-otter", specifier = "==25.0.2" }, + { name = "opentargets-otter", specifier = ">=25.0.9" }, { name = "orjson", specifier = ">=3.10.18" }, { name = "ot-croissant", git = "https://github.com/opentargets/ot_croissant?tag=v0.1.6" }, { name = "polars", specifier = ">=1.31.0" }, @@ -903,41 +903,45 @@ wheels = [ [[package]] name = "pydantic" -version = "2.10.6" +version = "2.11.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681, upload-time = "2025-01-24T01:42:12.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696, upload-time = "2025-01-24T01:42:10.371Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, ] [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.33.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443, upload-time = "2024-12-18T11:31:54.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709, upload-time = "2024-12-18T11:29:03.193Z" }, - { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273, upload-time = "2024-12-18T11:29:05.306Z" }, - { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027, upload-time = "2024-12-18T11:29:07.294Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888, upload-time = "2024-12-18T11:29:09.249Z" }, - { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738, upload-time = "2024-12-18T11:29:11.23Z" }, - { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138, upload-time = "2024-12-18T11:29:16.396Z" }, - { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025, upload-time = "2024-12-18T11:29:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633, upload-time = "2024-12-18T11:29:23.877Z" }, - { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404, upload-time = "2024-12-18T11:29:25.872Z" }, - { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130, upload-time = "2024-12-18T11:29:29.252Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946, upload-time = "2024-12-18T11:29:31.338Z" }, - { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387, upload-time = "2024-12-18T11:29:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453, upload-time = "2024-12-18T11:29:35.533Z" }, - { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186, upload-time = "2024-12-18T11:29:37.649Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, ] [[package]] @@ -1081,7 +1085,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1089,9 +1093,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] [[package]] @@ -1278,6 +1282,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, ] +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + [[package]] name = "tzdata" version = "2025.2" @@ -1289,11 +1305,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.3.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] [[package]]