Thibaut's picture
Fix import paths for metrics evaluation - corrected relative imports and client class names
03a45bc
"""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,
)