kleinkram 0.38.1.dev20241125112529__py3-none-any.whl → 0.38.1.dev20250113080249__py3-none-any.whl
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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- kleinkram/__init__.py +33 -2
- kleinkram/api/client.py +21 -16
- kleinkram/api/deser.py +165 -0
- kleinkram/api/file_transfer.py +13 -24
- kleinkram/api/pagination.py +56 -0
- kleinkram/api/query.py +111 -0
- kleinkram/api/routes.py +270 -97
- kleinkram/auth.py +21 -20
- kleinkram/cli/__init__.py +0 -0
- kleinkram/{commands/download.py → cli/_download.py} +18 -44
- kleinkram/cli/_endpoint.py +58 -0
- kleinkram/{commands/list.py → cli/_list.py} +25 -38
- kleinkram/cli/_mission.py +153 -0
- kleinkram/cli/_project.py +99 -0
- kleinkram/cli/_upload.py +84 -0
- kleinkram/cli/_verify.py +56 -0
- kleinkram/{app.py → cli/app.py} +50 -22
- kleinkram/cli/error_handling.py +44 -0
- kleinkram/config.py +141 -107
- kleinkram/core.py +251 -3
- kleinkram/errors.py +13 -45
- kleinkram/main.py +1 -1
- kleinkram/models.py +48 -149
- kleinkram/printing.py +325 -0
- kleinkram/py.typed +0 -0
- kleinkram/types.py +9 -0
- kleinkram/utils.py +82 -27
- kleinkram/wrappers.py +401 -0
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/METADATA +3 -3
- kleinkram-0.38.1.dev20250113080249.dist-info/RECORD +48 -0
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/WHEEL +1 -1
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/top_level.txt +1 -0
- testing/__init__.py +0 -0
- testing/backend_fixtures.py +69 -0
- tests/conftest.py +7 -0
- tests/test_config.py +115 -0
- tests/test_core.py +165 -0
- tests/test_end_to_end.py +29 -39
- tests/test_fixtures.py +29 -0
- tests/test_printing.py +62 -0
- tests/test_query.py +138 -0
- tests/test_utils.py +34 -24
- tests/test_wrappers.py +71 -0
- kleinkram/api/parsing.py +0 -86
- kleinkram/commands/__init__.py +0 -1
- kleinkram/commands/endpoint.py +0 -62
- kleinkram/commands/mission.py +0 -69
- kleinkram/commands/project.py +0 -24
- kleinkram/commands/upload.py +0 -164
- kleinkram/commands/verify.py +0 -142
- kleinkram/consts.py +0 -8
- kleinkram/enums.py +0 -10
- kleinkram/resources.py +0 -158
- kleinkram-0.38.1.dev20241125112529.dist-info/LICENSE +0 -674
- kleinkram-0.38.1.dev20241125112529.dist-info/RECORD +0 -37
- tests/test_resources.py +0 -137
- {kleinkram-0.38.1.dev20241125112529.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/entry_points.txt +0 -0
tests/test_query.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from kleinkram.api.query import InvalidMissionQuery
|
|
8
|
+
from kleinkram.api.query import InvalidProjectQuery
|
|
9
|
+
from kleinkram.api.query import MissionQuery
|
|
10
|
+
from kleinkram.api.query import ProjectQuery
|
|
11
|
+
from kleinkram.api.query import check_mission_query_is_creatable
|
|
12
|
+
from kleinkram.api.query import check_project_query_is_creatable
|
|
13
|
+
from kleinkram.api.query import mission_query_is_unique
|
|
14
|
+
from kleinkram.api.query import project_query_is_unique
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.parametrize(
|
|
18
|
+
"query, expected",
|
|
19
|
+
[
|
|
20
|
+
pytest.param(MissionQuery(), False, id="match all"),
|
|
21
|
+
pytest.param(MissionQuery(patterns=["*"]), False, id="mission name match all"),
|
|
22
|
+
pytest.param(
|
|
23
|
+
MissionQuery(patterns=["test"]),
|
|
24
|
+
False,
|
|
25
|
+
id="mission name without project",
|
|
26
|
+
),
|
|
27
|
+
pytest.param(
|
|
28
|
+
MissionQuery(patterns=["test"], project_query=ProjectQuery()),
|
|
29
|
+
False,
|
|
30
|
+
id="mission name with non-unique project",
|
|
31
|
+
),
|
|
32
|
+
pytest.param(
|
|
33
|
+
MissionQuery(
|
|
34
|
+
patterns=["test"],
|
|
35
|
+
project_query=ProjectQuery(ids=[uuid4()]),
|
|
36
|
+
),
|
|
37
|
+
True,
|
|
38
|
+
id="mission name with unique project",
|
|
39
|
+
),
|
|
40
|
+
pytest.param(
|
|
41
|
+
MissionQuery(ids=[uuid4()]),
|
|
42
|
+
True,
|
|
43
|
+
id="mission by id",
|
|
44
|
+
),
|
|
45
|
+
pytest.param(
|
|
46
|
+
MissionQuery(ids=[uuid4(), uuid4()]),
|
|
47
|
+
False,
|
|
48
|
+
id="multiple mission ids",
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
def test_mission_query_is_unique(query, expected):
|
|
53
|
+
assert mission_query_is_unique(query) == expected
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.mark.parametrize(
|
|
57
|
+
"query, expected",
|
|
58
|
+
[
|
|
59
|
+
pytest.param(ProjectQuery(), False, id="match all"),
|
|
60
|
+
pytest.param(ProjectQuery(patterns=["*"]), False, id="project name match all"),
|
|
61
|
+
pytest.param(
|
|
62
|
+
ProjectQuery(patterns=["test"]),
|
|
63
|
+
True,
|
|
64
|
+
id="project name",
|
|
65
|
+
),
|
|
66
|
+
pytest.param(
|
|
67
|
+
ProjectQuery(ids=[uuid4()]),
|
|
68
|
+
True,
|
|
69
|
+
id="project by id",
|
|
70
|
+
),
|
|
71
|
+
pytest.param(
|
|
72
|
+
ProjectQuery(ids=[uuid4(), uuid4()]),
|
|
73
|
+
False,
|
|
74
|
+
id="multiple project ids",
|
|
75
|
+
),
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
def test_project_query_is_unique(query, expected):
|
|
79
|
+
assert project_query_is_unique(query) == expected
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@pytest.mark.parametrize(
|
|
83
|
+
"query, valid",
|
|
84
|
+
[
|
|
85
|
+
pytest.param(
|
|
86
|
+
MissionQuery(patterns=["test"], project_query=ProjectQuery()),
|
|
87
|
+
False,
|
|
88
|
+
id="non-unique project",
|
|
89
|
+
),
|
|
90
|
+
pytest.param(
|
|
91
|
+
MissionQuery(
|
|
92
|
+
patterns=["test"],
|
|
93
|
+
project_query=ProjectQuery(ids=[uuid4()]),
|
|
94
|
+
),
|
|
95
|
+
True,
|
|
96
|
+
id="valid query",
|
|
97
|
+
),
|
|
98
|
+
pytest.param(
|
|
99
|
+
MissionQuery(ids=[uuid4()]),
|
|
100
|
+
False,
|
|
101
|
+
id="mission by id",
|
|
102
|
+
),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
def test_check_mission_query_is_createable(query, valid):
|
|
106
|
+
if not valid:
|
|
107
|
+
with pytest.raises(InvalidMissionQuery):
|
|
108
|
+
check_mission_query_is_creatable(query)
|
|
109
|
+
else:
|
|
110
|
+
check_mission_query_is_creatable(query)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.mark.parametrize(
|
|
114
|
+
"query, valid",
|
|
115
|
+
[
|
|
116
|
+
pytest.param(
|
|
117
|
+
ProjectQuery(patterns=["test"]),
|
|
118
|
+
True,
|
|
119
|
+
id="project name",
|
|
120
|
+
),
|
|
121
|
+
pytest.param(
|
|
122
|
+
ProjectQuery(ids=[uuid4()]),
|
|
123
|
+
False,
|
|
124
|
+
id="project by id",
|
|
125
|
+
),
|
|
126
|
+
pytest.param(
|
|
127
|
+
ProjectQuery(ids=[uuid4(), uuid4()]),
|
|
128
|
+
False,
|
|
129
|
+
id="multiple project ids",
|
|
130
|
+
),
|
|
131
|
+
],
|
|
132
|
+
)
|
|
133
|
+
def test_check_project_query_is_creatable(query, valid):
|
|
134
|
+
if not valid:
|
|
135
|
+
with pytest.raises(InvalidProjectQuery):
|
|
136
|
+
check_project_query_is_creatable(query)
|
|
137
|
+
else:
|
|
138
|
+
check_project_query_is_creatable(query)
|
tests/test_utils.py
CHANGED
|
@@ -5,15 +5,18 @@ from tempfile import TemporaryDirectory
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
|
+
|
|
8
9
|
from kleinkram.errors import FileTypeNotSupported
|
|
9
10
|
from kleinkram.utils import b64_md5
|
|
10
11
|
from kleinkram.utils import check_file_paths
|
|
11
|
-
from kleinkram.utils import
|
|
12
|
+
from kleinkram.utils import check_filename_is_sanatized
|
|
12
13
|
from kleinkram.utils import get_filename
|
|
13
14
|
from kleinkram.utils import get_filename_map
|
|
14
15
|
from kleinkram.utils import is_valid_uuid4
|
|
16
|
+
from kleinkram.utils import parse_path_like
|
|
17
|
+
from kleinkram.utils import parse_uuid_like
|
|
18
|
+
from kleinkram.utils import singleton_list
|
|
15
19
|
from kleinkram.utils import split_args
|
|
16
|
-
from kleinkram.utils import to_name_or_uuid
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
def test_split_args():
|
|
@@ -53,23 +56,14 @@ def test_check_file_paths():
|
|
|
53
56
|
assert check_file_paths([exists_bag, exits_mcap]) is None
|
|
54
57
|
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
["*.bag", "*.mcap"],
|
|
65
|
-
["a.bag", "b.mcap"],
|
|
66
|
-
id="all match",
|
|
67
|
-
),
|
|
68
|
-
pytest.param(["a", "b", "c"], ["a", "b"], ["a", "b"], id="full name match"),
|
|
69
|
-
],
|
|
70
|
-
)
|
|
71
|
-
def test_filtered_by_patterns(names, patterns, expected):
|
|
72
|
-
assert filtered_by_patterns(names, patterns) == expected
|
|
59
|
+
def test_check_filename_is_sanatized():
|
|
60
|
+
valid = "t_-est"
|
|
61
|
+
invalid = "test%"
|
|
62
|
+
too_long = "a" * 100
|
|
63
|
+
|
|
64
|
+
assert check_filename_is_sanatized(valid)
|
|
65
|
+
assert not check_filename_is_sanatized(invalid)
|
|
66
|
+
assert not check_filename_is_sanatized(too_long)
|
|
73
67
|
|
|
74
68
|
|
|
75
69
|
def test_is_valid_uuid4():
|
|
@@ -119,9 +113,25 @@ def test_b64_md5():
|
|
|
119
113
|
assert b64_md5(file) == "XrY7u+Ae7tCTyyK7j1rNww=="
|
|
120
114
|
|
|
121
115
|
|
|
122
|
-
def
|
|
123
|
-
|
|
124
|
-
|
|
116
|
+
def test_singleton_list() -> None:
|
|
117
|
+
assert [] == singleton_list(None)
|
|
118
|
+
assert [1] == singleton_list(1)
|
|
119
|
+
assert [[1]] == singleton_list([1])
|
|
120
|
+
assert [True] == singleton_list(True)
|
|
121
|
+
|
|
122
|
+
ob = object()
|
|
123
|
+
assert [ob] == singleton_list(ob)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_parse_uuid_like() -> None:
|
|
127
|
+
_id = uuid4()
|
|
128
|
+
assert parse_uuid_like(str(_id)) == _id
|
|
129
|
+
assert parse_uuid_like(_id) == _id
|
|
130
|
+
|
|
131
|
+
with pytest.raises(ValueError):
|
|
132
|
+
parse_uuid_like("invalid")
|
|
133
|
+
|
|
125
134
|
|
|
126
|
-
|
|
127
|
-
assert
|
|
135
|
+
def test_parse_path_like() -> None:
|
|
136
|
+
assert parse_path_like("test") == Path("test")
|
|
137
|
+
assert parse_path_like(Path("test")) == Path("test")
|
tests/test_wrappers.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from kleinkram.api.query import FileQuery
|
|
6
|
+
from kleinkram.api.query import MissionQuery
|
|
7
|
+
from kleinkram.api.query import ProjectQuery
|
|
8
|
+
from kleinkram.wrappers import _args_to_file_query
|
|
9
|
+
from kleinkram.wrappers import _args_to_mission_query
|
|
10
|
+
from kleinkram.wrappers import _args_to_project_query
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_args_to_project_query() -> None:
|
|
14
|
+
assert _args_to_project_query() == ProjectQuery()
|
|
15
|
+
assert _args_to_project_query(project_names=["test"]) == ProjectQuery(
|
|
16
|
+
patterns=["test"]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
_id = uuid4()
|
|
20
|
+
assert _args_to_project_query(project_ids=[_id]) == ProjectQuery(ids=[_id])
|
|
21
|
+
assert _args_to_project_query(
|
|
22
|
+
project_names=["test"], project_ids=[_id]
|
|
23
|
+
) == ProjectQuery(patterns=["test"], ids=[_id])
|
|
24
|
+
assert _args_to_project_query(project_ids=[str(_id)]) == ProjectQuery(ids=[_id])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_args_to_mission_query() -> None:
|
|
28
|
+
assert _args_to_mission_query() == MissionQuery()
|
|
29
|
+
assert _args_to_mission_query(mission_names=["test"]) == MissionQuery(
|
|
30
|
+
patterns=["test"]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
_id = uuid4()
|
|
34
|
+
assert _args_to_mission_query(mission_ids=[_id]) == MissionQuery(ids=[_id])
|
|
35
|
+
assert _args_to_mission_query(
|
|
36
|
+
mission_names=["test"], mission_ids=[_id]
|
|
37
|
+
) == MissionQuery(patterns=["test"], ids=[_id])
|
|
38
|
+
assert _args_to_mission_query(mission_ids=[str(_id)]) == MissionQuery(ids=[_id])
|
|
39
|
+
|
|
40
|
+
assert _args_to_mission_query(project_names=["test"]) == MissionQuery(
|
|
41
|
+
project_query=ProjectQuery(patterns=["test"])
|
|
42
|
+
)
|
|
43
|
+
assert _args_to_mission_query(project_ids=[_id]) == MissionQuery(
|
|
44
|
+
project_query=ProjectQuery(ids=[_id])
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_args_to_file_query() -> None:
|
|
49
|
+
assert _args_to_file_query() == FileQuery()
|
|
50
|
+
assert _args_to_file_query(file_names=["test"]) == FileQuery(patterns=["test"])
|
|
51
|
+
|
|
52
|
+
_id = uuid4()
|
|
53
|
+
assert _args_to_file_query(file_ids=[_id]) == FileQuery(ids=[_id])
|
|
54
|
+
assert _args_to_file_query(file_names=["test"], file_ids=[_id]) == FileQuery(
|
|
55
|
+
patterns=["test"], ids=[_id]
|
|
56
|
+
)
|
|
57
|
+
assert _args_to_file_query(file_ids=[str(_id)]) == FileQuery(ids=[_id])
|
|
58
|
+
|
|
59
|
+
assert _args_to_file_query(mission_names=["test"]) == FileQuery(
|
|
60
|
+
mission_query=MissionQuery(patterns=["test"])
|
|
61
|
+
)
|
|
62
|
+
assert _args_to_file_query(mission_ids=[_id]) == FileQuery(
|
|
63
|
+
mission_query=MissionQuery(ids=[_id])
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
assert _args_to_file_query(project_names=["test"]) == FileQuery(
|
|
67
|
+
mission_query=MissionQuery(project_query=ProjectQuery(patterns=["test"]))
|
|
68
|
+
)
|
|
69
|
+
assert _args_to_file_query(project_ids=[_id]) == FileQuery(
|
|
70
|
+
mission_query=MissionQuery(project_query=ProjectQuery(ids=[_id]))
|
|
71
|
+
)
|
kleinkram/api/parsing.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
from typing import Dict
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from uuid import UUID
|
|
7
|
-
|
|
8
|
-
from kleinkram.errors import ParsingError
|
|
9
|
-
from kleinkram.models import File
|
|
10
|
-
from kleinkram.models import FileState
|
|
11
|
-
from kleinkram.models import Mission
|
|
12
|
-
from kleinkram.models import Project
|
|
13
|
-
|
|
14
|
-
__all__ = [
|
|
15
|
-
"_parse_project",
|
|
16
|
-
"_parse_mission",
|
|
17
|
-
"_parse_file",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _parse_project(project: Dict[str, Any]) -> Project:
|
|
22
|
-
try:
|
|
23
|
-
project_id = UUID(project["uuid"], version=4)
|
|
24
|
-
project_name = project["name"]
|
|
25
|
-
project_description = project["description"]
|
|
26
|
-
|
|
27
|
-
parsed = Project(
|
|
28
|
-
id=project_id, name=project_name, description=project_description
|
|
29
|
-
)
|
|
30
|
-
except Exception:
|
|
31
|
-
raise ParsingError(f"error parsing project: {project}")
|
|
32
|
-
return parsed
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _parse_mission(
|
|
36
|
-
mission: Dict[str, Any], project: Optional[Project] = None
|
|
37
|
-
) -> Mission:
|
|
38
|
-
try:
|
|
39
|
-
mission_id = UUID(mission["uuid"], version=4)
|
|
40
|
-
mission_name = mission["name"]
|
|
41
|
-
|
|
42
|
-
project_id = (
|
|
43
|
-
project.id if project else UUID(mission["project"]["uuid"], version=4)
|
|
44
|
-
)
|
|
45
|
-
project_name = project.name if project else mission["project"]["name"]
|
|
46
|
-
|
|
47
|
-
parsed = Mission(
|
|
48
|
-
id=mission_id,
|
|
49
|
-
name=mission_name,
|
|
50
|
-
project_id=project_id,
|
|
51
|
-
project_name=project_name,
|
|
52
|
-
)
|
|
53
|
-
except Exception:
|
|
54
|
-
raise ParsingError(f"error parsing mission: {mission}")
|
|
55
|
-
return parsed
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _parse_file(file: Dict[str, Any], mission: Optional[Mission] = None) -> File:
|
|
59
|
-
try:
|
|
60
|
-
filename = file["filename"]
|
|
61
|
-
file_id = UUID(file["uuid"], version=4)
|
|
62
|
-
file_size = file["size"]
|
|
63
|
-
file_hash = file["hash"]
|
|
64
|
-
|
|
65
|
-
project_id = (
|
|
66
|
-
mission.project_id if mission else UUID(file["project"]["uuid"], version=4)
|
|
67
|
-
)
|
|
68
|
-
project_name = mission.project_name if mission else file["project"]["name"]
|
|
69
|
-
|
|
70
|
-
mission_id = mission.id if mission else UUID(file["mission"]["uuid"], version=4)
|
|
71
|
-
mission_name = mission.name if mission else file["mission"]["name"]
|
|
72
|
-
|
|
73
|
-
parsed = File(
|
|
74
|
-
id=file_id,
|
|
75
|
-
name=filename,
|
|
76
|
-
size=file_size,
|
|
77
|
-
hash=file_hash,
|
|
78
|
-
project_id=project_id,
|
|
79
|
-
project_name=project_name,
|
|
80
|
-
mission_id=mission_id,
|
|
81
|
-
mission_name=mission_name,
|
|
82
|
-
state=FileState(file["state"]),
|
|
83
|
-
)
|
|
84
|
-
except Exception:
|
|
85
|
-
raise ParsingError(f"error parsing file: {file}")
|
|
86
|
-
return parsed
|
kleinkram/commands/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
kleinkram/commands/endpoint.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
import typer
|
|
6
|
-
from kleinkram.auth import Config
|
|
7
|
-
|
|
8
|
-
HELP = """\
|
|
9
|
-
Get or set the current endpoint.
|
|
10
|
-
|
|
11
|
-
The endpoint is used to determine the API server to connect to\
|
|
12
|
-
(default is the API server of https://datasets.leggedrobotics.com).
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
endpoint_typer = typer.Typer(
|
|
16
|
-
name="endpoint",
|
|
17
|
-
help=HELP,
|
|
18
|
-
no_args_is_help=True,
|
|
19
|
-
context_settings={"help_option_names": ["-h", "--help"]},
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@endpoint_typer.command("set")
|
|
24
|
-
def set_endpoint(endpoint: str = typer.Argument(None, help="API endpoint to use")):
|
|
25
|
-
"""
|
|
26
|
-
Use this command to switch between different API endpoints.\n
|
|
27
|
-
Standard endpoints are:\n
|
|
28
|
-
- http://localhost:3000\n
|
|
29
|
-
- https://api.datasets.leggedrobotics.com\n
|
|
30
|
-
- https://api.datasets.dev.leggedrobotics.com
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
if not endpoint:
|
|
34
|
-
raise ValueError("No endpoint provided.")
|
|
35
|
-
|
|
36
|
-
tokenfile = Config()
|
|
37
|
-
tokenfile.endpoint = endpoint
|
|
38
|
-
tokenfile.save()
|
|
39
|
-
|
|
40
|
-
print(f"Endpoint set to: {endpoint}")
|
|
41
|
-
if tokenfile.endpoint not in tokenfile.credentials:
|
|
42
|
-
print("\nLogin with `klein login`.")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@endpoint_typer.command("list")
|
|
46
|
-
def list_endpoints():
|
|
47
|
-
"""
|
|
48
|
-
Get the current endpoint
|
|
49
|
-
|
|
50
|
-
Also displays all endpoints with saved tokens.
|
|
51
|
-
"""
|
|
52
|
-
config = Config()
|
|
53
|
-
print(f"Current endpoint: {config.endpoint}\n", file=sys.stderr)
|
|
54
|
-
|
|
55
|
-
if not config.credentials:
|
|
56
|
-
print("No saved credentials found.", file=sys.stderr)
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
print("Found Credentials for:", file=sys.stderr)
|
|
60
|
-
for ep in config.credentials.keys():
|
|
61
|
-
print(" - ", file=sys.stderr, end="", flush=True)
|
|
62
|
-
print(ep, file=sys.stdout, flush=True)
|
kleinkram/commands/mission.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
import typer
|
|
7
|
-
from kleinkram.api.client import AuthenticatedClient
|
|
8
|
-
from kleinkram.api.routes import _update_mission_metadata
|
|
9
|
-
from kleinkram.errors import MissionNotFound
|
|
10
|
-
from kleinkram.resources import get_missions_by_spec
|
|
11
|
-
from kleinkram.resources import mission_spec_is_unique
|
|
12
|
-
from kleinkram.resources import MissionSpec
|
|
13
|
-
from kleinkram.resources import ProjectSpec
|
|
14
|
-
from kleinkram.utils import load_metadata
|
|
15
|
-
from kleinkram.utils import split_args
|
|
16
|
-
|
|
17
|
-
mission_typer = typer.Typer(
|
|
18
|
-
no_args_is_help=True, context_settings={"help_option_names": ["-h", "--help"]}
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
UPDATE_HELP = """\
|
|
23
|
-
Update a mission.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
NOT_IMPLEMENTED_YET = "Not implemented yet"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@mission_typer.command(help=UPDATE_HELP)
|
|
30
|
-
def update(
|
|
31
|
-
project: Optional[str] = typer.Option(
|
|
32
|
-
None, "--project", "-p", help="project id or name"
|
|
33
|
-
),
|
|
34
|
-
mission: str = typer.Option(..., "--mission", "-m", help="mission id or name"),
|
|
35
|
-
metadata: str = typer.Option(help="path to metadata file (json or yaml)"),
|
|
36
|
-
) -> None:
|
|
37
|
-
mission_ids, mission_patterns = split_args([mission])
|
|
38
|
-
project_ids, project_patterns = split_args([project] if project else [])
|
|
39
|
-
|
|
40
|
-
project_spec = ProjectSpec(ids=project_ids, patterns=project_patterns)
|
|
41
|
-
mission_spec = MissionSpec(
|
|
42
|
-
ids=mission_ids,
|
|
43
|
-
patterns=mission_patterns,
|
|
44
|
-
project_spec=project_spec,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
if not mission_spec_is_unique(mission_spec):
|
|
48
|
-
raise ValueError(f"mission spec is not unique: {mission_spec}")
|
|
49
|
-
|
|
50
|
-
client = AuthenticatedClient()
|
|
51
|
-
missions = get_missions_by_spec(client, mission_spec)
|
|
52
|
-
|
|
53
|
-
if not missions:
|
|
54
|
-
raise MissionNotFound(f"Mission {mission} does not exist")
|
|
55
|
-
elif len(missions) > 1:
|
|
56
|
-
raise RuntimeError(f"Multiple missions found: {missions}") # unreachable
|
|
57
|
-
|
|
58
|
-
metadata_dct = load_metadata(Path(metadata))
|
|
59
|
-
_update_mission_metadata(client, missions[0].id, metadata=metadata_dct)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@mission_typer.command(help=NOT_IMPLEMENTED_YET)
|
|
63
|
-
def create() -> None:
|
|
64
|
-
raise NotImplementedError("Not implemented yet")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@mission_typer.command(help=NOT_IMPLEMENTED_YET)
|
|
68
|
-
def delete() -> None:
|
|
69
|
-
raise NotImplementedError("Not implemented yet")
|
kleinkram/commands/project.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import typer
|
|
4
|
-
|
|
5
|
-
project_typer = typer.Typer(
|
|
6
|
-
no_args_is_help=True, context_settings={"help_option_names": ["-h", "--help"]}
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
NOT_IMPLEMENTED_YET = "Not implemented yet"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@project_typer.command(help=NOT_IMPLEMENTED_YET)
|
|
13
|
-
def update() -> None:
|
|
14
|
-
raise NotImplementedError("Not implemented yet")
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@project_typer.command(help=NOT_IMPLEMENTED_YET)
|
|
18
|
-
def create() -> None:
|
|
19
|
-
raise NotImplementedError("Not implemented yet")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@project_typer.command(help=NOT_IMPLEMENTED_YET)
|
|
23
|
-
def delete() -> None:
|
|
24
|
-
raise NotImplementedError("Not implemented yet")
|