"""CVAT API annotation methods.""" from typing import TYPE_CHECKING from metrics_evaluation.schema.cvat import CvatApiJobList, CvatApiTaskList from .retry import retry_with_backoff # TYPE_CHECKING is False at runtime but True during static type checking. # This allows us to import CvatApiClient for type hints without creating a circular # import (client.py imports AnnotationsMethods, and we need CvatApiClient for type hints). # Benefits: # - Avoids circular import errors at runtime # - Provides proper type checking during development # - No performance overhead (import only happens during type checking, not at runtime) # This is a recommended pattern in modern Python (PEP 484, PEP 563). # -- Claude Code if TYPE_CHECKING: from .client import CvatApiClient class AnnotationsMethods: """Annotation GET/PUT operations for CVAT API.""" def __init__(self, client: "CvatApiClient"): """Initialize annotation methods with client reference. Args: client: Parent CvatApiClient instance """ self.client = client @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_job_annotations( self, job_id: int, token: str | None = None ) -> CvatApiJobList: """Fetch annotations for a job. Args: job_id: The ID of the job token: Authentication token (optional) Returns: Job annotations object with tags, shapes, and tracks """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/jobs/{job_id}/annotations" return self.client._make_request( method="GET", url=url, headers=headers, resource_name="job annotations", resource_id=job_id, response_model=CvatApiJobList, ) @retry_with_backoff(max_retries=3, initial_delay=1.0) def get_task_annotations( self, task_id: int, token: str | None = None ) -> CvatApiTaskList: """Fetch annotations for a task. Args: task_id: The ID of the task token: Authentication token (optional) Returns: Task annotations object with tags, shapes, and tracks """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/tasks/{task_id}/annotations" return self.client._make_request( method="GET", url=url, headers=headers, resource_name="task annotations", resource_id=task_id, response_model=CvatApiTaskList, ) def put_job_annotations( self, job_id: int, annotations: CvatApiJobList, token: str | None = None ) -> CvatApiJobList: """Update annotations for a job. Args: job_id: The ID of the job annotations: Job annotations to upload token: Authentication token (optional) Returns: Updated job annotations """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/jobs/{job_id}/annotations" return self.client._make_request( method="PUT", url=url, headers=headers, json_data=annotations.model_dump(), resource_name="job annotations", resource_id=job_id, response_model=CvatApiJobList, ) def put_task_annotations( self, task_id: int, annotations: CvatApiTaskList, token: str | None = None ) -> CvatApiTaskList: """Update annotations for a task. Args: task_id: The ID of the task annotations: Task annotations to upload token: Authentication token (optional) Returns: Updated task annotations """ headers = self.client._get_headers(token) url = f"{self.client.cvat_host}/api/tasks/{task_id}/annotations" return self.client._make_request( method="PUT", url=url, headers=headers, json_data=annotations.model_dump(), resource_name="task annotations", resource_id=task_id, response_model=CvatApiTaskList, )