Files
cvat-ai--cvat/tests/python/sdk/util.py
T
2026-07-13 13:32:23 +08:00

167 lines
4.8 KiB
Python

# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import contextlib
import http.server
import ssl
import textwrap
import threading
from collections.abc import Container, Generator
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from urllib.parse import urlparse
import pytest
import requests
from cvat_sdk.api_client.rest import RESTClientObject
from cvat_sdk.core.helpers import DeferredTqdmProgressReporter
from shared.utils.config import BASE_URL
def make_pbar(file, **kwargs):
return DeferredTqdmProgressReporter({"file": file, "mininterval": 0, **kwargs})
def generate_coco_json(filename: Path, img_info: tuple[Path, int, int]):
image_filename, image_width, image_height = img_info
content = generate_coco_anno(
image_filename.name,
image_width=image_width,
image_height=image_height,
)
with open(filename, "w") as coco:
coco.write(content)
def generate_coco_anno(image_path: str, image_width: int, image_height: int) -> str:
return (
textwrap.dedent("""
{
"categories": [
{
"id": 1,
"name": "car",
"supercategory": ""
},
{
"id": 2,
"name": "person",
"supercategory": ""
}
],
"images": [
{
"coco_url": "",
"date_captured": "",
"flickr_url": "",
"license": 0,
"id": 0,
"file_name": "%(image_path)s",
"height": %(image_height)d,
"width": %(image_width)d
}
],
"annotations": [
{
"category_id": 1,
"id": 1,
"image_id": 0,
"iscrowd": 0,
"segmentation": [
[]
],
"area": 17702.0,
"bbox": [
574.0,
407.0,
167.0,
106.0
]
}
]
}
""")
% {
"image_path": image_path,
"image_height": image_height,
"image_width": image_width,
}
)
def restrict_api_requests(
monkeypatch: pytest.MonkeyPatch, allow_paths: Container[str] = ()
) -> None:
original_request = RESTClientObject.request
def restricted_request(self, method, url, *args, **kwargs):
parsed_url = urlparse(url)
if parsed_url.path in allow_paths:
return original_request(self, method, url, *args, **kwargs)
raise RuntimeError("Disallowed!")
monkeypatch.setattr(RESTClientObject, "request", restricted_request)
@dataclass(frozen=True)
class OrgResourceHierarchy:
owner_username: str
maintainer_username: str
org_slug: str
project_id: int
task_id: int
job_id: int
issue_id: int | None = None
comment_id: int | None = None
@contextlib.contextmanager
def https_reverse_proxy() -> Generator[str, None, None]:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
cert_dir = Path(__file__).parent
ssl_context.load_cert_chain(cert_dir / "self-signed.crt", cert_dir / "self-signed.key")
with http.server.HTTPServer(("localhost", 0), _ProxyHttpRequestHandler) as proxy_server:
proxy_server.socket = ssl_context.wrap_socket(
proxy_server.socket,
server_side=True,
)
server_thread = threading.Thread(target=proxy_server.serve_forever)
server_thread.start()
try:
yield f"https://localhost:{proxy_server.server_port}"
finally:
proxy_server.shutdown()
server_thread.join()
class _ProxyHttpRequestHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
response = requests.get(**self._shared_request_args())
self._translate_response(response)
def do_POST(self):
body_length = int(self.headers["Content-Length"])
response = requests.post(data=self.rfile.read(body_length), **self._shared_request_args())
self._translate_response(response)
def _shared_request_args(self) -> dict[str, Any]:
headers = {k.lower(): v for k, v in self.headers.items()}
del headers["host"]
return {"url": BASE_URL + self.path, "headers": headers, "timeout": 60, "stream": True}
def _translate_response(self, response: requests.Response) -> None:
self.send_response(response.status_code)
for key, value in response.headers.items():
self.send_header(key, value)
self.end_headers()
# Need to use raw here to prevent requests from handling Content-Encoding.
self.wfile.write(response.raw.read())