cvat-cli 2.64.0__tar.gz → 2.66.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/PKG-INFO +6 -2
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/README.md +4 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/pyproject.toml +1 -1
- cvat_cli-2.66.0/src/cvat_cli/_internal/command_base.py +302 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/commands_projects.py +33 -1
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/commands_tasks.py +18 -145
- cvat_cli-2.66.0/src/cvat_cli/version.py +1 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/PKG-INFO +6 -2
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/requires.txt +1 -1
- cvat_cli-2.64.0/src/cvat_cli/_internal/command_base.py +0 -124
- cvat_cli-2.64.0/src/cvat_cli/version.py +0 -1
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/setup.cfg +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/__init__.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/__main__.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/__init__.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/agent.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/commands_all.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/commands_functions.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/common.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/parsers.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli/_internal/utils.py +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/SOURCES.txt +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/dependency_links.txt +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/entry_points.txt +0 -0
- {cvat_cli-2.64.0 → cvat_cli-2.66.0}/src/cvat_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cvat-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.66.0
|
|
4
4
|
Summary: Command-line client for CVAT
|
|
5
5
|
Author-email: "CVAT.ai Corporation" <support@cvat.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: cvat-sdk==2.
|
|
12
|
+
Requires-Dist: cvat-sdk==2.66.0
|
|
13
13
|
Requires-Dist: attrs>=24.2.0
|
|
14
14
|
Requires-Dist: Pillow>=10.3.0
|
|
15
15
|
|
|
@@ -22,8 +22,12 @@ comprehensive CVAT administration tool in the future.
|
|
|
22
22
|
The following subcommands are supported:
|
|
23
23
|
|
|
24
24
|
- Projects:
|
|
25
|
+
- `backup` - back up a project
|
|
25
26
|
- `create` - create a new project
|
|
27
|
+
- `create-from-backup` - create a project from a backup file
|
|
26
28
|
- `delete` - delete projects
|
|
29
|
+
- `export-dataset` - export a project as a dataset
|
|
30
|
+
- `import-dataset` - import annotations into a project from a dataset
|
|
27
31
|
- `ls` - list all projects
|
|
28
32
|
|
|
29
33
|
- Tasks:
|
|
@@ -7,8 +7,12 @@ comprehensive CVAT administration tool in the future.
|
|
|
7
7
|
The following subcommands are supported:
|
|
8
8
|
|
|
9
9
|
- Projects:
|
|
10
|
+
- `backup` - back up a project
|
|
10
11
|
- `create` - create a new project
|
|
12
|
+
- `create-from-backup` - create a project from a backup file
|
|
11
13
|
- `delete` - delete projects
|
|
14
|
+
- `export-dataset` - export a project as a dataset
|
|
15
|
+
- `import-dataset` - import annotations into a project from a dataset
|
|
12
16
|
- `ls` - list all projects
|
|
13
17
|
|
|
14
18
|
- Tasks:
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Copyright (C) CVAT.ai Corporation
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import textwrap
|
|
9
|
+
import types
|
|
10
|
+
from abc import ABCMeta, abstractmethod
|
|
11
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
12
|
+
from typing import Protocol
|
|
13
|
+
|
|
14
|
+
from attr.converters import to_bool
|
|
15
|
+
from cvat_sdk import Client
|
|
16
|
+
from cvat_sdk.core.helpers import DeferredTqdmProgressReporter
|
|
17
|
+
from cvat_sdk.core.proxies.types import Location
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Command(Protocol):
|
|
21
|
+
@property
|
|
22
|
+
def description(self) -> str: ...
|
|
23
|
+
|
|
24
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None: ...
|
|
25
|
+
|
|
26
|
+
# The exact parameters accepted by `execute` vary between commands,
|
|
27
|
+
# so we're forced to declare it like this instead of as a method.
|
|
28
|
+
@property
|
|
29
|
+
def execute(self) -> Callable[..., None]: ...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CommandGroup:
|
|
33
|
+
def __init__(self, *, description: str) -> None:
|
|
34
|
+
self._commands: dict[str, Command] = {}
|
|
35
|
+
self.description = description
|
|
36
|
+
|
|
37
|
+
def command_class(self, name: str):
|
|
38
|
+
def decorator(cls: type):
|
|
39
|
+
self._commands[name] = cls()
|
|
40
|
+
return cls
|
|
41
|
+
|
|
42
|
+
return decorator
|
|
43
|
+
|
|
44
|
+
def add_command(self, name: str, command: Command) -> None:
|
|
45
|
+
self._commands[name] = command
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def commands(self) -> Mapping[str, Command]:
|
|
49
|
+
return types.MappingProxyType(self._commands)
|
|
50
|
+
|
|
51
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
52
|
+
subparsers = parser.add_subparsers(required=True)
|
|
53
|
+
|
|
54
|
+
for name, command in self._commands.items():
|
|
55
|
+
subparser = subparsers.add_parser(name, description=command.description)
|
|
56
|
+
subparser.set_defaults(_executor=command.execute)
|
|
57
|
+
command.configure_parser(subparser)
|
|
58
|
+
|
|
59
|
+
def execute(self) -> None:
|
|
60
|
+
# It should be impossible for a command group to be executed,
|
|
61
|
+
# because configure_parser requires that a subcommand is specified.
|
|
62
|
+
assert False, "unreachable code"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DeprecatedAlias:
|
|
66
|
+
def __init__(self, command: Command, replacement: str) -> None:
|
|
67
|
+
self._command = command
|
|
68
|
+
self._replacement = replacement
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def description(self) -> str:
|
|
72
|
+
return textwrap.dedent(f"""\
|
|
73
|
+
{self._command.description}
|
|
74
|
+
(Deprecated; use "{self._replacement}" instead.)
|
|
75
|
+
""")
|
|
76
|
+
|
|
77
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
78
|
+
self._command.configure_parser(parser)
|
|
79
|
+
|
|
80
|
+
def execute(self, client: Client, **kwargs) -> None:
|
|
81
|
+
client.logger.warning('This command is deprecated. Use "%s" instead.', self._replacement)
|
|
82
|
+
self._command.execute(client, **kwargs)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class GenericCommand(metaclass=ABCMeta):
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def repo(self, client: Client): ...
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def resource_type_str(self) -> str: ...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class GenericListCommand(GenericCommand):
|
|
95
|
+
@property
|
|
96
|
+
def description(self) -> str:
|
|
97
|
+
return f"List all CVAT {self.resource_type_str}s in either basic or JSON format."
|
|
98
|
+
|
|
99
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--json",
|
|
102
|
+
dest="use_json_output",
|
|
103
|
+
default=False,
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="output JSON data",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def execute(self, client: Client, *, use_json_output: bool = False):
|
|
109
|
+
results = self.repo(client).list(return_json=use_json_output)
|
|
110
|
+
if use_json_output:
|
|
111
|
+
print(json.dumps(json.loads(results), indent=2))
|
|
112
|
+
else:
|
|
113
|
+
for r in results:
|
|
114
|
+
print(r.id)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class GenericDeleteCommand(GenericCommand):
|
|
118
|
+
@property
|
|
119
|
+
def description(self):
|
|
120
|
+
return f"Delete a list of {self.resource_type_str}s, ignoring those which don't exist."
|
|
121
|
+
|
|
122
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
123
|
+
parser.add_argument(
|
|
124
|
+
"ids", type=int, help=f"list of {self.resource_type_str} IDs", nargs="+"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def execute(self, client: Client, *, ids: Sequence[int]) -> None:
|
|
128
|
+
self.repo(client).remove_by_ids(ids)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class GenericDownloadBackupCommand(GenericCommand):
|
|
132
|
+
@property
|
|
133
|
+
def description(self) -> str:
|
|
134
|
+
return f"Download a {self.resource_type_str} backup."
|
|
135
|
+
|
|
136
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"resource_id",
|
|
139
|
+
metavar=f"{self.resource_type_str}_id",
|
|
140
|
+
type=int,
|
|
141
|
+
help=f"{self.resource_type_str} ID",
|
|
142
|
+
)
|
|
143
|
+
parser.add_argument(
|
|
144
|
+
"filename",
|
|
145
|
+
type=str,
|
|
146
|
+
nargs="?",
|
|
147
|
+
default="",
|
|
148
|
+
help="output file or directory (default: current directory)",
|
|
149
|
+
)
|
|
150
|
+
parser.add_argument(
|
|
151
|
+
"--completion_verification_period",
|
|
152
|
+
dest="status_check_period",
|
|
153
|
+
default=2,
|
|
154
|
+
type=float,
|
|
155
|
+
help="time interval between checks if archive building has been finished, in seconds",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def execute(
|
|
159
|
+
self, client: Client, *, resource_id: int, filename: str, status_check_period: float
|
|
160
|
+
) -> None:
|
|
161
|
+
if not filename:
|
|
162
|
+
filename = os.getcwd()
|
|
163
|
+
|
|
164
|
+
if filename.endswith((os.sep, os.altsep or os.sep)):
|
|
165
|
+
os.makedirs(filename, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
self.repo(client).retrieve(obj_id=resource_id).download_backup(
|
|
168
|
+
filename=filename,
|
|
169
|
+
status_check_period=status_check_period,
|
|
170
|
+
pbar=DeferredTqdmProgressReporter(),
|
|
171
|
+
location=Location.LOCAL,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class GenericCreateFromBackupCommand(GenericCommand):
|
|
176
|
+
@property
|
|
177
|
+
def description(self) -> str:
|
|
178
|
+
return f"Create a {self.resource_type_str} from a backup file."
|
|
179
|
+
|
|
180
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
181
|
+
parser.add_argument("filename", type=str, help="upload file")
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
"--completion_verification_period",
|
|
184
|
+
dest="status_check_period",
|
|
185
|
+
default=2,
|
|
186
|
+
type=float,
|
|
187
|
+
help="time interval between checks if archive processing was finished, in seconds",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def execute(self, client: Client, *, filename: str, status_check_period: float) -> None:
|
|
191
|
+
resource = self.repo(client).create_from_backup(
|
|
192
|
+
filename=filename,
|
|
193
|
+
status_check_period=status_check_period,
|
|
194
|
+
pbar=DeferredTqdmProgressReporter(),
|
|
195
|
+
)
|
|
196
|
+
print(resource.id)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class GenericExportDatasetCommand(GenericCommand):
|
|
200
|
+
@property
|
|
201
|
+
def description(self) -> str:
|
|
202
|
+
return textwrap.dedent(f"""\
|
|
203
|
+
Export a {self.resource_type_str} as a dataset in the specified format
|
|
204
|
+
(e.g. 'YOLO 1.1').
|
|
205
|
+
""")
|
|
206
|
+
|
|
207
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
208
|
+
parser.add_argument(
|
|
209
|
+
"resource_id",
|
|
210
|
+
metavar=f"{self.resource_type_str}_id",
|
|
211
|
+
type=int,
|
|
212
|
+
help=f"{self.resource_type_str} ID",
|
|
213
|
+
)
|
|
214
|
+
parser.add_argument(
|
|
215
|
+
"filename",
|
|
216
|
+
type=str,
|
|
217
|
+
nargs="?",
|
|
218
|
+
default="",
|
|
219
|
+
help="output file or directory (default: current directory)",
|
|
220
|
+
)
|
|
221
|
+
parser.add_argument(
|
|
222
|
+
"--format",
|
|
223
|
+
dest="fileformat",
|
|
224
|
+
type=str,
|
|
225
|
+
default="CVAT for images 1.1",
|
|
226
|
+
help="annotation format (default: %(default)s)",
|
|
227
|
+
)
|
|
228
|
+
parser.add_argument(
|
|
229
|
+
"--completion_verification_period",
|
|
230
|
+
dest="status_check_period",
|
|
231
|
+
default=2,
|
|
232
|
+
type=float,
|
|
233
|
+
help="number of seconds to wait until checking if dataset building finished",
|
|
234
|
+
)
|
|
235
|
+
parser.add_argument(
|
|
236
|
+
"--with-images",
|
|
237
|
+
type=to_bool,
|
|
238
|
+
default=False,
|
|
239
|
+
dest="include_images",
|
|
240
|
+
help="Whether to include images or not (default: %(default)s)",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def execute(
|
|
244
|
+
self,
|
|
245
|
+
client: Client,
|
|
246
|
+
*,
|
|
247
|
+
fileformat: str,
|
|
248
|
+
filename: str,
|
|
249
|
+
status_check_period: int,
|
|
250
|
+
include_images: bool,
|
|
251
|
+
resource_id: int,
|
|
252
|
+
) -> None:
|
|
253
|
+
if not filename:
|
|
254
|
+
filename = os.getcwd()
|
|
255
|
+
|
|
256
|
+
if filename.endswith((os.sep, os.altsep or os.sep)):
|
|
257
|
+
os.makedirs(filename, exist_ok=True)
|
|
258
|
+
|
|
259
|
+
self.repo(client).retrieve(obj_id=resource_id).export_dataset(
|
|
260
|
+
format_name=fileformat,
|
|
261
|
+
filename=filename,
|
|
262
|
+
pbar=DeferredTqdmProgressReporter(),
|
|
263
|
+
status_check_period=status_check_period,
|
|
264
|
+
include_images=include_images,
|
|
265
|
+
location=Location.LOCAL,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class GenericImportDatasetCommand(GenericCommand):
|
|
270
|
+
import_method_name = "import_dataset"
|
|
271
|
+
|
|
272
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
273
|
+
parser.add_argument(
|
|
274
|
+
"resource_id",
|
|
275
|
+
metavar=f"{self.resource_type_str}_id",
|
|
276
|
+
type=int,
|
|
277
|
+
help=f"{self.resource_type_str} ID",
|
|
278
|
+
)
|
|
279
|
+
parser.add_argument("filename", type=str, help="upload file")
|
|
280
|
+
parser.add_argument(
|
|
281
|
+
"--format",
|
|
282
|
+
dest="fileformat",
|
|
283
|
+
type=str,
|
|
284
|
+
default="CVAT 1.1",
|
|
285
|
+
help="annotation format (default: %(default)s)",
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def execute(
|
|
289
|
+
self,
|
|
290
|
+
client: Client,
|
|
291
|
+
*,
|
|
292
|
+
fileformat: str,
|
|
293
|
+
filename: str,
|
|
294
|
+
resource_id: int,
|
|
295
|
+
) -> None:
|
|
296
|
+
resource = self.repo(client).retrieve(obj_id=resource_id)
|
|
297
|
+
import_dataset = getattr(resource, self.import_method_name)
|
|
298
|
+
import_dataset(
|
|
299
|
+
format_name=fileformat,
|
|
300
|
+
filename=filename,
|
|
301
|
+
pbar=DeferredTqdmProgressReporter(),
|
|
302
|
+
)
|
|
@@ -7,7 +7,16 @@ import textwrap
|
|
|
7
7
|
|
|
8
8
|
from cvat_sdk import Client, models
|
|
9
9
|
|
|
10
|
-
from .command_base import
|
|
10
|
+
from .command_base import (
|
|
11
|
+
CommandGroup,
|
|
12
|
+
GenericCommand,
|
|
13
|
+
GenericCreateFromBackupCommand,
|
|
14
|
+
GenericDeleteCommand,
|
|
15
|
+
GenericDownloadBackupCommand,
|
|
16
|
+
GenericExportDatasetCommand,
|
|
17
|
+
GenericImportDatasetCommand,
|
|
18
|
+
GenericListCommand,
|
|
19
|
+
)
|
|
11
20
|
from .parsers import parse_label_arg
|
|
12
21
|
|
|
13
22
|
COMMANDS = CommandGroup(description="Perform operations on CVAT projects.")
|
|
@@ -87,3 +96,26 @@ class ProjectCreate:
|
|
|
87
96
|
@COMMANDS.command_class("delete")
|
|
88
97
|
class ProjectDelete(GenericDeleteCommand, GenericProjectCommand):
|
|
89
98
|
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@COMMANDS.command_class("backup")
|
|
102
|
+
class ProjectBackup(GenericDownloadBackupCommand, GenericProjectCommand):
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@COMMANDS.command_class("create-from-backup")
|
|
107
|
+
class ProjectCreateFromBackup(GenericCreateFromBackupCommand, GenericProjectCommand):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@COMMANDS.command_class("export-dataset")
|
|
112
|
+
class ProjectExportDataset(GenericExportDatasetCommand, GenericProjectCommand):
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@COMMANDS.command_class("import-dataset")
|
|
117
|
+
class ProjectImportDataset(GenericImportDatasetCommand, GenericProjectCommand):
|
|
118
|
+
description = textwrap.dedent("""\
|
|
119
|
+
Create tasks in a project from a dataset in the specified format
|
|
120
|
+
(e.g. 'YOLO 1.1'), including images and annotations.
|
|
121
|
+
""")
|
|
@@ -5,18 +5,24 @@
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
|
-
import os
|
|
9
8
|
import textwrap
|
|
10
9
|
from collections.abc import Sequence
|
|
11
10
|
|
|
12
11
|
import cvat_sdk.auto_annotation as cvataa
|
|
13
|
-
from attr.converters import to_bool
|
|
14
12
|
from cvat_sdk import Client, models
|
|
15
13
|
from cvat_sdk.core.helpers import DeferredTqdmProgressReporter
|
|
16
14
|
from cvat_sdk.core.proxies.tasks import ResourceType
|
|
17
|
-
from cvat_sdk.core.proxies.types import Location
|
|
18
15
|
|
|
19
|
-
from .command_base import
|
|
16
|
+
from .command_base import (
|
|
17
|
+
CommandGroup,
|
|
18
|
+
GenericCommand,
|
|
19
|
+
GenericCreateFromBackupCommand,
|
|
20
|
+
GenericDeleteCommand,
|
|
21
|
+
GenericDownloadBackupCommand,
|
|
22
|
+
GenericExportDatasetCommand,
|
|
23
|
+
GenericImportDatasetCommand,
|
|
24
|
+
GenericListCommand,
|
|
25
|
+
)
|
|
20
26
|
from .common import FunctionLoader, configure_function_implementation_arguments
|
|
21
27
|
from .parsers import parse_label_arg, parse_resource_type, parse_threshold
|
|
22
28
|
|
|
@@ -254,160 +260,27 @@ class TaskFrames:
|
|
|
254
260
|
|
|
255
261
|
|
|
256
262
|
@COMMANDS.command_class("export-dataset")
|
|
257
|
-
class TaskExportDataset:
|
|
258
|
-
|
|
259
|
-
Export a task as a dataset in the specified format (e.g. 'YOLO 1.1').
|
|
260
|
-
""")
|
|
261
|
-
|
|
262
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
263
|
-
parser.add_argument("task_id", type=int, help="task ID")
|
|
264
|
-
parser.add_argument(
|
|
265
|
-
"filename",
|
|
266
|
-
type=str,
|
|
267
|
-
nargs="?",
|
|
268
|
-
default="",
|
|
269
|
-
help="output file or directory (default: current directory)",
|
|
270
|
-
)
|
|
271
|
-
parser.add_argument(
|
|
272
|
-
"--format",
|
|
273
|
-
dest="fileformat",
|
|
274
|
-
type=str,
|
|
275
|
-
default="CVAT for images 1.1",
|
|
276
|
-
help="annotation format (default: %(default)s)",
|
|
277
|
-
)
|
|
278
|
-
parser.add_argument(
|
|
279
|
-
"--completion_verification_period",
|
|
280
|
-
dest="status_check_period",
|
|
281
|
-
default=2,
|
|
282
|
-
type=float,
|
|
283
|
-
help="number of seconds to wait until checking if dataset building finished",
|
|
284
|
-
)
|
|
285
|
-
parser.add_argument(
|
|
286
|
-
"--with-images",
|
|
287
|
-
type=to_bool,
|
|
288
|
-
default=False,
|
|
289
|
-
dest="include_images",
|
|
290
|
-
help="Whether to include images or not (default: %(default)s)",
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
def execute(
|
|
294
|
-
self,
|
|
295
|
-
client: Client,
|
|
296
|
-
*,
|
|
297
|
-
task_id: int,
|
|
298
|
-
fileformat: str,
|
|
299
|
-
filename: str,
|
|
300
|
-
status_check_period: int,
|
|
301
|
-
include_images: bool,
|
|
302
|
-
) -> None:
|
|
303
|
-
if not filename:
|
|
304
|
-
filename = os.getcwd()
|
|
305
|
-
|
|
306
|
-
if filename.endswith((os.sep, os.altsep or os.sep)):
|
|
307
|
-
os.makedirs(filename, exist_ok=True)
|
|
308
|
-
|
|
309
|
-
client.tasks.retrieve(obj_id=task_id).export_dataset(
|
|
310
|
-
format_name=fileformat,
|
|
311
|
-
filename=filename,
|
|
312
|
-
pbar=DeferredTqdmProgressReporter(),
|
|
313
|
-
status_check_period=status_check_period,
|
|
314
|
-
include_images=include_images,
|
|
315
|
-
location=Location.LOCAL,
|
|
316
|
-
)
|
|
263
|
+
class TaskExportDataset(GenericExportDatasetCommand, GenericTaskCommand):
|
|
264
|
+
pass
|
|
317
265
|
|
|
318
266
|
|
|
319
267
|
@COMMANDS.command_class("import-dataset")
|
|
320
|
-
class TaskImportDataset:
|
|
268
|
+
class TaskImportDataset(GenericImportDatasetCommand, GenericTaskCommand):
|
|
321
269
|
description = textwrap.dedent("""\
|
|
322
270
|
Import annotations into a task from a dataset in the specified format
|
|
323
271
|
(e.g. 'YOLO 1.1').
|
|
324
272
|
""")
|
|
325
|
-
|
|
326
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
327
|
-
parser.add_argument("task_id", type=int, help="task ID")
|
|
328
|
-
parser.add_argument("filename", type=str, help="upload file")
|
|
329
|
-
parser.add_argument(
|
|
330
|
-
"--format",
|
|
331
|
-
dest="fileformat",
|
|
332
|
-
type=str,
|
|
333
|
-
default="CVAT 1.1",
|
|
334
|
-
help="annotation format (default: %(default)s)",
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
def execute(
|
|
338
|
-
self,
|
|
339
|
-
client: Client,
|
|
340
|
-
*,
|
|
341
|
-
task_id: int,
|
|
342
|
-
fileformat: str,
|
|
343
|
-
filename: str,
|
|
344
|
-
) -> None:
|
|
345
|
-
client.tasks.retrieve(obj_id=task_id).import_annotations(
|
|
346
|
-
format_name=fileformat,
|
|
347
|
-
filename=filename,
|
|
348
|
-
pbar=DeferredTqdmProgressReporter(),
|
|
349
|
-
)
|
|
273
|
+
import_method_name = "import_annotations"
|
|
350
274
|
|
|
351
275
|
|
|
352
276
|
@COMMANDS.command_class("backup")
|
|
353
|
-
class TaskBackup:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
357
|
-
parser.add_argument("task_id", type=int, help="task ID")
|
|
358
|
-
parser.add_argument(
|
|
359
|
-
"filename",
|
|
360
|
-
type=str,
|
|
361
|
-
nargs="?",
|
|
362
|
-
default="",
|
|
363
|
-
help="output file or directory (default: current directory)",
|
|
364
|
-
)
|
|
365
|
-
parser.add_argument(
|
|
366
|
-
"--completion_verification_period",
|
|
367
|
-
dest="status_check_period",
|
|
368
|
-
default=2,
|
|
369
|
-
type=float,
|
|
370
|
-
help="time interval between checks if archive building has been finished, in seconds",
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
def execute(
|
|
374
|
-
self, client: Client, *, task_id: int, filename: str, status_check_period: int
|
|
375
|
-
) -> None:
|
|
376
|
-
if not filename:
|
|
377
|
-
filename = os.getcwd()
|
|
378
|
-
|
|
379
|
-
if filename.endswith((os.sep, os.altsep or os.sep)):
|
|
380
|
-
os.makedirs(filename, exist_ok=True)
|
|
381
|
-
|
|
382
|
-
client.tasks.retrieve(obj_id=task_id).download_backup(
|
|
383
|
-
filename=filename,
|
|
384
|
-
status_check_period=status_check_period,
|
|
385
|
-
pbar=DeferredTqdmProgressReporter(),
|
|
386
|
-
location=Location.LOCAL,
|
|
387
|
-
)
|
|
277
|
+
class TaskBackup(GenericDownloadBackupCommand, GenericTaskCommand):
|
|
278
|
+
pass
|
|
388
279
|
|
|
389
280
|
|
|
390
281
|
@COMMANDS.command_class("create-from-backup")
|
|
391
|
-
class TaskCreateFromBackup:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
395
|
-
parser.add_argument("filename", type=str, help="upload file")
|
|
396
|
-
parser.add_argument(
|
|
397
|
-
"--completion_verification_period",
|
|
398
|
-
dest="status_check_period",
|
|
399
|
-
default=2,
|
|
400
|
-
type=float,
|
|
401
|
-
help="time interval between checks if archive processing was finished, in seconds",
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
def execute(self, client: Client, *, filename: str, status_check_period: int) -> None:
|
|
405
|
-
task = client.tasks.create_from_backup(
|
|
406
|
-
filename=filename,
|
|
407
|
-
status_check_period=status_check_period,
|
|
408
|
-
pbar=DeferredTqdmProgressReporter(),
|
|
409
|
-
)
|
|
410
|
-
print(task.id)
|
|
282
|
+
class TaskCreateFromBackup(GenericCreateFromBackupCommand, GenericTaskCommand):
|
|
283
|
+
pass
|
|
411
284
|
|
|
412
285
|
|
|
413
286
|
@COMMANDS.command_class("auto-annotate")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "2.66.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cvat-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.66.0
|
|
4
4
|
Summary: Command-line client for CVAT
|
|
5
5
|
Author-email: "CVAT.ai Corporation" <support@cvat.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: cvat-sdk==2.
|
|
12
|
+
Requires-Dist: cvat-sdk==2.66.0
|
|
13
13
|
Requires-Dist: attrs>=24.2.0
|
|
14
14
|
Requires-Dist: Pillow>=10.3.0
|
|
15
15
|
|
|
@@ -22,8 +22,12 @@ comprehensive CVAT administration tool in the future.
|
|
|
22
22
|
The following subcommands are supported:
|
|
23
23
|
|
|
24
24
|
- Projects:
|
|
25
|
+
- `backup` - back up a project
|
|
25
26
|
- `create` - create a new project
|
|
27
|
+
- `create-from-backup` - create a project from a backup file
|
|
26
28
|
- `delete` - delete projects
|
|
29
|
+
- `export-dataset` - export a project as a dataset
|
|
30
|
+
- `import-dataset` - import annotations into a project from a dataset
|
|
27
31
|
- `ls` - list all projects
|
|
28
32
|
|
|
29
33
|
- Tasks:
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# Copyright (C) CVAT.ai Corporation
|
|
2
|
-
#
|
|
3
|
-
# SPDX-License-Identifier: MIT
|
|
4
|
-
|
|
5
|
-
import argparse
|
|
6
|
-
import json
|
|
7
|
-
import textwrap
|
|
8
|
-
import types
|
|
9
|
-
from abc import ABCMeta, abstractmethod
|
|
10
|
-
from collections.abc import Callable, Mapping, Sequence
|
|
11
|
-
from typing import Protocol
|
|
12
|
-
|
|
13
|
-
from cvat_sdk import Client
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Command(Protocol):
|
|
17
|
-
@property
|
|
18
|
-
def description(self) -> str: ...
|
|
19
|
-
|
|
20
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None: ...
|
|
21
|
-
|
|
22
|
-
# The exact parameters accepted by `execute` vary between commands,
|
|
23
|
-
# so we're forced to declare it like this instead of as a method.
|
|
24
|
-
@property
|
|
25
|
-
def execute(self) -> Callable[..., None]: ...
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class CommandGroup:
|
|
29
|
-
def __init__(self, *, description: str) -> None:
|
|
30
|
-
self._commands: dict[str, Command] = {}
|
|
31
|
-
self.description = description
|
|
32
|
-
|
|
33
|
-
def command_class(self, name: str):
|
|
34
|
-
def decorator(cls: type):
|
|
35
|
-
self._commands[name] = cls()
|
|
36
|
-
return cls
|
|
37
|
-
|
|
38
|
-
return decorator
|
|
39
|
-
|
|
40
|
-
def add_command(self, name: str, command: Command) -> None:
|
|
41
|
-
self._commands[name] = command
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def commands(self) -> Mapping[str, Command]:
|
|
45
|
-
return types.MappingProxyType(self._commands)
|
|
46
|
-
|
|
47
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
48
|
-
subparsers = parser.add_subparsers(required=True)
|
|
49
|
-
|
|
50
|
-
for name, command in self._commands.items():
|
|
51
|
-
subparser = subparsers.add_parser(name, description=command.description)
|
|
52
|
-
subparser.set_defaults(_executor=command.execute)
|
|
53
|
-
command.configure_parser(subparser)
|
|
54
|
-
|
|
55
|
-
def execute(self) -> None:
|
|
56
|
-
# It should be impossible for a command group to be executed,
|
|
57
|
-
# because configure_parser requires that a subcommand is specified.
|
|
58
|
-
assert False, "unreachable code"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class DeprecatedAlias:
|
|
62
|
-
def __init__(self, command: Command, replacement: str) -> None:
|
|
63
|
-
self._command = command
|
|
64
|
-
self._replacement = replacement
|
|
65
|
-
|
|
66
|
-
@property
|
|
67
|
-
def description(self) -> str:
|
|
68
|
-
return textwrap.dedent(f"""\
|
|
69
|
-
{self._command.description}
|
|
70
|
-
(Deprecated; use "{self._replacement}" instead.)
|
|
71
|
-
""")
|
|
72
|
-
|
|
73
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
74
|
-
self._command.configure_parser(parser)
|
|
75
|
-
|
|
76
|
-
def execute(self, client: Client, **kwargs) -> None:
|
|
77
|
-
client.logger.warning('This command is deprecated. Use "%s" instead.', self._replacement)
|
|
78
|
-
self._command.execute(client, **kwargs)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class GenericCommand(metaclass=ABCMeta):
|
|
82
|
-
@abstractmethod
|
|
83
|
-
def repo(self, client: Client): ...
|
|
84
|
-
|
|
85
|
-
@property
|
|
86
|
-
@abstractmethod
|
|
87
|
-
def resource_type_str(self) -> str: ...
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class GenericListCommand(GenericCommand):
|
|
91
|
-
@property
|
|
92
|
-
def description(self) -> str:
|
|
93
|
-
return f"List all CVAT {self.resource_type_str}s in either basic or JSON format."
|
|
94
|
-
|
|
95
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
96
|
-
parser.add_argument(
|
|
97
|
-
"--json",
|
|
98
|
-
dest="use_json_output",
|
|
99
|
-
default=False,
|
|
100
|
-
action="store_true",
|
|
101
|
-
help="output JSON data",
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
def execute(self, client: Client, *, use_json_output: bool = False):
|
|
105
|
-
results = self.repo(client).list(return_json=use_json_output)
|
|
106
|
-
if use_json_output:
|
|
107
|
-
print(json.dumps(json.loads(results), indent=2))
|
|
108
|
-
else:
|
|
109
|
-
for r in results:
|
|
110
|
-
print(r.id)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
class GenericDeleteCommand(GenericCommand):
|
|
114
|
-
@property
|
|
115
|
-
def description(self):
|
|
116
|
-
return f"Delete a list of {self.resource_type_str}s, ignoring those which don't exist."
|
|
117
|
-
|
|
118
|
-
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
119
|
-
parser.add_argument(
|
|
120
|
-
"ids", type=int, help=f"list of {self.resource_type_str} IDs", nargs="+"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
def execute(self, client: Client, *, ids: Sequence[int]) -> None:
|
|
124
|
-
self.repo(client).remove_by_ids(ids)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "2.64.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|