cvat-cli 2.71.0__tar.gz → 2.74.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.71.0 → cvat_cli-2.74.0}/PKG-INFO +3 -2
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/pyproject.toml +2 -1
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/commands_all.py +2 -0
- cvat_cli-2.74.0/src/cvat_cli/_internal/commands_profile.py +173 -0
- cvat_cli-2.74.0/src/cvat_cli/_internal/utils.py +61 -0
- cvat_cli-2.74.0/src/cvat_cli/version.py +1 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli.egg-info/PKG-INFO +3 -2
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli.egg-info/SOURCES.txt +1 -0
- cvat_cli-2.74.0/src/cvat_cli.egg-info/requires.txt +4 -0
- cvat_cli-2.71.0/src/cvat_cli/_internal/utils.py +0 -9
- cvat_cli-2.71.0/src/cvat_cli/version.py +0 -1
- cvat_cli-2.71.0/src/cvat_cli.egg-info/requires.txt +0 -3
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/README.md +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/setup.cfg +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/__init__.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/__main__.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/__init__.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/agent.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/agent_driver.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/agent_driver_detection.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/agent_driver_interaction.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/agent_driver_tracking.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/command_base.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/commands_config.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/commands_functions.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/commands_projects.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/commands_tasks.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/common.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli/_internal/parsers.py +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli.egg-info/dependency_links.txt +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.0}/src/cvat_cli.egg-info/entry_points.txt +0 -0
- {cvat_cli-2.71.0 → cvat_cli-2.74.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.74.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,8 +9,9 @@ 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.74.0
|
|
13
13
|
Requires-Dist: attrs>=24.2.0
|
|
14
|
+
Requires-Dist: json-with-comments==1.2.10
|
|
14
15
|
Requires-Dist: Pillow>=10.3.0
|
|
15
16
|
|
|
16
17
|
# Command-line client for CVAT
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
from .command_base import CommandGroup, DeprecatedAlias
|
|
6
6
|
from .commands_config import COMMANDS as COMMANDS_CONFIG
|
|
7
7
|
from .commands_functions import COMMANDS as COMMANDS_FUNCTIONS
|
|
8
|
+
from .commands_profile import COMMANDS as COMMANDS_PROFILE
|
|
8
9
|
from .commands_projects import COMMANDS as COMMANDS_PROJECTS
|
|
9
10
|
from .commands_tasks import COMMANDS as COMMANDS_TASKS
|
|
10
11
|
|
|
@@ -12,6 +13,7 @@ COMMANDS = CommandGroup(description="Perform operations on CVAT resources.")
|
|
|
12
13
|
|
|
13
14
|
COMMANDS.add_command("config", COMMANDS_CONFIG)
|
|
14
15
|
COMMANDS.add_command("function", COMMANDS_FUNCTIONS)
|
|
16
|
+
COMMANDS.add_command("profile", COMMANDS_PROFILE)
|
|
15
17
|
COMMANDS.add_command("project", COMMANDS_PROJECTS)
|
|
16
18
|
COMMANDS.add_command("task", COMMANDS_TASKS)
|
|
17
19
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Copyright (C) CVAT.ai Corporation
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import getpass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from urllib.parse import urlsplit
|
|
11
|
+
|
|
12
|
+
from cvat_sdk.core.auth import DEFAULT_SERVER, AuthStore, ProfileEntry
|
|
13
|
+
from cvat_sdk.core.client import AccessTokenCredentials, Client, Config
|
|
14
|
+
from cvat_sdk.core.utils import normalize_server_url
|
|
15
|
+
|
|
16
|
+
from .command_base import CommandGroup
|
|
17
|
+
from .common import CriticalError
|
|
18
|
+
from .utils import fetch_current_access_token_name, get_current_time_iso, read_token_file
|
|
19
|
+
|
|
20
|
+
COMMANDS = CommandGroup(description="Manage saved CVAT authentication profiles.")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@COMMANDS.command_class("list")
|
|
24
|
+
class ProfileList:
|
|
25
|
+
needs_client = False
|
|
26
|
+
description = "List saved profiles, marking the default one."
|
|
27
|
+
|
|
28
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"--names-only", action="store_true", help="print profile names only (one per line)"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
34
|
+
store = AuthStore()
|
|
35
|
+
profiles = store.list_profiles()
|
|
36
|
+
default = store.get_default_profile()
|
|
37
|
+
default_name = default[0] if default is not None else None
|
|
38
|
+
|
|
39
|
+
for name in sorted(profiles):
|
|
40
|
+
if args.names_only:
|
|
41
|
+
print(name)
|
|
42
|
+
else:
|
|
43
|
+
marker = "(default)" if name == default_name else ""
|
|
44
|
+
print(f"{name}\t{profiles[name].server}\t{marker}".rstrip())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@COMMANDS.command_class("default")
|
|
48
|
+
class ProfileDefault:
|
|
49
|
+
needs_client = False
|
|
50
|
+
description = "Print, set, or unset the default profile."
|
|
51
|
+
|
|
52
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
53
|
+
parser.add_argument("name", nargs="?", default=None, help="profile to make default")
|
|
54
|
+
parser.add_argument("--unset", action="store_true", help="clear the default profile")
|
|
55
|
+
|
|
56
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
57
|
+
store = AuthStore()
|
|
58
|
+
|
|
59
|
+
if args.unset and args.name is not None:
|
|
60
|
+
raise CriticalError("Cannot combine a profile name with '--unset'.")
|
|
61
|
+
|
|
62
|
+
if args.unset:
|
|
63
|
+
store.clear_default_profile()
|
|
64
|
+
print("Default profile cleared.")
|
|
65
|
+
elif args.name is not None:
|
|
66
|
+
try:
|
|
67
|
+
store.set_default_profile(args.name)
|
|
68
|
+
except KeyError:
|
|
69
|
+
raise CriticalError(f"Unknown profile '{args.name}'. Run 'cvat-cli profile list'.")
|
|
70
|
+
print(f"Default profile is now '{args.name}'.")
|
|
71
|
+
else:
|
|
72
|
+
default = store.get_default_profile()
|
|
73
|
+
if default is None:
|
|
74
|
+
raise CriticalError("No default profile is set.")
|
|
75
|
+
print(default[0])
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@COMMANDS.command_class("delete")
|
|
79
|
+
class ProfileDelete:
|
|
80
|
+
needs_client = False
|
|
81
|
+
description = (
|
|
82
|
+
"Remove a saved profile. Does not revoke the server-side token. "
|
|
83
|
+
"If it was the default, the default becomes unset."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
87
|
+
parser.add_argument("name", help="profile to remove")
|
|
88
|
+
|
|
89
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
90
|
+
store = AuthStore()
|
|
91
|
+
try:
|
|
92
|
+
store.remove_profile(args.name)
|
|
93
|
+
except KeyError:
|
|
94
|
+
raise CriticalError(f"Unknown profile '{args.name}'. Run 'cvat-cli profile list'.")
|
|
95
|
+
print(f"Removed profile '{args.name}'.")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@COMMANDS.command_class("create")
|
|
99
|
+
class ProfileCreate:
|
|
100
|
+
needs_client = False
|
|
101
|
+
description = (
|
|
102
|
+
"Save a Personal Access Token (PAT) and the server info into a local profile. "
|
|
103
|
+
"A PAT must be created on the server manually first. "
|
|
104
|
+
"Read more: https://docs.cvat.ai/docs/api_sdk/access_tokens/"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
|
108
|
+
parser.add_argument("--name", default=None, help="profile name (unique)")
|
|
109
|
+
parser.add_argument("token", nargs="?", default=None, help="PAT (omit to be prompted)")
|
|
110
|
+
parser.add_argument("--set-default", action="store_true", help="mark as default profile")
|
|
111
|
+
parser.add_argument("--force", action="store_true", help="overwrite an existing profile")
|
|
112
|
+
parser.add_argument(
|
|
113
|
+
"--file",
|
|
114
|
+
type=Path,
|
|
115
|
+
default=None,
|
|
116
|
+
help=(
|
|
117
|
+
"read the PAT from a file; accepts plain tokens and JSONC envelopes. "
|
|
118
|
+
"'--server-host' overrides the envelope server"
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
123
|
+
store = AuthStore()
|
|
124
|
+
|
|
125
|
+
envelope_server = envelope_name = None
|
|
126
|
+
if args.file is not None:
|
|
127
|
+
if args.token is not None:
|
|
128
|
+
raise CriticalError("Cannot combine a PAT argument with '--file'.")
|
|
129
|
+
try:
|
|
130
|
+
token, envelope_server, envelope_name = read_token_file(args.file)
|
|
131
|
+
except (OSError, UnicodeError, ValueError) as e:
|
|
132
|
+
raise CriticalError(f"Cannot read token file '{args.file}': {e}") from e
|
|
133
|
+
elif args.token is not None:
|
|
134
|
+
token = args.token
|
|
135
|
+
else:
|
|
136
|
+
token = None
|
|
137
|
+
|
|
138
|
+
server = (
|
|
139
|
+
args.server_host or envelope_server or store.get_default_server() or DEFAULT_SERVER
|
|
140
|
+
).rstrip("/")
|
|
141
|
+
if args.server_port:
|
|
142
|
+
parsed_url = urlsplit(("https://" if "://" not in server else "") + server)
|
|
143
|
+
if parsed_url.port:
|
|
144
|
+
raise CriticalError(
|
|
145
|
+
"A server URL with a port and '--server-port' cannot be used together. "
|
|
146
|
+
"Please specify only one port."
|
|
147
|
+
)
|
|
148
|
+
server = f"{server}:{args.server_port}"
|
|
149
|
+
server = normalize_server_url(server)
|
|
150
|
+
|
|
151
|
+
if token is None:
|
|
152
|
+
token = getpass.getpass(f"Personal Access Token (PAT) for '{server}': ")
|
|
153
|
+
if not token:
|
|
154
|
+
raise CriticalError("A non-empty PAT is required.")
|
|
155
|
+
|
|
156
|
+
name = args.name or envelope_name
|
|
157
|
+
if name is None:
|
|
158
|
+
with Client(
|
|
159
|
+
url=server, config=Config(verify_ssl=not args.insecure), check_server_version=False
|
|
160
|
+
) as client:
|
|
161
|
+
client.login(AccessTokenCredentials(token))
|
|
162
|
+
name = fetch_current_access_token_name(client)
|
|
163
|
+
|
|
164
|
+
if store.get_profile(name) is not None and not args.force:
|
|
165
|
+
raise CriticalError(f"Profile '{name}' already exists. Pass '--force' to overwrite.")
|
|
166
|
+
|
|
167
|
+
store.put_profile(
|
|
168
|
+
name,
|
|
169
|
+
ProfileEntry(server=server, token=token, created_date=get_current_time_iso()),
|
|
170
|
+
set_default=args.set_default,
|
|
171
|
+
)
|
|
172
|
+
suffix = " (set as default)" if store.get_default_profile()[0] == name else ""
|
|
173
|
+
print(f"Saved profile '{name}' for {server}{suffix}.")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright (C) CVAT.ai Corporation
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from json import JSONDecodeError
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import jsonc
|
|
12
|
+
from cvat_sdk import Client
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def popattr(obj, name):
|
|
16
|
+
value = getattr(obj, name)
|
|
17
|
+
delattr(obj, name)
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_current_time_iso() -> str:
|
|
22
|
+
return datetime.now(timezone.utc).isoformat()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def fetch_current_access_token_name(client: Client) -> str:
|
|
26
|
+
access_token, _ = client.api_client.auth_api.retrieve_access_tokens_self()
|
|
27
|
+
return access_token.name
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def read_token_file(path: Path) -> tuple[str, str | None, str | None]:
|
|
31
|
+
"""Read a PAT from a plain-text token file or a JSONC envelope."""
|
|
32
|
+
if not path.is_file():
|
|
33
|
+
raise ValueError("path must be a regular file")
|
|
34
|
+
|
|
35
|
+
text = path.read_text(encoding="utf-8")
|
|
36
|
+
if not text:
|
|
37
|
+
# jsonc.loads raises IndexError for empty strings:
|
|
38
|
+
# https://github.com/n-takumasa/json-with-comments/issues/48
|
|
39
|
+
return "", None, None
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
doc = jsonc.loads(text)
|
|
43
|
+
except JSONDecodeError:
|
|
44
|
+
return text.strip(), None, None
|
|
45
|
+
|
|
46
|
+
if not isinstance(doc, dict):
|
|
47
|
+
return text.strip(), None, None
|
|
48
|
+
|
|
49
|
+
token = doc.get("token")
|
|
50
|
+
if not isinstance(token, str):
|
|
51
|
+
raise ValueError("JSON envelope field 'token' must be a string")
|
|
52
|
+
|
|
53
|
+
server = doc.get("server")
|
|
54
|
+
if server is not None and not isinstance(server, str):
|
|
55
|
+
raise ValueError("JSON envelope field 'server' must be a string")
|
|
56
|
+
|
|
57
|
+
name = doc.get("name")
|
|
58
|
+
if name is not None and not isinstance(name, str):
|
|
59
|
+
raise ValueError("JSON envelope field 'name' must be a string")
|
|
60
|
+
|
|
61
|
+
return token, server, name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "2.74.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cvat-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.74.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,8 +9,9 @@ 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.74.0
|
|
13
13
|
Requires-Dist: attrs>=24.2.0
|
|
14
|
+
Requires-Dist: json-with-comments==1.2.10
|
|
14
15
|
Requires-Dist: Pillow>=10.3.0
|
|
15
16
|
|
|
16
17
|
# Command-line client for CVAT
|
|
@@ -19,6 +19,7 @@ src/cvat_cli/_internal/command_base.py
|
|
|
19
19
|
src/cvat_cli/_internal/commands_all.py
|
|
20
20
|
src/cvat_cli/_internal/commands_config.py
|
|
21
21
|
src/cvat_cli/_internal/commands_functions.py
|
|
22
|
+
src/cvat_cli/_internal/commands_profile.py
|
|
22
23
|
src/cvat_cli/_internal/commands_projects.py
|
|
23
24
|
src/cvat_cli/_internal/commands_tasks.py
|
|
24
25
|
src/cvat_cli/_internal/common.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "2.71.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|