chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:32:23 +08:00
commit 10df941303
2913 changed files with 455804 additions and 0 deletions
View File
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import cvat_sdk.auto_annotation as cvataa
import cvat_sdk.models as models
import PIL.Image
spec = cvataa.DetectionFunctionSpec(
labels=[
cvataa.label_spec("car", 0),
],
)
def detect(
context: cvataa.DetectionFunctionContext, image: PIL.Image.Image
) -> list[models.LabeledShapeRequest]:
if context.conv_mask_to_poly:
return [cvataa.polygon(0, [0, 0, 0, 1, 1, 1])]
else:
return [cvataa.mask(0, [1, 0, 0, 0, 0])]
@@ -0,0 +1,21 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import cvat_sdk.auto_annotation as cvataa
import cvat_sdk.models as models
import PIL.Image
spec = cvataa.DetectionFunctionSpec(
labels=[
cvataa.label_spec("car", 0),
],
)
def detect(
context: cvataa.DetectionFunctionContext, image: PIL.Image.Image
) -> list[models.LabeledShapeRequest]:
return [
cvataa.rectangle(0, [context.conf_threshold, 1, 1, 1]),
]
+6
View File
@@ -0,0 +1,6 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
# Force execution of fixture definitions
from sdk.fixtures import * # pylint: disable=wildcard-import
+21
View File
@@ -0,0 +1,21 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import cvat_sdk.auto_annotation as cvataa
import cvat_sdk.models as models
import PIL.Image
spec = cvataa.DetectionFunctionSpec(
labels=[
cvataa.label_spec("car", 0),
],
)
def detect(
context: cvataa.DetectionFunctionContext, image: PIL.Image.Image
) -> list[models.LabeledShapeRequest]:
return [
cvataa.rectangle(0, [1, 2, 3, 4]),
]
@@ -0,0 +1,31 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
from types import SimpleNamespace as namespace
import cvat_sdk.auto_annotation as cvataa
import cvat_sdk.models as models
import PIL.Image
def create(s: str, i: int, f: float, b: bool) -> cvataa.DetectionFunction:
assert s == "string"
assert i == 123
assert f == 5.5
assert b is False
spec = cvataa.DetectionFunctionSpec(
labels=[
cvataa.label_spec("car", 0),
],
)
def detect(
context: cvataa.DetectionFunctionContext, image: PIL.Image.Image
) -> list[models.LabeledShapeRequest]:
return [
cvataa.rectangle(0, [1, 2, 3, 4]),
]
return namespace(spec=spec, detect=detect)
+240
View File
@@ -0,0 +1,240 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import os
from datetime import timedelta
from io import BytesIO
from pathlib import Path
from unittest import mock
import packaging.version as pv
import pytest
from cvat_cli._internal.agent import (
_Event,
_NewReconnectionDelay,
_parse_event_stream,
_TaskCacheLimiter,
)
from cvat_sdk import Client
from cvat_sdk.api_client import models
from cvat_sdk.core.proxies.tasks import ResourceType
from sdk.util import https_reverse_proxy
from .util import TestCliBase, generate_images, run_cli
class TestCliMisc(TestCliBase):
def test_can_warn_on_mismatching_server_version(self, monkeypatch, caplog):
def mocked_version(_):
return pv.Version("0")
# We don't actually run a separate process in the tests here, so it works
monkeypatch.setattr(Client, "get_server_version", mocked_version)
self.run_cli("task", "ls")
assert "Server version '0' is not compatible with SDK version" in caplog.text
@pytest.mark.parametrize("verify", [True, False])
def test_can_control_ssl_verification_with_arg(self, verify: bool):
with https_reverse_proxy() as proxy_url:
if verify:
insecure_args = []
else:
insecure_args = ["--insecure"]
run_cli(
self,
f"--auth={self.user}:{self.password}",
f"--server-host={proxy_url}",
*insecure_args,
"task",
"ls",
expected_code=1 if verify else 0,
)
stdout = self.stdout.getvalue()
if not verify:
for line in stdout.splitlines():
int(line)
def test_can_control_organization_context(self):
org = "cli-test-org"
self.client.organizations.create(models.OrganizationWriteRequest(org))
files = generate_images(self.tmp_path, 1)
stdout = self.run_cli(
"task",
"create",
"personal_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--completion_verification_period=0.01",
organization="",
)
personal_task_id = int(stdout.split()[-1])
stdout = self.run_cli(
"task",
"create",
"org_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--completion_verification_period=0.01",
organization=org,
)
org_task_id = int(stdout.split()[-1])
personal_task_ids = list(map(int, self.run_cli("task", "ls", organization="").split()))
assert personal_task_id in personal_task_ids
assert org_task_id not in personal_task_ids
org_task_ids = list(map(int, self.run_cli("task", "ls", organization=org).split()))
assert personal_task_id not in org_task_ids
assert org_task_id in org_task_ids
all_task_ids = list(map(int, self.run_cli("task", "ls").split()))
assert personal_task_id in all_task_ids
assert org_task_id in all_task_ids
def test_can_use_access_token_env_variable(
self, monkeypatch: pytest.MonkeyPatch, access_tokens
):
token = next(t for t in access_tokens)["private_key"]
from cvat_sdk.api_client.rest import RESTClientObject
original_request = RESTClientObject.request
calls = 0
def patched_request(self, *args, **kwargs):
nonlocal calls
calls += 1
assert kwargs["headers"].get("Authorization") == f"Bearer {token}"
return original_request(self, *args, **kwargs)
monkeypatch.setenv("CVAT_ACCESS_TOKEN", token)
monkeypatch.setattr(RESTClientObject, "request", patched_request)
self.run_cli("task", "ls", authenticate=False)
assert calls
def test_can_use_current_user_env_variable(self, monkeypatch: pytest.MonkeyPatch):
# set all user env vars supported by getuser()
for env_var in ("LOGNAME", "USER", "LNAME", "USERNAME"):
monkeypatch.setenv(env_var, self.user)
from getpass import getuser as original_getuser
from cvat_sdk.core.auth import default_auth_factory
with (
mock.patch(
"cvat_sdk.core.auth.default_auth_factory", wraps=default_auth_factory
) as mock_auth_factory,
mock.patch("getpass.getuser", wraps=original_getuser) as mock_getuser,
mock.patch("getpass.getpass", return_value=self.password) as mock_getpass,
):
self.run_cli("task", "ls", authenticate=False)
mock_auth_factory.assert_called_once()
mock_getuser.assert_called()
mock_getpass.assert_called_once()
def test_can_use_pass_env_variable(self, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv("PASS", self.password)
from cvat_sdk.core.auth import get_auth_factory
with (
mock.patch(
"cvat_sdk.core.auth.get_auth_factory", wraps=get_auth_factory
) as mock_auth_factory,
mock.patch("getpass.getpass") as mock_getpass,
):
self.run_cli(f"--auth={self.user}", "task", "ls", authenticate=False)
mock_auth_factory.assert_called_once()
mock_getpass.assert_not_called()
@pytest.mark.parametrize(
["lines", "messages"],
[
# empty
([], []),
([""], [_Event("", "")]),
# event only
(["event: test", ""], [_Event("test", "")]),
(["event: foo", "event: bar", ""], [_Event("bar", "")]),
# data only
(["data: test", ""], [_Event("", "test")]),
(["data: foo", "data: bar", ""], [_Event("", "foo\nbar")]),
# event and data
(["event: test", "data: foo", "data: bar", ""], [_Event("test", "foo\nbar")]),
(["data: foo", "event: test", "data: bar", ""], [_Event("test", "foo\nbar")]),
(["data: foo", "data: bar", "event: test", ""], [_Event("test", "foo\nbar")]),
# fields without values
(["event: test", "event", ""], [_Event("", "")]),
(["data: test", "data", ""], [_Event("", "test\n")]),
# incomplete event
(["event: test", "data: foo"], []),
# multiple events
(
["event: test1", "data: foo", "", "event: test2", "data: bar", ""],
[_Event("test1", "foo"), _Event("test2", "bar")],
),
# comments
([":"], []),
([":1", "event: test", ":2", "data: foo", ":3", ""], [_Event("test", "foo")]),
# retry
(["retry: 1234"], [_NewReconnectionDelay(timedelta(milliseconds=1234))]),
(["retry", "retry:", "retry: a"], []),
# no space
(["event:test", "data:foo", ""], [_Event("test", "foo")]),
# two spaces
(["event: test", "data: foo", ""], [_Event(" test", " foo")]),
# carriage return
(["event: test\r", "data: foo\r", "\r"], [_Event("test", "foo")]),
],
)
def test_parse_event_stream(lines, messages):
stream = BytesIO(b"".join(line.encode() + b"\n" for line in lines))
assert list(_parse_event_stream(stream)) == messages
def test_task_cache_limiter_keeps_last_10_tasks(
tmp_path: Path,
fxt_login: tuple[Client, str],
fxt_logger,
):
client = fxt_login[0]
client.logger = fxt_logger[0]
client.config.cache_dir = tmp_path / "cache"
limiter = _TaskCacheLimiter(client)
for task_id in range(1, 13):
limiter._cache_manager.task_dir(task_id).mkdir(parents=True)
with limiter.using_cache_for_task(task_id):
pass
if task_id <= 10:
for cached_task_id in range(1, task_id + 1):
assert limiter._cache_manager.task_dir(cached_task_id).exists()
elif task_id == 11:
assert not limiter._cache_manager.task_dir(1).exists()
for cached_task_id in range(2, 12):
assert limiter._cache_manager.task_dir(cached_task_id).exists()
elif task_id == 12:
assert not limiter._cache_manager.task_dir(1).exists()
assert not limiter._cache_manager.task_dir(2).exists()
for cached_task_id in range(3, 13):
assert limiter._cache_manager.task_dir(cached_task_id).exists()
+196
View File
@@ -0,0 +1,196 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import json
import os
import zipfile
import pytest
from cvat_sdk.api_client import exceptions
from cvat_sdk.core.proxies.projects import Project
from cvat_sdk.core.proxies.tasks import ResourceType
from .util import TestCliBase, generate_images
class TestCliProjects(TestCliBase):
@pytest.fixture
def fxt_new_project(self):
project = self.client.projects.create(
spec={
"name": "test_project",
"labels": [{"name": "car"}, {"name": "person"}],
},
)
return project
@pytest.fixture
def fxt_project_with_task(self, fxt_new_project: Project):
files = generate_images(self.tmp_path, 5)
self.client.tasks.create_from_data(
spec={
"name": "test_task",
"project_id": fxt_new_project.id,
},
resource_type=ResourceType.LOCAL,
resources=files,
)
fxt_new_project.fetch()
return fxt_new_project
@pytest.fixture
def fxt_backup_file(self, fxt_project_with_task: Project):
backup_path = self.tmp_path / "backup.zip"
fxt_project_with_task.download_backup(backup_path)
yield backup_path
def test_can_create_project(self):
stdout = self.run_cli(
"project",
"create",
"new_project",
"--labels",
json.dumps([{"name": "car"}, {"name": "person"}]),
"--bug_tracker",
"https://bugs.example/",
)
project_id = int(stdout.rstrip("\n"))
created_project = self.client.projects.retrieve(project_id)
assert created_project.name == "new_project"
assert created_project.bug_tracker == "https://bugs.example/"
assert {label.name for label in created_project.get_labels()} == {"car", "person"}
def test_can_create_project_from_dataset(self, fxt_coco_dataset):
stdout = self.run_cli(
"project",
"create",
"new_project",
"--dataset_path",
os.fspath(fxt_coco_dataset),
"--dataset_format",
"COCO 1.0",
)
project_id = int(stdout.rstrip("\n"))
created_project = self.client.projects.retrieve(project_id)
assert created_project.name == "new_project"
assert {label.name for label in created_project.get_labels()} == {"car", "person"}
assert created_project.tasks.count == 1
def test_can_list_projects_in_simple_format(self, fxt_new_project: Project):
output = self.run_cli("project", "ls")
results = output.split("\n")
assert any(str(fxt_new_project.id) in r for r in results)
def test_can_list_project_in_json_format(self, fxt_new_project: Project):
output = self.run_cli("project", "ls", "--json")
results = json.loads(output)
assert any(r["id"] == fxt_new_project.id for r in results)
def test_can_delete_project(self, fxt_new_project: Project):
self.run_cli("project", "delete", str(fxt_new_project.id))
with pytest.raises(exceptions.NotFoundException):
fxt_new_project.fetch()
def test_can_download_project_backup(self, fxt_project_with_task: Project):
filename = self.tmp_path / f"project_{fxt_project_with_task.id}-cvat.zip"
self.run_cli(
"project",
"backup",
str(fxt_project_with_task.id),
str(filename),
"--completion_verification_period",
"0.01",
)
assert 0 < filename.stat().st_size
with zipfile.ZipFile(filename) as backup_zip:
assert "project.json" in backup_zip.namelist()
def test_can_download_project_backup_with_server_filename(self, fxt_project_with_task: Project):
output_dir = str(self.tmp_path / "save_dir") + os.path.sep
self.run_cli(
"project",
"backup",
str(fxt_project_with_task.id),
output_dir,
"--completion_verification_period",
"0.01",
)
output_dir_files = os.listdir(output_dir)
assert len(output_dir_files) == 1
assert os.stat(output_dir + output_dir_files[0]).st_size > 0
def test_can_create_project_from_backup(self, fxt_project_with_task: Project, fxt_backup_file):
stdout = self.run_cli("project", "create-from-backup", str(fxt_backup_file))
project_id = int(stdout.rstrip("\n"))
assert project_id
assert project_id != fxt_project_with_task.id
restored_project = self.client.projects.retrieve(project_id)
restored_tasks = restored_project.get_tasks()
assert len(restored_tasks) == 1
assert restored_tasks[0].size == fxt_project_with_task.get_tasks()[0].size
def test_can_download_project_annotations(self, fxt_project_with_task: Project):
filename = self.tmp_path / f"project_{fxt_project_with_task.id}-cvat.zip"
self.run_cli(
"project",
"export-dataset",
str(fxt_project_with_task.id),
str(filename),
"--format",
"CVAT for images 1.1",
"--with-images",
"no",
"--completion_verification_period",
"0.01",
)
assert 0 < filename.stat().st_size
def test_can_download_project_annotations_with_server_filename(
self, fxt_project_with_task: Project
):
output_dir = str(self.tmp_path / "save_dir") + os.path.sep
self.run_cli(
"project",
"export-dataset",
str(fxt_project_with_task.id),
output_dir,
"--format",
"CVAT for images 1.1",
"--with-images",
"yes",
"--completion_verification_period",
"0.01",
)
output_dir_files = os.listdir(output_dir)
assert len(output_dir_files) == 1
assert os.stat(output_dir + output_dir_files[0]).st_size > 0
def test_can_upload_project_annotations(self, fxt_new_project: Project, fxt_coco_dataset):
self.run_cli(
"project",
"import-dataset",
str(fxt_new_project.id),
os.fspath(fxt_coco_dataset),
"--format",
"COCO 1.0",
)
fxt_new_project.fetch()
assert fxt_new_project.tasks.count == 1
+350
View File
@@ -0,0 +1,350 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import json
import os
import zipfile
from pathlib import Path
import pytest
from cvat_sdk.api_client import exceptions
from cvat_sdk.core.proxies.tasks import ResourceType, Task
from PIL import Image
from sdk.util import generate_coco_json
from shared.utils.helpers import generate_image_file
from .util import TestCliBase, generate_images
class TestCliTasks(TestCliBase):
@pytest.fixture
def fxt_image_file(self):
img_path = self.tmp_path / "img_0.png"
with img_path.open("wb") as f:
f.write(generate_image_file(filename=str(img_path)).getvalue())
return img_path
@pytest.fixture
def fxt_coco_file(self, fxt_image_file: Path):
img_filename = fxt_image_file
img_size = Image.open(img_filename).size
ann_filename = self.tmp_path / "coco.json"
generate_coco_json(ann_filename, img_info=(img_filename, *img_size))
yield ann_filename
@pytest.fixture
def fxt_backup_file(self, fxt_new_task: Task, fxt_coco_file: str):
backup_path = self.tmp_path / "backup.zip"
fxt_new_task.import_annotations("COCO 1.0", filename=fxt_coco_file)
fxt_new_task.download_backup(backup_path)
yield backup_path
@pytest.fixture
def fxt_new_task(self):
files = generate_images(self.tmp_path, 5)
task = self.client.tasks.create_from_data(
spec={
"name": "test_task",
"labels": [{"name": "car"}, {"name": "person"}],
},
resource_type=ResourceType.LOCAL,
resources=files,
)
return task
def test_can_create_task_from_local_images(self):
files = generate_images(self.tmp_path, 5)
stdout = self.run_cli(
"task",
"create",
"test_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--labels",
json.dumps([{"name": "car"}, {"name": "person"}]),
"--completion_verification_period",
"0.01",
)
task_id = int(stdout.rstrip("\n"))
assert self.client.tasks.retrieve(task_id).size == 5
def test_can_create_task_from_local_images_with_parameters(self):
# Checks for regressions of <https://github.com/cvat-ai/cvat/issues/4962>
files = generate_images(self.tmp_path, 7)
files.sort(reverse=True)
frame_step = 3
stdout = self.run_cli(
"task",
"create",
"test_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--labels",
json.dumps([{"name": "car"}, {"name": "person"}]),
"--completion_verification_period",
"0.01",
"--sorting-method",
"predefined",
"--frame_step",
str(frame_step),
"--bug_tracker",
"http://localhost/bug",
)
task_id = int(stdout.rstrip("\n"))
task = self.client.tasks.retrieve(task_id)
frames = task.get_frames_info()
assert [f.name for f in frames] == [
f.name for i, f in enumerate(files) if i % frame_step == 0
]
assert task.get_meta().frame_filter == f"step={frame_step}"
assert task.bug_tracker == "http://localhost/bug"
def test_can_list_tasks_in_simple_format(self, fxt_new_task: Task):
output = self.run_cli("task", "ls")
results = output.split("\n")
assert any(str(fxt_new_task.id) in r for r in results)
def test_can_list_tasks_in_json_format(self, fxt_new_task: Task):
output = self.run_cli("task", "ls", "--json")
results = json.loads(output)
assert any(r["id"] == fxt_new_task.id for r in results)
def test_can_delete_task(self, fxt_new_task: Task):
self.run_cli("task", "delete", str(fxt_new_task.id))
with pytest.raises(exceptions.NotFoundException):
fxt_new_task.fetch()
def test_can_download_task_annotations(self, fxt_new_task: Task):
filename = self.tmp_path / "task_{fxt_new_task.id}-cvat.zip"
self.run_cli(
"task",
"export-dataset",
str(fxt_new_task.id),
str(filename),
"--format",
"CVAT for images 1.1",
"--with-images",
"no",
"--completion_verification_period",
"0.01",
)
assert 0 < filename.stat().st_size
def test_can_download_task_annotations_with_server_filename(self, fxt_new_task: Task):
output_dir = str(self.tmp_path / "save_dir") + os.path.sep
self.run_cli(
"task",
"export-dataset",
str(fxt_new_task.id),
output_dir,
"--format",
"CVAT for images 1.1",
"--with-images",
"no",
"--completion_verification_period",
"0.01",
)
output_dir_files = os.listdir(output_dir)
assert len(output_dir_files) == 1
assert os.stat(output_dir + output_dir_files[0]).st_size > 0
def test_can_download_task_annotations_to_current_directory(
self, fxt_new_task: Task, monkeypatch: pytest.MonkeyPatch
):
output_dir = self.tmp_path / "current_dir"
output_dir.mkdir()
monkeypatch.chdir(output_dir)
self.run_cli(
"task",
"export-dataset",
str(fxt_new_task.id),
"--format",
"CVAT for images 1.1",
"--with-images",
"no",
"--completion_verification_period",
"0.01",
)
output_dir_files = list(output_dir.iterdir())
assert len(output_dir_files) == 1
assert output_dir_files[0].stat().st_size > 0
def test_can_download_task_backup(self, fxt_new_task: Task):
filename = self.tmp_path / "task_{fxt_new_task.id}-cvat.zip"
self.run_cli(
"task",
"backup",
str(fxt_new_task.id),
str(filename),
"--completion_verification_period",
"0.01",
)
assert 0 < filename.stat().st_size
with zipfile.ZipFile(filename) as backup_zip:
assert "task.json" in backup_zip.namelist()
def test_can_download_task_backup_with_server_filename(self, fxt_new_task: Task):
output_dir = str(self.tmp_path / "save_dir") + os.path.sep
self.run_cli(
"task",
"backup",
str(fxt_new_task.id),
output_dir,
"--completion_verification_period",
"0.01",
)
output_dir_files = os.listdir(output_dir)
assert len(output_dir_files) == 1
assert os.stat(output_dir + output_dir_files[0]).st_size > 0
@pytest.mark.parametrize("quality", ("compressed", "original"))
def test_can_download_task_frames(self, fxt_new_task: Task, quality: str):
out_dir = str(self.tmp_path / "downloads")
self.run_cli(
"task",
"frames",
str(fxt_new_task.id),
"0",
"1",
"--outdir",
out_dir,
"--quality",
quality,
)
assert set(os.listdir(out_dir)) == {
"task_{}_frame_{:06d}.jpg".format(fxt_new_task.id, i) for i in range(2)
}
def test_can_upload_annotations(self, fxt_new_task: Task, fxt_coco_file: Path):
self.run_cli(
"task",
"import-dataset",
str(fxt_new_task.id),
str(fxt_coco_file),
"--format",
"COCO 1.0",
)
def test_can_create_from_backup(self, fxt_new_task: Task, fxt_backup_file: Path):
stdout = self.run_cli("task", "create-from-backup", str(fxt_backup_file))
task_id = int(stdout.rstrip("\n"))
assert task_id
assert task_id != fxt_new_task.id
assert self.client.tasks.retrieve(task_id).size == fxt_new_task.size
def test_auto_annotate_with_module(self, fxt_new_task: Task):
annotations = fxt_new_task.get_annotations()
assert not annotations.shapes
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-module={__package__}.example_function",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes
def test_auto_annotate_with_file(self, fxt_new_task: Task):
annotations = fxt_new_task.get_annotations()
assert not annotations.shapes
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-file={Path(__file__).with_name('example_function.py')}",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes
def test_auto_annotate_with_parameters(self, fxt_new_task: Task):
annotations = fxt_new_task.get_annotations()
assert not annotations.shapes
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-module={__package__}.example_parameterized_function",
"-ps=str:string",
"-pi=int:123",
"-pf=float:5.5",
"-pb=bool:false",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes
def test_auto_annotate_with_threshold(self, fxt_new_task: Task):
annotations = fxt_new_task.get_annotations()
assert not annotations.shapes
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-module={__package__}.conf_threshold_function",
"--conf-threshold=0.75",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes[0].points[0] == 0.75 # python:S1244 NOSONAR
def test_auto_annotate_with_cmtp(self, fxt_new_task: Task):
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-module={__package__}.cmtp_function",
"--clear-existing",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes[0].type.value == "mask"
self.run_cli(
"task",
"auto-annotate",
str(fxt_new_task.id),
f"--function-module={__package__}.cmtp_function",
"--clear-existing",
"--conv-mask-to-poly",
)
annotations = fxt_new_task.get_annotations()
assert annotations.shapes[0].type.value == "polygon"
def test_legacy_alias(self, caplog):
# All legacy aliases are implemented the same way;
# no need to test every single one.
self.run_cli("ls")
assert "deprecated" in caplog.text
assert "task ls" in caplog.text
+94
View File
@@ -0,0 +1,94 @@
# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import io
import unittest
from pathlib import Path
from typing import Any
import pytest
from cvat_sdk import make_client
from shared.utils.config import BASE_URL, USER_PASS
from shared.utils.helpers import generate_image_file
def run_cli(
test: unittest.TestCase | Any,
*args: str,
expected_code: int = 0,
) -> None:
from cvat_cli.__main__ import main
if isinstance(test, unittest.TestCase):
# Unittest
test.assertEqual(expected_code, main(args), str(args))
else:
# Pytest case
assert expected_code == main(args)
def generate_images(dst_dir: Path, count: int) -> list[Path]:
filenames = []
dst_dir.mkdir(parents=True, exist_ok=True)
for i in range(count):
filename = dst_dir / f"img_{i}.jpg"
filename.write_bytes(generate_image_file(filename.name).getvalue())
filenames.append(filename)
return filenames
class TestCliBase:
@pytest.fixture(autouse=True)
def setup(
self,
restore_db_per_function, # force fixture call order to allow DB setup
restore_redis_inmem_per_function,
restore_redis_ondisk_per_function,
fxt_stdout: io.StringIO,
tmp_path: Path,
admin_user: str,
):
self.tmp_path = tmp_path
self.stdout = fxt_stdout
self.host, self.port = BASE_URL.rsplit(":", maxsplit=1)
self.user = admin_user
self.password = USER_PASS
self.client = make_client(
host=self.host, port=self.port, credentials=(self.user, self.password)
)
self.client.config.status_check_period = 0.01
yield
def run_cli(
self,
cmd: str,
*args: str,
expected_code: int = 0,
organization: str | None = None,
authenticate: bool = True,
) -> str:
common_args = [
f"--server-host={self.host}",
f"--server-port={self.port}",
]
if authenticate:
common_args += [
f"--auth={self.user}:{self.password}",
]
if organization is not None:
common_args.append(f"--organization={organization}")
run_cli(
self,
*common_args,
cmd,
*args,
expected_code=expected_code,
)
return self.stdout.getvalue()