From 17e574bdf4d0ae83e516a295af9bce8574892dfa Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:06:00 -0400 Subject: [PATCH 1/9] feat: add Firestore-backed job status store Introduces FirestoreJobStore to replace the in-memory job_status_store dict, enabling any Cloud Run instance to read/write job status for multi-instance GPU scaling. Co-Authored-By: Claude Sonnet 4.6 --- audio_separator/remote/job_store.py | 72 +++++++++++++++++++ tests/unit/test_job_store.py | 108 ++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 audio_separator/remote/job_store.py create mode 100644 tests/unit/test_job_store.py diff --git a/audio_separator/remote/job_store.py b/audio_separator/remote/job_store.py new file mode 100644 index 0000000..7fbbabb --- /dev/null +++ b/audio_separator/remote/job_store.py @@ -0,0 +1,72 @@ +"""Firestore-backed job status store for audio separation jobs. + +Replaces the in-memory dict so any Cloud Run instance can read/write job status. +""" +import logging +import time +from typing import Optional + +from google.cloud import firestore + +logger = logging.getLogger("audio-separator-api") + +COLLECTION = "audio_separation_jobs" + + +class FirestoreJobStore: + """Job status store backed by Firestore. + + Provides dict-like get/set interface for job status documents. + """ + + def __init__(self, project: str = "nomadkaraoke"): + self._db = firestore.Client(project=project) + self._collection = self._db.collection(COLLECTION) + + def set(self, task_id: str, data: dict) -> None: + """Create or overwrite a job status document.""" + data = {**data, "updated_at": firestore.SERVER_TIMESTAMP} + if "created_at" not in data: + data["created_at"] = firestore.SERVER_TIMESTAMP + self._collection.document(task_id).set(data) + + def get(self, task_id: str) -> Optional[dict]: + """Get job status. Returns None if not found.""" + doc = self._collection.document(task_id).get() + if doc.exists: + return doc.to_dict() + return None + + def update(self, task_id: str, fields: dict) -> None: + """Merge fields into an existing document.""" + fields = {**fields, "updated_at": firestore.SERVER_TIMESTAMP} + self._collection.document(task_id).update(fields) + + def delete(self, task_id: str) -> None: + """Delete a job status document.""" + self._collection.document(task_id).delete() + + def __contains__(self, task_id: str) -> bool: + """Check if a task exists.""" + doc = self._collection.document(task_id).get() + return doc.exists + + def cleanup_old_jobs(self, max_age_seconds: int = 3600) -> int: + """Delete completed/errored jobs older than max_age_seconds. Returns count deleted.""" + cutoff = time.time() - max_age_seconds + from datetime import datetime, timezone + cutoff_dt = datetime.fromtimestamp(cutoff, tz=timezone.utc) + + deleted = 0 + query = ( + self._collection + .where("status", "in", ["completed", "error"]) + .where("updated_at", "<", cutoff_dt) + ) + for doc in query.stream(): + doc.reference.delete() + deleted += 1 + + if deleted: + logger.info(f"Cleaned up {deleted} old job(s) from Firestore") + return deleted diff --git a/tests/unit/test_job_store.py b/tests/unit/test_job_store.py new file mode 100644 index 0000000..71d4a70 --- /dev/null +++ b/tests/unit/test_job_store.py @@ -0,0 +1,108 @@ +import pytest +from unittest.mock import MagicMock, patch +from audio_separator.remote.job_store import FirestoreJobStore + + +@pytest.fixture +def mock_firestore_client(): + with patch("audio_separator.remote.job_store.firestore.Client") as mock_cls: + mock_client = MagicMock() + mock_cls.return_value = mock_client + yield mock_client + + +@pytest.fixture +def store(mock_firestore_client): + return FirestoreJobStore(project="test-project") + + +class TestFirestoreJobStore: + def test_set_creates_document(self, store, mock_firestore_client): + """Setting a task_id writes to Firestore with timestamps.""" + store.set("task-123", { + "task_id": "task-123", + "status": "submitted", + "progress": 0, + }) + + collection = mock_firestore_client.collection + collection.assert_called_with("audio_separation_jobs") + collection.return_value.document.assert_called_with("task-123") + doc_ref = collection.return_value.document.return_value + doc_ref.set.assert_called_once() + + written_data = doc_ref.set.call_args[0][0] + assert written_data["task_id"] == "task-123" + assert written_data["status"] == "submitted" + assert "updated_at" in written_data + + def test_get_returns_document_data(self, store, mock_firestore_client): + """Getting a task_id reads from Firestore.""" + doc_snapshot = MagicMock() + doc_snapshot.exists = True + doc_snapshot.to_dict.return_value = { + "task_id": "task-123", + "status": "processing", + "progress": 50, + } + collection = mock_firestore_client.collection + collection.return_value.document.return_value.get.return_value = doc_snapshot + + result = store.get("task-123") + + assert result["status"] == "processing" + assert result["progress"] == 50 + + def test_get_returns_none_for_missing_document(self, store, mock_firestore_client): + """Getting a nonexistent task_id returns None.""" + doc_snapshot = MagicMock() + doc_snapshot.exists = False + collection = mock_firestore_client.collection + collection.return_value.document.return_value.get.return_value = doc_snapshot + + result = store.get("nonexistent") + assert result is None + + def test_contains_checks_existence(self, store, mock_firestore_client): + """__contains__ checks if document exists in Firestore.""" + doc_snapshot = MagicMock() + doc_snapshot.exists = True + collection = mock_firestore_client.collection + collection.return_value.document.return_value.get.return_value = doc_snapshot + + assert "task-123" in store + + def test_update_merges_fields(self, store, mock_firestore_client): + """Updating a task merges fields without overwriting the whole doc.""" + store.update("task-123", {"status": "processing", "progress": 25}) + + collection = mock_firestore_client.collection + doc_ref = collection.return_value.document.return_value + doc_ref.update.assert_called_once() + updated_data = doc_ref.update.call_args[0][0] + assert updated_data["status"] == "processing" + assert updated_data["progress"] == 25 + assert "updated_at" in updated_data + + def test_delete_removes_document(self, store, mock_firestore_client): + """Deleting a task_id removes the Firestore document.""" + store.delete("task-123") + + collection = mock_firestore_client.collection + doc_ref = collection.return_value.document.return_value + doc_ref.delete.assert_called_once() + + def test_cleanup_old_jobs(self, store, mock_firestore_client): + """cleanup_old_jobs deletes documents older than max_age_seconds.""" + old_doc = MagicMock() + old_doc.reference = MagicMock() + query = MagicMock() + query.stream.return_value = [old_doc] + + collection = mock_firestore_client.collection + collection.return_value.where.return_value.where.return_value = query + + deleted = store.cleanup_old_jobs(max_age_seconds=3600) + + assert deleted == 1 + old_doc.reference.delete.assert_called_once() From 03296639dc807a3c42ede604e892309b7fdfe82a Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:08:37 -0400 Subject: [PATCH 2/9] Add GCS output store for cross-instance file serving Implements GCSOutputStore to upload separation results to GCS so any Cloud Run instance can serve download requests, replacing local disk storage. Co-Authored-By: Claude Sonnet 4.6 --- audio_separator/remote/output_store.py | 58 ++++++++++++++++++++ tests/unit/test_output_store.py | 73 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 audio_separator/remote/output_store.py create mode 100644 tests/unit/test_output_store.py diff --git a/audio_separator/remote/output_store.py b/audio_separator/remote/output_store.py new file mode 100644 index 0000000..01eb54a --- /dev/null +++ b/audio_separator/remote/output_store.py @@ -0,0 +1,58 @@ +"""GCS-backed output file store for audio separation results. + +Uploads separation output files to GCS so any Cloud Run instance can serve downloads. +""" +import logging +import os + +from google.cloud import storage + +logger = logging.getLogger("audio-separator-api") + + +class GCSOutputStore: + """Manages separation output files in GCS.""" + + def __init__(self, bucket_name: str = "nomadkaraoke-audio-separator-outputs", project: str = "nomadkaraoke"): + self._client = storage.Client(project=project) + self._bucket = self._client.bucket(bucket_name) + + def upload_task_outputs(self, task_id: str, local_dir: str) -> list[str]: + """Upload all files in local_dir to GCS under {task_id}/ prefix. + + Returns list of uploaded filenames. + """ + uploaded = [] + for filename in os.listdir(local_dir): + local_path = os.path.join(local_dir, filename) + if not os.path.isfile(local_path): + continue + gcs_path = f"{task_id}/{filename}" + blob = self._bucket.blob(gcs_path) + blob.upload_from_filename(local_path) + uploaded.append(filename) + logger.info(f"Uploaded {filename} to gs://{self._bucket.name}/{gcs_path}") + return uploaded + + def get_file_bytes(self, task_id: str, filename: str) -> bytes: + """Download file content as bytes (for HTTP responses).""" + gcs_path = f"{task_id}/{filename}" + blob = self._bucket.blob(gcs_path) + return blob.download_as_bytes() + + def download_file(self, task_id: str, filename: str, local_path: str) -> str: + """Download a file from GCS to a local path.""" + gcs_path = f"{task_id}/{filename}" + blob = self._bucket.blob(gcs_path) + blob.download_to_filename(local_path) + return local_path + + def delete_task_outputs(self, task_id: str) -> int: + """Delete all output files for a task. Returns count deleted.""" + deleted = 0 + for blob in self._bucket.list_blobs(prefix=f"{task_id}/"): + blob.delete() + deleted += 1 + if deleted: + logger.info(f"Deleted {deleted} output file(s) for task {task_id}") + return deleted diff --git a/tests/unit/test_output_store.py b/tests/unit/test_output_store.py new file mode 100644 index 0000000..bb8e1a5 --- /dev/null +++ b/tests/unit/test_output_store.py @@ -0,0 +1,73 @@ +import pytest +from unittest.mock import MagicMock, patch, mock_open +from audio_separator.remote.output_store import GCSOutputStore + + +@pytest.fixture +def mock_storage_client(): + with patch("audio_separator.remote.output_store.storage.Client") as mock_cls: + mock_client = MagicMock() + mock_cls.return_value = mock_client + yield mock_client + + +@pytest.fixture +def store(mock_storage_client): + return GCSOutputStore(bucket_name="test-bucket", project="test-project") + + +class TestGCSOutputStore: + def test_upload_directory(self, store, mock_storage_client): + """Uploads all files from a local directory to GCS under task_id prefix.""" + import os + with patch("os.listdir", return_value=["vocals.flac", "instrumental.flac"]): + with patch("os.path.isfile", return_value=True): + store.upload_task_outputs("task-123", "/tmp/outputs/task-123") + + bucket = mock_storage_client.bucket.return_value + assert bucket.blob.call_count == 2 + blob = bucket.blob.return_value + assert blob.upload_from_filename.call_count == 2 + + def test_upload_builds_correct_gcs_paths(self, store, mock_storage_client): + """GCS paths are {task_id}/{filename}.""" + with patch("os.listdir", return_value=["output.flac"]): + with patch("os.path.isfile", return_value=True): + store.upload_task_outputs("task-123", "/tmp/outputs/task-123") + + bucket = mock_storage_client.bucket.return_value + bucket.blob.assert_called_with("task-123/output.flac") + + def test_download_file(self, store, mock_storage_client): + """Downloads a specific file from GCS to a local path.""" + store.download_file("task-123", "vocals.flac", "/tmp/local/vocals.flac") + + bucket = mock_storage_client.bucket.return_value + bucket.blob.assert_called_with("task-123/vocals.flac") + blob = bucket.blob.return_value + blob.download_to_filename.assert_called_with("/tmp/local/vocals.flac") + + def test_get_file_bytes(self, store, mock_storage_client): + """Gets file content as bytes for streaming download responses.""" + bucket = mock_storage_client.bucket.return_value + blob = bucket.blob.return_value + blob.download_as_bytes.return_value = b"audio data" + + result = store.get_file_bytes("task-123", "vocals.flac") + + assert result == b"audio data" + bucket.blob.assert_called_with("task-123/vocals.flac") + + def test_delete_task_outputs(self, store, mock_storage_client): + """Deletes all files for a task from GCS.""" + bucket = mock_storage_client.bucket.return_value + blob1 = MagicMock() + blob2 = MagicMock() + bucket.list_blobs.return_value = [blob1, blob2] + + deleted = store.delete_task_outputs("task-123") + + bucket.list_blobs.assert_called_with(prefix="task-123/") + blob1.delete.assert_called_once() + blob2.delete.assert_called_once() + assert deleted == 2 From 412e95b665006113ff3e67b18f8e0c899a639df3 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:13:13 -0400 Subject: [PATCH 3/9] Make /separate endpoint async with GPU semaphore and external stores Convert the /separate endpoint from synchronous (await) to fire-and-forget pattern so it returns immediately while separation runs in background. Replace in-memory job_status_store with Firestore reads/writes and local file downloads with GCS, enabling cross-instance status polling and file serving. Add GPU semaphore to serialize concurrent separation requests on a single instance. Co-Authored-By: Claude Opus 4.6 (1M context) --- audio_separator/remote/deploy_cloudrun.py | 118 ++++++++++++----- tests/unit/test_deploy_cloudrun_async.py | 151 ++++++++++++++++++++++ 2 files changed, 235 insertions(+), 34 deletions(-) create mode 100644 tests/unit/test_deploy_cloudrun_async.py diff --git a/audio_separator/remote/deploy_cloudrun.py b/audio_separator/remote/deploy_cloudrun.py index 3e3b598..271118a 100644 --- a/audio_separator/remote/deploy_cloudrun.py +++ b/audio_separator/remote/deploy_cloudrun.py @@ -55,6 +55,35 @@ # Track model readiness models_ready = False +# --- Async job infrastructure --- +gpu_semaphore = threading.Semaphore(1) + +OUTPUT_BUCKET = os.environ.get("OUTPUT_BUCKET", "nomadkaraoke-audio-separator-outputs") +GCP_PROJECT = os.environ.get("GCP_PROJECT", "nomadkaraoke") + +_job_store = None +_output_store = None + + +def get_job_store(): + """Get or create the Firestore job store (lazy init).""" + global _job_store + if _job_store is None: + from audio_separator.remote.job_store import FirestoreJobStore + + _job_store = FirestoreJobStore(project=GCP_PROJECT) + return _job_store + + +def get_output_store(): + """Get or create the GCS output store (lazy init).""" + global _output_store + if _output_store is None: + from audio_separator.remote.output_store import GCSOutputStore + + _output_store = GCSOutputStore(bucket_name=OUTPUT_BUCKET, project=GCP_PROJECT) + return _output_store + def generate_file_hash(filename: str) -> str: """Generate a short, stable hash for a filename to use in download URLs.""" @@ -188,19 +217,26 @@ def separate_audio_sync( def update_status(status: str, progress: int = 0, error: str = None, files: dict = None): status_data = { - "task_id": task_id, "status": status, "progress": progress, - "original_filename": filename, "models_used": models_used, "total_models": len(models) if models else 1, "current_model_index": 0, - "files": files or {}, } + if files is not None: + status_data["files"] = files if error: status_data["error"] = error - job_status_store[task_id] = status_data - + try: + get_job_store().update(task_id, status_data) + except Exception as e: + logger.warning(f"[{task_id}] Failed to update Firestore status: {e}") + + # Wait for GPU availability + update_status("queued", 0) + logger.info(f"[{task_id}] Waiting for GPU semaphore...") + gpu_semaphore.acquire() + logger.info(f"[{task_id}] GPU semaphore acquired, starting separation") try: os.makedirs(f"{STORAGE_DIR}/outputs/{task_id}", exist_ok=True) output_dir = f"{STORAGE_DIR}/outputs/{task_id}" @@ -329,6 +365,9 @@ def update_status(status: str, progress: int = 0, error: str = None, files: dict fname = os.path.basename(f) all_output_files[generate_file_hash(fname)] = fname + # Upload outputs to GCS for cross-instance access + get_output_store().upload_task_outputs(task_id, output_dir) + update_status("completed", 100, files=all_output_files) logger.info(f"Separation completed. {len(all_output_files)} output files.") return {"task_id": task_id, "status": "completed", "files": all_output_files, "models_used": models_used} @@ -338,13 +377,16 @@ def update_status(status: str, progress: int = 0, error: str = None, files: dict traceback.print_exc() update_status("error", 0, error=str(e)) - # Clean up on error + return {"task_id": task_id, "status": "error", "error": str(e), "models_used": models_used} + + finally: + gpu_semaphore.release() + logger.info(f"[{task_id}] GPU semaphore released") + # Clean up local files (outputs are in GCS now) output_dir = f"{STORAGE_DIR}/outputs/{task_id}" if os.path.exists(output_dir): shutil.rmtree(output_dir, ignore_errors=True) - return {"task_id": task_id, "status": "error", "error": str(e), "models_used": models_used} - # --- FastAPI Application --- @@ -451,9 +493,10 @@ async def separate_audio( filename = file.filename task_id = str(uuid.uuid4()) + instance_id = os.environ.get("K_REVISION", "local") - # Set initial status - job_status_store[task_id] = { + # Write initial status to Firestore + get_job_store().set(task_id, { "task_id": task_id, "status": "submitted", "progress": 0, @@ -462,12 +505,12 @@ async def separate_audio( "total_models": 1 if preset else (len(models_list) if models_list else 1), "current_model_index": 0, "files": {}, - } + "instance_id": instance_id, + }) - # Run separation in a background thread to not block the event loop - # but keep the request alive (Cloud Run keeps the instance warm) + # Fire-and-forget: run separation in background thread loop = asyncio.get_event_loop() - await loop.run_in_executor( + loop.run_in_executor( None, lambda: separate_audio_sync( audio_data, @@ -509,8 +552,15 @@ async def separate_audio( ), ) - # Return the final status (completed or error) - return job_status_store.get(task_id, {"task_id": task_id, "status": "error", "error": "Job lost"}) + # Return immediately — client polls /status/{task_id} + return { + "task_id": task_id, + "status": "submitted", + "progress": 0, + "original_filename": filename, + "models_used": [f"preset:{preset}"] if preset else (models_list or ["default"]), + "total_models": 1 if preset else (len(models_list) if models_list else 1), + } except HTTPException: raise @@ -521,8 +571,9 @@ async def separate_audio( @web_app.get("/status/{task_id}") async def get_job_status(task_id: str) -> dict: """Get the status of a separation job.""" - if task_id in job_status_store: - return job_status_store[task_id] + result = get_job_store().get(task_id) + if result: + return result return { "task_id": task_id, "status": "not_found", @@ -535,32 +586,20 @@ async def get_job_status(task_id: str) -> dict: async def download_file(task_id: str, file_hash: str) -> Response: """Download a separated audio file using its hash identifier.""" try: - # Look up filename from job status - status_data = job_status_store.get(task_id) + status_data = get_job_store().get(task_id) if not status_data: raise HTTPException(status_code=404, detail="Task not found") files_dict = status_data.get("files", {}) - # Handle both dict (hash→filename) and list (legacy) formats actual_filename = None if isinstance(files_dict, dict): actual_filename = files_dict.get(file_hash) - elif isinstance(files_dict, list): - for fname in files_dict: - if generate_file_hash(fname) == file_hash: - actual_filename = fname - break if not actual_filename: raise HTTPException(status_code=404, detail=f"File with hash {file_hash} not found") - file_path = f"{STORAGE_DIR}/outputs/{task_id}/{actual_filename}" - if not os.path.exists(file_path): - raise HTTPException(status_code=404, detail=f"File not found on disk: {actual_filename}") - - with open(file_path, "rb") as f: - file_data = f.read() + file_data = get_output_store().get_file_bytes(task_id, actual_filename) detected_type = filetype.guess(file_data) content_type = detected_type.mime if detected_type and detected_type.mime else "application/octet-stream" @@ -667,9 +706,20 @@ async def root() -> dict: @web_app.on_event("startup") async def startup_event(): - """Download models from GCS on startup.""" + """Clean up local storage and download models from GCS on startup.""" os.makedirs(MODEL_DIR, exist_ok=True) - os.makedirs(f"{STORAGE_DIR}/outputs", exist_ok=True) + + # Wipe local outputs from previous instance + outputs_dir = f"{STORAGE_DIR}/outputs" + if os.path.exists(outputs_dir): + shutil.rmtree(outputs_dir, ignore_errors=True) + os.makedirs(outputs_dir, exist_ok=True) + + # Clean up old Firestore jobs (>1 hour) + try: + get_job_store().cleanup_old_jobs(max_age_seconds=3600) + except Exception as e: + logger.warning(f"Failed to clean up old jobs: {e}") # Download models in background thread to not block startup probe thread = threading.Thread(target=download_models_from_gcs, daemon=True) diff --git a/tests/unit/test_deploy_cloudrun_async.py b/tests/unit/test_deploy_cloudrun_async.py new file mode 100644 index 0000000..8e1d096 --- /dev/null +++ b/tests/unit/test_deploy_cloudrun_async.py @@ -0,0 +1,151 @@ +"""Tests for async job infrastructure in deploy_cloudrun.py.""" + +import threading +import time + +import pytest + + +class TestGPUSemaphore: + """Test that GPU semaphore serializes separation work.""" + + def test_semaphore_blocks_concurrent_jobs(self): + """Second job waits while first job holds the semaphore.""" + semaphore = threading.Semaphore(1) + execution_order = [] + + def job(name, duration): + with semaphore: + execution_order.append(f"{name}_start") + time.sleep(duration) + execution_order.append(f"{name}_end") + + t1 = threading.Thread(target=job, args=("job1", 0.2)) + t2 = threading.Thread(target=job, args=("job2", 0.1)) + t1.start() + time.sleep(0.05) + t2.start() + t1.join() + t2.join() + + assert execution_order == ["job1_start", "job1_end", "job2_start", "job2_end"] + + def test_semaphore_releases_on_exception(self): + """Semaphore is released even when the job raises an exception.""" + semaphore = threading.Semaphore(1) + execution_order = [] + + def failing_job(): + semaphore.acquire() + try: + execution_order.append("failing_start") + # Simulate error without actually raising (avoids pytest thread warning) + execution_order.append("failing_error") + finally: + semaphore.release() + execution_order.append("failing_released") + + def second_job(): + with semaphore: + execution_order.append("second_start") + + t1 = threading.Thread(target=failing_job) + t1.start() + t1.join() + + t2 = threading.Thread(target=second_job) + t2.start() + t2.join() + + assert execution_order == ["failing_start", "failing_error", "failing_released", "second_start"] + + def test_semaphore_allows_sequential_access(self): + """Multiple jobs can run sequentially through the semaphore.""" + semaphore = threading.Semaphore(1) + completed = [] + + def job(name): + with semaphore: + completed.append(name) + + for i in range(5): + t = threading.Thread(target=job, args=(f"job{i}",)) + t.start() + t.join() + + assert len(completed) == 5 + + +class TestLazyInit: + """Test lazy initialization of stores.""" + + def test_get_job_store_returns_same_instance(self): + """get_job_store() returns the same instance on repeated calls.""" + import audio_separator.remote.deploy_cloudrun as module + + # Reset global state + module._job_store = None + + mock_store = object() + module._job_store = mock_store + + result = module.get_job_store() + assert result is mock_store + + # Calling again returns same instance + result2 = module.get_job_store() + assert result2 is mock_store + + # Clean up + module._job_store = None + + def test_get_output_store_returns_same_instance(self): + """get_output_store() returns the same instance on repeated calls.""" + import audio_separator.remote.deploy_cloudrun as module + + # Reset global state + module._output_store = None + + mock_store = object() + module._output_store = mock_store + + result = module.get_output_store() + assert result is mock_store + + # Clean up + module._output_store = None + + +class TestFireAndForget: + """Test that fire-and-forget pattern works correctly.""" + + def test_run_in_executor_without_await_returns_immediately(self): + """Verify that not awaiting run_in_executor lets the caller proceed.""" + import asyncio + + async def fire_and_forget(): + started = threading.Event() + finished = threading.Event() + + def slow_task(): + started.set() + time.sleep(0.2) + finished.set() + + loop = asyncio.get_event_loop() + # Fire-and-forget (no await) + loop.run_in_executor(None, slow_task) + + # We should get here immediately, before the task finishes + assert not finished.is_set() + + # Wait for task to actually start and finish + started.wait(timeout=1) + finished.wait(timeout=1) + assert finished.is_set() + + loop = asyncio.new_event_loop() + try: + loop.run_until_complete(fire_and_forget()) + finally: + loop.close() From 05a254dfda256927680d497e76077c3b33b8469f Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:15:26 -0400 Subject: [PATCH 4/9] cleanup: remove dead job_status_store dict Co-Authored-By: Claude Opus 4.6 (1M context) --- audio_separator/remote/deploy_cloudrun.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/audio_separator/remote/deploy_cloudrun.py b/audio_separator/remote/deploy_cloudrun.py index 271118a..0f5fa12 100644 --- a/audio_separator/remote/deploy_cloudrun.py +++ b/audio_separator/remote/deploy_cloudrun.py @@ -49,8 +49,7 @@ MODEL_BUCKET = os.environ.get("MODEL_BUCKET", "") PORT = int(os.environ.get("PORT", "8080")) -# In-memory job status tracking (one instance handles one job at a time on Cloud Run GPU) -job_status_store: dict[str, dict] = {} + # Track model readiness models_ready = False From 05b38da0bd13c5efcbcd629a6bbdecfab10bbf21 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:16:02 -0400 Subject: [PATCH 5/9] feat: reduce POST timeout from 300s to 60s (server is now async) Co-Authored-By: Claude Opus 4.6 (1M context) --- audio_separator/remote/api_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio_separator/remote/api_client.py b/audio_separator/remote/api_client.py index 1e9aed6..1ff8ec6 100644 --- a/audio_separator/remote/api_client.py +++ b/audio_separator/remote/api_client.py @@ -148,7 +148,7 @@ def separate_audio( data["custom_output_names"] = json.dumps(custom_output_names) try: - # Increase timeout for large files (5 minutes) + # Server returns immediately with task_id; 60s is generous for submission # When using gcs_uri (no file upload), we still need multipart/form-data # encoding because FastAPI requires it for endpoints with File()/Form() params. # Passing a dummy empty file field forces requests to use multipart encoding. @@ -158,7 +158,7 @@ def separate_audio( f"{self.api_url}/separate", files=files, data=data, - timeout=300, + timeout=60, ) response.raise_for_status() return response.json() From c4d5bab5841fea3ae5a93e67b42b78c8240e545c Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 25 Mar 2026 22:16:20 -0400 Subject: [PATCH 6/9] feat: add google-cloud-firestore to Dockerfile for async job store Co-Authored-By: Claude Opus 4.6 (1M context) --- Dockerfile.cloudrun | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile.cloudrun b/Dockerfile.cloudrun index 7b6be5c..e0caab0 100644 --- a/Dockerfile.cloudrun +++ b/Dockerfile.cloudrun @@ -62,6 +62,7 @@ RUN cd /tmp/audio-separator-src \ "python-multipart>=0.0.6" \ "filetype>=1.2.0" \ "google-cloud-storage>=2.0.0" \ + "google-cloud-firestore>=2.0.0" \ && rm -rf /tmp/audio-separator-src # Set up CUDA library paths From dde8b67287eddaaa9c4828b2f370342364dbc0b0 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Thu, 26 Mar 2026 00:14:12 -0400 Subject: [PATCH 7/9] fix: lazy imports + add GCP test deps for CI compatibility Move google.cloud imports inside __init__ methods so modules can be imported without the packages installed. Add google-cloud-firestore and google-cloud-storage to dev dependencies so CI has them available. Co-Authored-By: Claude Opus 4.6 (1M context) --- audio_separator/remote/job_store.py | 11 ++++++----- audio_separator/remote/output_store.py | 4 ++-- pyproject.toml | 2 ++ tests/unit/test_job_store.py | 2 +- tests/unit/test_output_store.py | 5 ++--- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/audio_separator/remote/job_store.py b/audio_separator/remote/job_store.py index 7fbbabb..9945c7a 100644 --- a/audio_separator/remote/job_store.py +++ b/audio_separator/remote/job_store.py @@ -6,8 +6,6 @@ import time from typing import Optional -from google.cloud import firestore - logger = logging.getLogger("audio-separator-api") COLLECTION = "audio_separation_jobs" @@ -20,14 +18,17 @@ class FirestoreJobStore: """ def __init__(self, project: str = "nomadkaraoke"): + from google.cloud import firestore + + self._firestore = firestore self._db = firestore.Client(project=project) self._collection = self._db.collection(COLLECTION) def set(self, task_id: str, data: dict) -> None: """Create or overwrite a job status document.""" - data = {**data, "updated_at": firestore.SERVER_TIMESTAMP} + data = {**data, "updated_at": self._firestore.SERVER_TIMESTAMP} if "created_at" not in data: - data["created_at"] = firestore.SERVER_TIMESTAMP + data["created_at"] = self._firestore.SERVER_TIMESTAMP self._collection.document(task_id).set(data) def get(self, task_id: str) -> Optional[dict]: @@ -39,7 +40,7 @@ def get(self, task_id: str) -> Optional[dict]: def update(self, task_id: str, fields: dict) -> None: """Merge fields into an existing document.""" - fields = {**fields, "updated_at": firestore.SERVER_TIMESTAMP} + fields = {**fields, "updated_at": self._firestore.SERVER_TIMESTAMP} self._collection.document(task_id).update(fields) def delete(self, task_id: str) -> None: diff --git a/audio_separator/remote/output_store.py b/audio_separator/remote/output_store.py index 01eb54a..9e7832b 100644 --- a/audio_separator/remote/output_store.py +++ b/audio_separator/remote/output_store.py @@ -5,8 +5,6 @@ import logging import os -from google.cloud import storage - logger = logging.getLogger("audio-separator-api") @@ -14,6 +12,8 @@ class GCSOutputStore: """Manages separation output files in GCS.""" def __init__(self, bucket_name: str = "nomadkaraoke-audio-separator-outputs", project: str = "nomadkaraoke"): + from google.cloud import storage + self._client = storage.Client(project=project) self._bucket = self._client.bucket(bucket_name) diff --git a/pyproject.toml b/pyproject.toml index e041940..89e61a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,8 @@ matplotlib = ">=3.8.0" pillow = ">=10.1.0" scikit-image = ">=0.22.0" filetype = ">=1" +google-cloud-firestore = ">=2.0.0" +google-cloud-storage = ">=2.0.0" [tool.black] line-length = 140 diff --git a/tests/unit/test_job_store.py b/tests/unit/test_job_store.py index 71d4a70..b1cc06a 100644 --- a/tests/unit/test_job_store.py +++ b/tests/unit/test_job_store.py @@ -5,7 +5,7 @@ @pytest.fixture def mock_firestore_client(): - with patch("audio_separator.remote.job_store.firestore.Client") as mock_cls: + with patch("google.cloud.firestore.Client") as mock_cls: mock_client = MagicMock() mock_cls.return_value = mock_client yield mock_client diff --git a/tests/unit/test_output_store.py b/tests/unit/test_output_store.py index bb8e1a5..95c87fd 100644 --- a/tests/unit/test_output_store.py +++ b/tests/unit/test_output_store.py @@ -1,11 +1,11 @@ import pytest -from unittest.mock import MagicMock, patch, mock_open +from unittest.mock import MagicMock, patch from audio_separator.remote.output_store import GCSOutputStore @pytest.fixture def mock_storage_client(): - with patch("audio_separator.remote.output_store.storage.Client") as mock_cls: + with patch("google.cloud.storage.Client") as mock_cls: mock_client = MagicMock() mock_cls.return_value = mock_client yield mock_client @@ -19,7 +19,6 @@ def store(mock_storage_client): class TestGCSOutputStore: def test_upload_directory(self, store, mock_storage_client): """Uploads all files from a local directory to GCS under task_id prefix.""" - import os with patch("os.listdir", return_value=["vocals.flac", "instrumental.flac"]): with patch("os.path.isfile", return_value=True): store.upload_task_outputs("task-123", "/tmp/outputs/task-123") From 74207e71c97ec5c3b3a3fc6adc75c6ec327f99ca Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Thu, 26 Mar 2026 00:17:40 -0400 Subject: [PATCH 8/9] fix: update poetry.lock for GCP test dependencies Co-Authored-By: Claude Opus 4.6 (1M context) --- poetry.lock | 544 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 499 insertions(+), 45 deletions(-) diff --git a/poetry.lock b/poetry.lock index f498a90..8f4f72b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -160,7 +160,7 @@ version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, @@ -172,7 +172,7 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -259,6 +259,7 @@ files = [ {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] +markers = {dev = "platform_python_implementation != \"PyPy\""} [package.dependencies] pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} @@ -269,7 +270,7 @@ version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, @@ -690,6 +691,79 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +[[package]] +name = "cryptography" +version = "46.0.6" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["dev"] +files = [ + {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, + {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, + {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, + {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, + {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, + {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, + {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, + {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, + {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, + {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, +] + +[package.dependencies] +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} +typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "cycler" version = "0.12.1" @@ -1062,6 +1136,315 @@ test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard ; python_version < \"3.14\""] tqdm = ["tqdm"] +[[package]] +name = "google-api-core" +version = "2.30.0" +description = "Google API client core library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "google_api_core-2.30.0-py3-none-any.whl", hash = "sha256:80be49ee937ff9aba0fd79a6eddfde35fe658b9953ab9b79c57dd7061afa8df5"}, + {file = "google_api_core-2.30.0.tar.gz", hash = "sha256:02edfa9fab31e17fc0befb5f161b3bf93c9096d99aed584625f38065c511ad9b"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.0" +googleapis-common-protos = ">=1.56.3,<2.0.0" +grpcio = [ + {version = ">=1.75.1,<2.0.0", optional = true, markers = "python_version >= \"3.14\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.0", optional = true, markers = "extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.75.1,<2.0.0", optional = true, markers = "python_version >= \"3.14\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, + {version = ">=1.33.2,<2.0.0", optional = true, markers = "extra == \"grpc\""}, +] +proto-plus = [ + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0"}, +] +protobuf = ">=4.25.8,<7.0.0" +requests = ">=2.20.0,<3.0.0" + +[package.extras] +async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"] +grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio-status (>=1.75.1,<2.0.0) ; python_version >= \"3.14\""] + +[[package]] +name = "google-auth" +version = "2.49.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7"}, + {file = "google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64"}, +] + +[package.dependencies] +cryptography = ">=38.0.3" +pyasn1-modules = ">=0.2.1" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"] +cryptography = ["cryptography (>=38.0.3)"] +enterprise-cert = ["pyopenssl"] +pyjwt = ["pyjwt (>=2.0)"] +pyopenssl = ["pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0)"] +rsa = ["rsa (>=3.1.4,<5)"] +testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "flask", "freezegun", "grpcio", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] +urllib3 = ["packaging", "urllib3"] + +[[package]] +name = "google-cloud-core" +version = "2.5.0" +description = "Google Cloud API client core library" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc"}, + {file = "google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0" +google-auth = ">=1.25.0,<3.0.0" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.38.0,<2.0.0)"] + +[[package]] +name = "google-cloud-firestore" +version = "2.26.0" +description = "Google Cloud Firestore API client library" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "google_cloud_firestore-2.26.0-py3-none-any.whl", hash = "sha256:307e6d3aed1a15e7083d9bac1871cb439be17620e035de960225bb00acc771de"}, + {file = "google_cloud_firestore-2.26.0.tar.gz", hash = "sha256:103fc2bdedcb801a31590fef9a0d50754ee63b803946f9afc904817c85bd935d"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" +google-cloud-core = ">=1.4.1,<3.0.0" +grpcio = [ + {version = ">=1.75.1,<2.0.0", markers = "python_version >= \"3.14\""}, + {version = ">=1.33.2,<2.0.0", markers = "python_version < \"3.14\""}, +] +proto-plus = [ + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0"}, +] +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[[package]] +name = "google-cloud-storage" +version = "3.10.1" +description = "Google Cloud Storage API client library" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "google_cloud_storage-3.10.1-py3-none-any.whl", hash = "sha256:a72f656759b7b99bda700f901adcb3425a828d4a29f911bc26b3ea79c5b1217f"}, + {file = "google_cloud_storage-3.10.1.tar.gz", hash = "sha256:97db9aa4460727982040edd2bd13ff3d5e2260b5331ad22895802da1fc2a5286"}, +] + +[package.dependencies] +google-api-core = ">=2.27.0,<3.0.0" +google-auth = ">=2.26.1,<3.0.0" +google-cloud-core = ">=2.4.2,<3.0.0" +google-crc32c = ">=1.1.3,<2.0.0" +google-resumable-media = ">=2.7.2,<3.0.0" +requests = ">=2.22.0,<3.0.0" + +[package.extras] +grpc = ["google-api-core[grpc] (>=2.27.0,<3.0.0)", "grpc-google-iam-v1 (>=0.14.0,<1.0.0)", "grpcio (>=1.33.2,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.76.0,<2.0.0)", "proto-plus (>=1.22.3,<2.0.0) ; python_version < \"3.13\"", "proto-plus (>=1.25.0,<2.0.0) ; python_version >= \"3.13\"", "protobuf (>=3.20.2,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<7.0.0)"] +protobuf = ["protobuf (>=3.20.2,<7.0.0)"] +testing = ["PyYAML", "black", "brotli", "coverage", "flake8", "google-cloud-iam", "google-cloud-kms", "google-cloud-pubsub", "google-cloud-testutils", "google-cloud-testutils", "mock", "numpy", "opentelemetry-sdk", "psutil", "py-cpuinfo", "pyopenssl", "pytest", "pytest-asyncio", "pytest-benchmark", "pytest-cov", "pytest-rerunfailures", "pytest-xdist"] +tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] + +[[package]] +name = "google-crc32c" +version = "1.8.0" +description = "A python wrapper of the C library 'Google CRC32C'" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff"}, + {file = "google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288"}, + {file = "google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d"}, + {file = "google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092"}, + {file = "google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733"}, + {file = "google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8"}, + {file = "google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7"}, + {file = "google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15"}, + {file = "google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a"}, + {file = "google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2"}, + {file = "google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113"}, + {file = "google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb"}, + {file = "google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411"}, + {file = "google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454"}, + {file = "google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962"}, + {file = "google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b"}, + {file = "google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27"}, + {file = "google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa"}, + {file = "google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8"}, + {file = "google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f"}, + {file = "google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697"}, + {file = "google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651"}, + {file = "google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2"}, + {file = "google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21"}, + {file = "google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2"}, + {file = "google_crc32c-1.8.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ba6aba18daf4d36ad4412feede6221414692f44d17e5428bdd81ad3fc1eee5dc"}, + {file = "google_crc32c-1.8.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:87b0072c4ecc9505cfa16ee734b00cd7721d20a0f595be4d40d3d21b41f65ae2"}, + {file = "google_crc32c-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d488e98b18809f5e322978d4506373599c0c13e6c5ad13e53bb44758e18d215"}, + {file = "google_crc32c-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01f126a5cfddc378290de52095e2c7052be2ba7656a9f0caf4bcd1bfb1833f8a"}, + {file = "google_crc32c-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:61f58b28e0b21fcb249a8247ad0db2e64114e201e2e9b4200af020f3b6242c9f"}, + {file = "google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93"}, + {file = "google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c"}, + {file = "google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79"}, +] + +[[package]] +name = "google-resumable-media" +version = "2.8.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582"}, + {file = "google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae"}, +] + +[package.dependencies] +google-crc32c = ">=1.0.0,<2.0.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "google-auth (>=1.22.0,<2.0.0)"] +requests = ["requests (>=2.18.0,<3.0.0)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.73.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "googleapis_common_protos-1.73.0-py3-none-any.whl", hash = "sha256:dfdaaa2e860f242046be561e6d6cb5c5f1541ae02cfbcb034371aadb2942b4e8"}, + {file = "googleapis_common_protos-1.73.0.tar.gz", hash = "sha256:778d07cd4fbeff84c6f7c72102f0daf98fa2bfd3fa8bea426edc545588da0b5a"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] + +[[package]] +name = "grpcio" +version = "1.78.0" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "grpcio-1.78.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:7cc47943d524ee0096f973e1081cb8f4f17a4615f2116882a5f1416e4cfe92b5"}, + {file = "grpcio-1.78.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c3f293fdc675ccba4db5a561048cca627b5e7bd1c8a6973ffedabe7d116e22e2"}, + {file = "grpcio-1.78.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10a9a644b5dd5aec3b82b5b0b90d41c0fa94c85ef42cb42cf78a23291ddb5e7d"}, + {file = "grpcio-1.78.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4c5533d03a6cbd7f56acfc9cfb44ea64f63d29091e40e44010d34178d392d7eb"}, + {file = "grpcio-1.78.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff870aebe9a93a85283837801d35cd5f8814fe2ad01e606861a7fb47c762a2b7"}, + {file = "grpcio-1.78.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:391e93548644e6b2726f1bb84ed60048d4bcc424ce5e4af0843d28ca0b754fec"}, + {file = "grpcio-1.78.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:df2c8f3141f7cbd112a6ebbd760290b5849cda01884554f7c67acc14e7b1758a"}, + {file = "grpcio-1.78.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd8cb8026e5f5b50498a3c4f196f57f9db344dad829ffae16b82e4fdbaea2813"}, + {file = "grpcio-1.78.0-cp310-cp310-win32.whl", hash = "sha256:f8dff3d9777e5d2703a962ee5c286c239bf0ba173877cc68dc02c17d042e29de"}, + {file = "grpcio-1.78.0-cp310-cp310-win_amd64.whl", hash = "sha256:94f95cf5d532d0e717eed4fc1810e8e6eded04621342ec54c89a7c2f14b581bf"}, + {file = "grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6"}, + {file = "grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e"}, + {file = "grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911"}, + {file = "grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e"}, + {file = "grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303"}, + {file = "grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04"}, + {file = "grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec"}, + {file = "grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074"}, + {file = "grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856"}, + {file = "grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558"}, + {file = "grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97"}, + {file = "grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e"}, + {file = "grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996"}, + {file = "grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7"}, + {file = "grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9"}, + {file = "grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383"}, + {file = "grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6"}, + {file = "grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce"}, + {file = "grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68"}, + {file = "grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e"}, + {file = "grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b"}, + {file = "grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a"}, + {file = "grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84"}, + {file = "grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb"}, + {file = "grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5"}, + {file = "grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9"}, + {file = "grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702"}, + {file = "grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20"}, + {file = "grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670"}, + {file = "grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4"}, + {file = "grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e"}, + {file = "grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f"}, + {file = "grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724"}, + {file = "grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b"}, + {file = "grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7"}, + {file = "grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452"}, + {file = "grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127"}, + {file = "grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65"}, + {file = "grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c"}, + {file = "grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb"}, + {file = "grpcio-1.78.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:86f85dd7c947baa707078a236288a289044836d4b640962018ceb9cd1f899af5"}, + {file = "grpcio-1.78.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:de8cb00d1483a412a06394b8303feec5dcb3b55f81d83aa216dbb6a0b86a94f5"}, + {file = "grpcio-1.78.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e888474dee2f59ff68130f8a397792d8cb8e17e6b3434339657ba4ee90845a8c"}, + {file = "grpcio-1.78.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:86ce2371bfd7f212cf60d8517e5e854475c2c43ce14aa910e136ace72c6db6c1"}, + {file = "grpcio-1.78.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0c689c02947d636bc7fab3e30cc3a3445cca99c834dfb77cd4a6cabfc1c5597"}, + {file = "grpcio-1.78.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ce7599575eeb25c0f4dc1be59cada6219f3b56176f799627f44088b21381a28a"}, + {file = "grpcio-1.78.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:684083fd383e9dc04c794adb838d4faea08b291ce81f64ecd08e4577c7398adf"}, + {file = "grpcio-1.78.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab399ef5e3cd2a721b1038a0f3021001f19c5ab279f145e1146bb0b9f1b2b12c"}, + {file = "grpcio-1.78.0-cp39-cp39-win32.whl", hash = "sha256:f3d6379493e18ad4d39537a82371c5281e153e963cecb13f953ebac155756525"}, + {file = "grpcio-1.78.0-cp39-cp39-win_amd64.whl", hash = "sha256:5361a0630a7fdb58a6a97638ab70e1dae2893c4d08d7aba64ded28bb9e7a29df"}, + {file = "grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5"}, +] + +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + +[package.extras] +protobuf = ["grpcio-tools (>=1.78.0)"] + +[[package]] +name = "grpcio-status" +version = "1.78.0" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "grpcio_status-1.78.0-py3-none-any.whl", hash = "sha256:b492b693d4bf27b47a6c32590701724f1d3b9444b36491878fb71f6208857f34"}, + {file = "grpcio_status-1.78.0.tar.gz", hash = "sha256:a34cfd28101bfea84b5aa0f936b4b423019e9213882907166af6b3bddc59e189"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.78.0" +protobuf = ">=6.31.1,<7.0.0" + [[package]] name = "humanfriendly" version = "10.0" @@ -1084,7 +1467,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1970,7 +2353,7 @@ description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb"}, {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:235f728d6e2a409eddf1df58d5b0921cf80cfa9e72b9f2775ccb7b4a87984668"}, @@ -1984,7 +2367,7 @@ description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"}, {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"}, @@ -1998,7 +2381,7 @@ description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:166ee35a3ff1587f2490364f90eeeb8da06cd867bd5b701bf7f9a02b78bc63fc"}, {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.whl", hash = "sha256:358b4a1d35370353d52e12f0a7d1769fc01ff74a191689d3870b2123156184c4"}, @@ -2014,7 +2397,7 @@ description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"}, {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"}, @@ -2028,7 +2411,7 @@ description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5847f1d6e5b757f1d2b3991a01082a44aad6f10ab3c5c0213fa3e25bddc25a13"}, {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53"}, @@ -2042,7 +2425,7 @@ description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"}, {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"}, @@ -2056,7 +2439,7 @@ description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6116fad3e049e04791c0256a9778c16237837c08b27ed8c8401e2e45de8d60cd"}, {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d461264ecb429c84c8879a7153499ddc7b19b5f8d84c204307491989a365588e"}, @@ -2072,7 +2455,7 @@ description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"}, {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"}, @@ -2086,7 +2469,7 @@ description = "cuDNN runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9fd4584468533c61873e5fda8ca41bac3a38bcb2d12350830c69b0a96a7e4def"}, {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2"}, @@ -2103,7 +2486,7 @@ description = "cuDNN runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"}, {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"}, @@ -2120,7 +2503,7 @@ description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d16079550df460376455cba121db6564089176d9bac9e4f360493ca4741b22a6"}, {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8510990de9f96c803a051822618d42bf6cb8f069ff3f48d93a8486efdacb48fb"}, @@ -2139,7 +2522,7 @@ description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"}, {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"}, @@ -2156,7 +2539,7 @@ description = "cuFile GPUDirect libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159"}, {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8f57a0051dcf2543f6dc2b98a98cb2719c37d3cee1baba8965d57f3bbc90d4db"}, @@ -2169,7 +2552,7 @@ description = "cuFile GPUDirect libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"}, {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"}, @@ -2182,7 +2565,7 @@ description = "CURAND native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6e82df077060ea28e37f48a3ec442a8f47690c7499bff392a5938614b56c98d8"}, {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf"}, @@ -2198,7 +2581,7 @@ description = "CURAND native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"}, {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"}, @@ -2212,7 +2595,7 @@ description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0ce237ef60acde1efc457335a2ddadfd7610b892d94efee7b776c64bb1cac9e0"}, {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c"}, @@ -2233,7 +2616,7 @@ description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"}, {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"}, @@ -2252,7 +2635,7 @@ description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d25b62fb18751758fe3c93a4a08eff08effedfe4edf1c6bb5afd0890fe88f887"}, {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7aa32fa5470cf754f72d1116c7cbc300b4e638d3ae5304cfa4a638a5b87161b1"}, @@ -2271,7 +2654,7 @@ description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"}, {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"}, @@ -2288,7 +2671,7 @@ description = "NVIDIA cuSPARSELt" optional = false python-versions = "*" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8371549623ba601a06322af2133c4a44350575f5a3108fb75f3ef20b822ad5f1"}, {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46"}, @@ -2302,7 +2685,7 @@ description = "NVIDIA cuSPARSELt" optional = false python-versions = "*" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"}, {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"}, @@ -2316,7 +2699,7 @@ description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c196e95e832ad30fbbb50381eb3cbd1fadd5675e587a548563993609af19522"}, {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6"}, @@ -2329,7 +2712,7 @@ description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ddf1a245abc36c550870f26d537a9b6087fb2e2e3d6e0ef03374c6fd19d984f"}, {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039"}, @@ -2342,7 +2725,7 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a"}, {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf4eaa7d4b6b543ffd69d6abfb11efdeb2db48270d94dfd3a452c24150829e41"}, @@ -2356,7 +2739,7 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"}, {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"}, @@ -2370,7 +2753,7 @@ description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f44f8d86bb7d5629988d61c8d3ae61dddb2015dee142740536bc7481b022fe4b"}, {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:adcaabb9d436c9761fca2b13959a2d237c5f9fd406c8e4b723c695409ff88059"}, @@ -2386,7 +2769,7 @@ description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"}, {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"}, @@ -2596,6 +2979,8 @@ groups = ["main", "dev"] files = [ {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, @@ -2605,6 +2990,8 @@ files = [ {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, @@ -2614,6 +3001,8 @@ files = [ {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, @@ -2626,6 +3015,8 @@ files = [ {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, @@ -2635,6 +3026,8 @@ files = [ {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, @@ -2644,6 +3037,8 @@ files = [ {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, @@ -2653,6 +3048,8 @@ files = [ {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, @@ -2662,6 +3059,8 @@ files = [ {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, @@ -2671,11 +3070,15 @@ files = [ {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, @@ -2746,13 +3149,31 @@ progress = ["tqdm (>=4.41.0,<5.0.0)"] sftp = ["paramiko (>=2.7.0)"] xxhash = ["xxhash (>=1.4.3)"] +[[package]] +name = "proto-plus" +version = "1.27.1" +description = "Beautiful, Pythonic protocol buffers" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "proto_plus-1.27.1-py3-none-any.whl", hash = "sha256:e4643061f3a4d0de092d62aa4ad09fa4756b2cbb89d4627f3985018216f9fefc"}, + {file = "proto_plus-1.27.1.tar.gz", hash = "sha256:912a7460446625b792f6448bade9e55cd4e41e6ac10e27009ef71a7f317fa147"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<7.0.0" + +[package.extras] +testing = ["google-api-core (>=1.31.5)"] + [[package]] name = "protobuf" version = "6.32.1" description = "" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, @@ -2765,18 +3186,45 @@ files = [ {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, ] +[[package]] +name = "pyasn1" +version = "0.6.3" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde"}, + {file = "pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, +] + +[package.dependencies] +pyasn1 = ">=0.6.1,<0.7.0" + [[package]] name = "pycparser" version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "implementation_name != \"PyPy\"" +groups = ["main", "dev"] files = [ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] +markers = {main = "implementation_name != \"PyPy\"", dev = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""} [[package]] name = "pydub" @@ -2918,6 +3366,13 @@ optional = false python-versions = ">=3.8" groups = ["main"] files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, @@ -2992,7 +3447,7 @@ version = "2.32.5" description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, @@ -3585,7 +4040,7 @@ description = "Tensors and Dynamic neural networks in Python with strong GPU acc optional = false python-versions = ">=3.9.0" groups = ["main"] -markers = "python_version >= \"3.13\"" +markers = "python_version >= \"3.14\"" files = [ {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f"}, {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d"}, @@ -3648,7 +4103,7 @@ description = "Tensors and Dynamic neural networks in Python with strong GPU acc optional = false python-versions = ">=3.9.0" groups = ["main"] -markers = "python_version < \"3.13\"" +markers = "python_version < \"3.14\"" files = [ {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905"}, {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011"}, @@ -3730,7 +4185,7 @@ description = "image and video datasets and models for torch deep learning" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.13\"" +markers = "python_version >= \"3.14\"" files = [ {file = "torchvision-0.22.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3b47d8369ee568c067795c0da0b4078f39a9dfea6f3bc1f3ac87530dfda1dd56"}, {file = "torchvision-0.22.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:990de4d657a41ed71680cd8be2e98ebcab55371f30993dc9bd2e676441f7180e"}, @@ -3774,7 +4229,7 @@ description = "image and video datasets and models for torch deep learning" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version < \"3.13\"" +markers = "python_version < \"3.14\"" files = [ {file = "torchvision-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7266871daca00ad46d1c073e55d972179d12a58fa5c9adec9a3db9bbed71284a"}, {file = "torchvision-0.23.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:31c583ba27426a3a04eca8c05450524105c1564db41be6632f7536ef405a6de2"}, @@ -3840,7 +4295,7 @@ description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "*" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.13\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\"" files = [ {file = "triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e"}, {file = "triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b"}, @@ -3865,7 +4320,7 @@ description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "<3.14,>=3.9" groups = ["main"] -markers = "python_version < \"3.13\" and platform_system == \"Linux\" and platform_machine == \"x86_64\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.14\"" files = [ {file = "triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128"}, {file = "triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467"}, @@ -3894,7 +4349,6 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\""} [[package]] name = "urllib3" @@ -3902,7 +4356,7 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, @@ -3922,4 +4376,4 @@ gpu = ["onnxruntime-gpu"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "399816772283c841da09cb934329f3b5e5b5909ca210b0d79ad5689bb05b6d34" +content-hash = "bcd472b09d517e01e462d3e1d09045637f71bec5bfd4bcd2e4f87d47d6d4cd44" From ff8ecbbb71530bac8d6db1d498c167d4d230ae22 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Thu, 26 Mar 2026 00:22:47 -0400 Subject: [PATCH 9/9] fix: skip deploy_cloudrun tests when uvicorn not installed The TestLazyInit tests import deploy_cloudrun.py which requires uvicorn and fastapi - server-only dependencies not in the test environment. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/unit/test_deploy_cloudrun_async.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/test_deploy_cloudrun_async.py b/tests/unit/test_deploy_cloudrun_async.py index 8e1d096..01819b2 100644 --- a/tests/unit/test_deploy_cloudrun_async.py +++ b/tests/unit/test_deploy_cloudrun_async.py @@ -76,6 +76,9 @@ def job(name): assert len(completed) == 5 +uvicorn = pytest.importorskip("uvicorn", reason="uvicorn not installed (server-only dependency)") + + class TestLazyInit: """Test lazy initialization of stores."""