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
kleinkram/utils.py
CHANGED
|
@@ -10,20 +10,56 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
from typing import Dict
|
|
12
12
|
from typing import List
|
|
13
|
+
from typing import Optional
|
|
13
14
|
from typing import Sequence
|
|
14
15
|
from typing import Tuple
|
|
16
|
+
from typing import TypeVar
|
|
15
17
|
from typing import Union
|
|
16
18
|
from uuid import UUID
|
|
17
19
|
|
|
18
20
|
import yaml
|
|
21
|
+
from rich.console import Console
|
|
22
|
+
|
|
19
23
|
from kleinkram._version import __version__
|
|
24
|
+
from kleinkram.errors import FileNameNotSupported
|
|
20
25
|
from kleinkram.errors import FileTypeNotSupported
|
|
21
|
-
from
|
|
26
|
+
from kleinkram.models import File
|
|
27
|
+
from kleinkram.types import IdLike
|
|
28
|
+
from kleinkram.types import PathLike
|
|
22
29
|
|
|
23
30
|
INTERNAL_ALLOWED_CHARS = string.ascii_letters + string.digits + "_" + "-"
|
|
31
|
+
SUPPORT_FILE_TYPES = [
|
|
32
|
+
".bag",
|
|
33
|
+
".mcap",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def file_paths_from_files(
|
|
38
|
+
files: Sequence[File], *, dest: Path, allow_nested: bool = False
|
|
39
|
+
) -> Dict[Path, File]:
|
|
40
|
+
"""\
|
|
41
|
+
determines the destinations for a sequence of `File` objects,
|
|
42
|
+
possibly nested by project and mission
|
|
43
|
+
"""
|
|
44
|
+
if (
|
|
45
|
+
len(set([(file.project_id, file.mission_id) for file in files])) > 1
|
|
46
|
+
and not allow_nested
|
|
47
|
+
):
|
|
48
|
+
raise ValueError("files from multiple missions were selected")
|
|
49
|
+
elif not allow_nested:
|
|
50
|
+
return {dest / file.name: file for file in files}
|
|
51
|
+
else:
|
|
52
|
+
return {
|
|
53
|
+
dest / file.project_name / file.mission_name / file.name: file
|
|
54
|
+
for file in files
|
|
55
|
+
}
|
|
24
56
|
|
|
25
57
|
|
|
26
|
-
def split_args(args:
|
|
58
|
+
def split_args(args: Sequence[str]) -> Tuple[List[UUID], List[str]]:
|
|
59
|
+
"""\
|
|
60
|
+
split a sequece of strings into a list of UUIDs and a list of names
|
|
61
|
+
depending on whether the string is a valid UUID or not
|
|
62
|
+
"""
|
|
27
63
|
uuids = []
|
|
28
64
|
names = []
|
|
29
65
|
for arg in args:
|
|
@@ -35,20 +71,30 @@ def split_args(args: List[str]) -> Tuple[List[UUID], List[str]]:
|
|
|
35
71
|
|
|
36
72
|
|
|
37
73
|
def check_file_paths(files: Sequence[Path]) -> None:
|
|
74
|
+
"""\
|
|
75
|
+
checks that files exist, are files and have a supported file suffix
|
|
76
|
+
|
|
77
|
+
NOTE: kleinkram treats filesuffixes as filetypes and limits
|
|
78
|
+
the supported suffixes
|
|
79
|
+
"""
|
|
38
80
|
for file in files:
|
|
39
|
-
|
|
40
|
-
raise FileNotFoundError(f"{file} is a directory and not a file")
|
|
41
|
-
if not file.exists():
|
|
42
|
-
raise FileNotFoundError(f"{file} does not exist")
|
|
43
|
-
if file.suffix not in (".bag", ".mcap"):
|
|
44
|
-
raise FileTypeNotSupported(
|
|
45
|
-
f"only `.bag` or `.mcap` files are supported: {file}"
|
|
46
|
-
)
|
|
81
|
+
check_file_path(file)
|
|
47
82
|
|
|
48
83
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
84
|
+
def check_file_path(file: Path) -> None:
|
|
85
|
+
if file.is_dir():
|
|
86
|
+
raise FileNotFoundError(f"{file} is a directory and not a file")
|
|
87
|
+
if not file.exists():
|
|
88
|
+
raise FileNotFoundError(f"{file} does not exist")
|
|
89
|
+
if file.suffix not in SUPPORT_FILE_TYPES:
|
|
90
|
+
raise FileTypeNotSupported(
|
|
91
|
+
f"only {', '.join(SUPPORT_FILE_TYPES)} files are supported: {file}"
|
|
92
|
+
)
|
|
93
|
+
if not check_filename_is_sanatized(file.stem):
|
|
94
|
+
raise FileNameNotSupported(
|
|
95
|
+
f"only `{''.join(INTERNAL_ALLOWED_CHARS)}` are "
|
|
96
|
+
f"allowed in filenames and at most 50chars: {file}"
|
|
97
|
+
)
|
|
52
98
|
|
|
53
99
|
|
|
54
100
|
def format_error(msg: str, exc: Exception, *, verbose: bool = False) -> str:
|
|
@@ -65,14 +111,6 @@ def format_traceback(exc: Exception) -> str:
|
|
|
65
111
|
)
|
|
66
112
|
|
|
67
113
|
|
|
68
|
-
def filtered_by_patterns(names: Sequence[str], patterns: List[str]) -> List[str]:
|
|
69
|
-
filtered = []
|
|
70
|
-
for name in names:
|
|
71
|
-
if any(fnmatch.fnmatch(name, p) for p in patterns):
|
|
72
|
-
filtered.append(name)
|
|
73
|
-
return filtered
|
|
74
|
-
|
|
75
|
-
|
|
76
114
|
def styled_string(*objects: Any, **kwargs: Any) -> str:
|
|
77
115
|
"""\
|
|
78
116
|
accepts any object that Console.print can print
|
|
@@ -92,6 +130,14 @@ def is_valid_uuid4(uuid: str) -> bool:
|
|
|
92
130
|
return False
|
|
93
131
|
|
|
94
132
|
|
|
133
|
+
def check_filename_is_sanatized(filename: str) -> bool:
|
|
134
|
+
if len(filename) > 50:
|
|
135
|
+
return False
|
|
136
|
+
if not all(char in INTERNAL_ALLOWED_CHARS for char in filename):
|
|
137
|
+
return False
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
95
141
|
def get_filename(path: Path) -> str:
|
|
96
142
|
"""\
|
|
97
143
|
takes a path and returns a sanitized filename
|
|
@@ -145,12 +191,6 @@ def b64_md5(file: Path) -> str:
|
|
|
145
191
|
return base64.b64encode(binary_digest).decode("utf-8")
|
|
146
192
|
|
|
147
193
|
|
|
148
|
-
def to_name_or_uuid(s: str) -> Union[UUID, str]:
|
|
149
|
-
if is_valid_uuid4(s):
|
|
150
|
-
return UUID(s)
|
|
151
|
-
return s
|
|
152
|
-
|
|
153
|
-
|
|
154
194
|
def load_metadata(path: Path) -> Dict[str, str]:
|
|
155
195
|
if not path.exists():
|
|
156
196
|
raise FileNotFoundError(f"metadata file not found: {path}")
|
|
@@ -164,3 +204,18 @@ def load_metadata(path: Path) -> Dict[str, str]:
|
|
|
164
204
|
def get_supported_api_version() -> Tuple[int, int, int]:
|
|
165
205
|
vers = __version__.split(".")
|
|
166
206
|
return tuple(map(int, vers[:3])) # type: ignore
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
T = TypeVar("T")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def singleton_list(x: Optional[T]) -> List[T]:
|
|
213
|
+
return [] if x is None else [x]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def parse_uuid_like(s: IdLike) -> UUID:
|
|
217
|
+
return UUID(str(s))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def parse_path_like(s: PathLike) -> Path:
|
|
221
|
+
return Path(s)
|
kleinkram/wrappers.py
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""\
|
|
2
|
+
this file contains wrappers around core functionality
|
|
3
|
+
|
|
4
|
+
these functions are meant to be exposed to the user, they
|
|
5
|
+
accept a more diverse set of arguments and handle the
|
|
6
|
+
conversion to the internal representation
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Collection
|
|
13
|
+
from typing import Dict
|
|
14
|
+
from typing import List
|
|
15
|
+
from typing import Literal
|
|
16
|
+
from typing import Optional
|
|
17
|
+
from typing import Sequence
|
|
18
|
+
from typing import overload
|
|
19
|
+
|
|
20
|
+
import kleinkram.api.routes
|
|
21
|
+
import kleinkram.core
|
|
22
|
+
import kleinkram.utils
|
|
23
|
+
from kleinkram.api.client import AuthenticatedClient
|
|
24
|
+
from kleinkram.api.query import FileQuery
|
|
25
|
+
from kleinkram.api.query import MissionQuery
|
|
26
|
+
from kleinkram.api.query import ProjectQuery
|
|
27
|
+
from kleinkram.errors import FileNameNotSupported
|
|
28
|
+
from kleinkram.models import File
|
|
29
|
+
from kleinkram.models import Mission
|
|
30
|
+
from kleinkram.models import Project
|
|
31
|
+
from kleinkram.types import IdLike
|
|
32
|
+
from kleinkram.types import PathLike
|
|
33
|
+
from kleinkram.utils import parse_path_like
|
|
34
|
+
from kleinkram.utils import parse_uuid_like
|
|
35
|
+
from kleinkram.utils import singleton_list
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _args_to_project_query(
|
|
39
|
+
project_names: Optional[Sequence[str]] = None,
|
|
40
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
41
|
+
) -> ProjectQuery:
|
|
42
|
+
return ProjectQuery(
|
|
43
|
+
ids=[parse_uuid_like(_id) for _id in project_ids or []],
|
|
44
|
+
patterns=list(project_names or []),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _args_to_mission_query(
|
|
49
|
+
mission_names: Optional[Sequence[str]] = None,
|
|
50
|
+
mission_ids: Optional[Sequence[IdLike]] = None,
|
|
51
|
+
project_names: Optional[Sequence[str]] = None,
|
|
52
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
53
|
+
) -> MissionQuery:
|
|
54
|
+
return MissionQuery(
|
|
55
|
+
ids=[parse_uuid_like(_id) for _id in mission_ids or []],
|
|
56
|
+
patterns=list(mission_names or []),
|
|
57
|
+
project_query=_args_to_project_query(
|
|
58
|
+
project_names=project_names, project_ids=project_ids
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _args_to_file_query(
|
|
64
|
+
file_names: Optional[Sequence[str]] = None,
|
|
65
|
+
file_ids: Optional[Sequence[IdLike]] = None,
|
|
66
|
+
mission_names: Optional[Sequence[str]] = None,
|
|
67
|
+
mission_ids: Optional[Sequence[IdLike]] = None,
|
|
68
|
+
project_names: Optional[Sequence[str]] = None,
|
|
69
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
70
|
+
) -> FileQuery:
|
|
71
|
+
return FileQuery(
|
|
72
|
+
ids=[parse_uuid_like(_id) for _id in file_ids or []],
|
|
73
|
+
patterns=list(file_names or []),
|
|
74
|
+
mission_query=_args_to_mission_query(
|
|
75
|
+
mission_names=mission_names,
|
|
76
|
+
mission_ids=mission_ids,
|
|
77
|
+
project_names=project_names,
|
|
78
|
+
project_ids=project_ids,
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def download(
|
|
84
|
+
*,
|
|
85
|
+
file_ids: Optional[Sequence[IdLike]] = None,
|
|
86
|
+
file_names: Optional[Sequence[str]] = None,
|
|
87
|
+
mission_ids: Optional[Sequence[IdLike]] = None,
|
|
88
|
+
mission_names: Optional[Sequence[str]] = None,
|
|
89
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
90
|
+
project_names: Optional[Sequence[str]] = None,
|
|
91
|
+
dest: PathLike,
|
|
92
|
+
nested: bool = False,
|
|
93
|
+
overwrite: bool = False,
|
|
94
|
+
verbose: bool = False,
|
|
95
|
+
) -> None:
|
|
96
|
+
query = _args_to_file_query(
|
|
97
|
+
file_names=file_names,
|
|
98
|
+
file_ids=file_ids,
|
|
99
|
+
mission_names=mission_names,
|
|
100
|
+
mission_ids=mission_ids,
|
|
101
|
+
project_names=project_names,
|
|
102
|
+
project_ids=project_ids,
|
|
103
|
+
)
|
|
104
|
+
client = AuthenticatedClient()
|
|
105
|
+
kleinkram.core.download(
|
|
106
|
+
client=client,
|
|
107
|
+
query=query,
|
|
108
|
+
base_dir=parse_path_like(dest),
|
|
109
|
+
nested=nested,
|
|
110
|
+
overwrite=overwrite,
|
|
111
|
+
verbose=verbose,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def list_files(
|
|
116
|
+
*,
|
|
117
|
+
file_ids: Optional[Sequence[IdLike]] = None,
|
|
118
|
+
file_names: Optional[Sequence[str]] = None,
|
|
119
|
+
mission_ids: Optional[Sequence[IdLike]] = None,
|
|
120
|
+
mission_names: Optional[Sequence[str]] = None,
|
|
121
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
122
|
+
project_names: Optional[Sequence[str]] = None,
|
|
123
|
+
) -> List[File]:
|
|
124
|
+
query = _args_to_file_query(
|
|
125
|
+
file_names=file_names,
|
|
126
|
+
file_ids=file_ids,
|
|
127
|
+
mission_names=mission_names,
|
|
128
|
+
mission_ids=mission_ids,
|
|
129
|
+
project_names=project_names,
|
|
130
|
+
project_ids=project_ids,
|
|
131
|
+
)
|
|
132
|
+
client = AuthenticatedClient()
|
|
133
|
+
return list(kleinkram.api.routes.get_files(client, query))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def list_missions(
|
|
137
|
+
*,
|
|
138
|
+
mission_ids: Optional[Sequence[IdLike]] = None,
|
|
139
|
+
mission_names: Optional[Sequence[str]] = None,
|
|
140
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
141
|
+
project_names: Optional[Sequence[str]] = None,
|
|
142
|
+
) -> List[Mission]:
|
|
143
|
+
query = _args_to_mission_query(
|
|
144
|
+
mission_names=mission_names,
|
|
145
|
+
mission_ids=mission_ids,
|
|
146
|
+
project_names=project_names,
|
|
147
|
+
project_ids=project_ids,
|
|
148
|
+
)
|
|
149
|
+
client = AuthenticatedClient()
|
|
150
|
+
return list(kleinkram.api.routes.get_missions(client, query))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def list_projects(
|
|
154
|
+
*,
|
|
155
|
+
project_ids: Optional[Sequence[IdLike]] = None,
|
|
156
|
+
project_names: Optional[Sequence[str]] = None,
|
|
157
|
+
) -> List[Project]:
|
|
158
|
+
query = _args_to_project_query(
|
|
159
|
+
project_names=project_names,
|
|
160
|
+
project_ids=project_ids,
|
|
161
|
+
)
|
|
162
|
+
client = AuthenticatedClient()
|
|
163
|
+
return list(kleinkram.api.routes.get_projects(client, query))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@overload
|
|
167
|
+
def upload(
|
|
168
|
+
*,
|
|
169
|
+
mission_name: str,
|
|
170
|
+
project_name: str,
|
|
171
|
+
files: Sequence[PathLike],
|
|
172
|
+
create: bool = False,
|
|
173
|
+
fix_filenames: bool = False,
|
|
174
|
+
metadata: Optional[Dict[str, str]] = None,
|
|
175
|
+
ignore_missing_metadata: bool = False,
|
|
176
|
+
verbose: bool = False,
|
|
177
|
+
) -> None: ...
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@overload
|
|
181
|
+
def upload(
|
|
182
|
+
*,
|
|
183
|
+
mission_id: IdLike,
|
|
184
|
+
files: Sequence[PathLike],
|
|
185
|
+
create: Literal[False] = False,
|
|
186
|
+
fix_filenames: bool = False,
|
|
187
|
+
verbose: bool = False,
|
|
188
|
+
) -> None: ...
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@overload
|
|
192
|
+
def upload(
|
|
193
|
+
*,
|
|
194
|
+
mission_name: str,
|
|
195
|
+
project_id: IdLike,
|
|
196
|
+
files: Sequence[PathLike],
|
|
197
|
+
create: bool = False,
|
|
198
|
+
fix_filenames: bool = False,
|
|
199
|
+
metadata: Optional[Dict[str, str]] = None,
|
|
200
|
+
ignore_missing_metadata: bool = False,
|
|
201
|
+
verbose: bool = False,
|
|
202
|
+
) -> None: ...
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def upload(
|
|
206
|
+
*,
|
|
207
|
+
mission_name: Optional[str] = None,
|
|
208
|
+
mission_id: Optional[IdLike] = None,
|
|
209
|
+
project_name: Optional[str] = None,
|
|
210
|
+
project_id: Optional[IdLike] = None,
|
|
211
|
+
files: Sequence[PathLike],
|
|
212
|
+
create: bool = False,
|
|
213
|
+
fix_filenames: bool = False,
|
|
214
|
+
metadata: Optional[Dict[str, str]] = None,
|
|
215
|
+
ignore_missing_metadata: bool = False,
|
|
216
|
+
verbose: bool = False,
|
|
217
|
+
) -> None:
|
|
218
|
+
parsed_file_paths = [parse_path_like(f) for f in files]
|
|
219
|
+
if not fix_filenames:
|
|
220
|
+
for file in parsed_file_paths:
|
|
221
|
+
if not kleinkram.utils.check_filename_is_sanatized(file.stem):
|
|
222
|
+
print(file.name)
|
|
223
|
+
raise FileNameNotSupported(
|
|
224
|
+
f"only `{''.join(kleinkram.utils.INTERNAL_ALLOWED_CHARS)}` are "
|
|
225
|
+
f"allowed in filenames and at most 50 chars: {file}"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
query = _args_to_mission_query(
|
|
229
|
+
mission_names=singleton_list(mission_name),
|
|
230
|
+
mission_ids=singleton_list(mission_id),
|
|
231
|
+
project_names=singleton_list(project_name),
|
|
232
|
+
project_ids=singleton_list(project_id),
|
|
233
|
+
)
|
|
234
|
+
client = AuthenticatedClient()
|
|
235
|
+
kleinkram.core.upload(
|
|
236
|
+
client=client,
|
|
237
|
+
query=query,
|
|
238
|
+
file_paths=parsed_file_paths,
|
|
239
|
+
create=create,
|
|
240
|
+
metadata=metadata,
|
|
241
|
+
ignore_missing_metadata=ignore_missing_metadata,
|
|
242
|
+
verbose=verbose,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@overload
|
|
247
|
+
def verify(
|
|
248
|
+
*,
|
|
249
|
+
mission_name: str,
|
|
250
|
+
project_name: str,
|
|
251
|
+
files: Sequence[PathLike],
|
|
252
|
+
verbose: bool = False,
|
|
253
|
+
) -> Dict[Path, kleinkram.core.FileVerificationStatus]: ...
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@overload
|
|
257
|
+
def verify(
|
|
258
|
+
*,
|
|
259
|
+
mission_name: str,
|
|
260
|
+
project_id: IdLike,
|
|
261
|
+
files: Sequence[PathLike],
|
|
262
|
+
verbose: bool = False,
|
|
263
|
+
) -> Dict[Path, kleinkram.core.FileVerificationStatus]: ...
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@overload
|
|
267
|
+
def verify(
|
|
268
|
+
*,
|
|
269
|
+
mission_id: IdLike,
|
|
270
|
+
files: Sequence[PathLike],
|
|
271
|
+
verbose: bool = False,
|
|
272
|
+
) -> Dict[Path, kleinkram.core.FileVerificationStatus]: ...
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def verify(
|
|
276
|
+
*,
|
|
277
|
+
mission_name: Optional[str] = None,
|
|
278
|
+
mission_id: Optional[IdLike] = None,
|
|
279
|
+
project_name: Optional[str] = None,
|
|
280
|
+
project_id: Optional[IdLike] = None,
|
|
281
|
+
files: Sequence[PathLike],
|
|
282
|
+
skip_hash: bool = False,
|
|
283
|
+
verbose: bool = False,
|
|
284
|
+
) -> Dict[Path, kleinkram.core.FileVerificationStatus]:
|
|
285
|
+
query = _args_to_mission_query(
|
|
286
|
+
mission_names=singleton_list(mission_name),
|
|
287
|
+
mission_ids=singleton_list(mission_id),
|
|
288
|
+
project_names=singleton_list(project_name),
|
|
289
|
+
project_ids=singleton_list(project_id),
|
|
290
|
+
)
|
|
291
|
+
return kleinkram.core.verify(
|
|
292
|
+
client=AuthenticatedClient(),
|
|
293
|
+
query=query,
|
|
294
|
+
file_paths=[parse_path_like(f) for f in files],
|
|
295
|
+
skip_hash=skip_hash,
|
|
296
|
+
verbose=verbose,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def create_mission(
|
|
301
|
+
mission_name: str,
|
|
302
|
+
project_id: IdLike,
|
|
303
|
+
metadata: Dict[str, str],
|
|
304
|
+
ignore_missing_metadata: bool = False,
|
|
305
|
+
) -> None:
|
|
306
|
+
kleinkram.api.routes._create_mission(
|
|
307
|
+
AuthenticatedClient(),
|
|
308
|
+
parse_uuid_like(project_id),
|
|
309
|
+
mission_name,
|
|
310
|
+
metadata=metadata,
|
|
311
|
+
ignore_missing_tags=ignore_missing_metadata,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def create_project(project_name: str, description: str) -> None:
|
|
316
|
+
kleinkram.api.routes._create_project(
|
|
317
|
+
AuthenticatedClient(), project_name, description
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def update_file(file_id: IdLike) -> None:
|
|
322
|
+
kleinkram.core.update_file(
|
|
323
|
+
client=AuthenticatedClient(), file_id=parse_uuid_like(file_id)
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def update_mission(mission_id: IdLike, metadata: Dict[str, str]) -> None:
|
|
328
|
+
kleinkram.core.update_mission(
|
|
329
|
+
client=AuthenticatedClient(),
|
|
330
|
+
mission_id=parse_uuid_like(mission_id),
|
|
331
|
+
metadata=metadata,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def update_project(project_id: IdLike, description: Optional[str] = None) -> None:
|
|
336
|
+
kleinkram.core.update_project(
|
|
337
|
+
client=AuthenticatedClient(),
|
|
338
|
+
project_id=parse_uuid_like(project_id),
|
|
339
|
+
description=description,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def delete_files(file_ids: Collection[IdLike]) -> None:
|
|
344
|
+
"""\
|
|
345
|
+
delete multiple files by their ids
|
|
346
|
+
"""
|
|
347
|
+
kleinkram.core.delete_files(
|
|
348
|
+
client=AuthenticatedClient(),
|
|
349
|
+
file_ids=[parse_uuid_like(_id) for _id in file_ids],
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def delete_file(file_id: IdLike) -> None:
|
|
354
|
+
"""\
|
|
355
|
+
delete a single file by id
|
|
356
|
+
"""
|
|
357
|
+
file = kleinkram.api.routes.get_file(
|
|
358
|
+
AuthenticatedClient(), FileQuery(ids=[parse_uuid_like(file_id)])
|
|
359
|
+
)
|
|
360
|
+
kleinkram.api.routes._delete_files(
|
|
361
|
+
AuthenticatedClient(), file_ids=[file.id], mission_id=file.mission_id
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def delete_mission(mission_id: IdLike) -> None:
|
|
366
|
+
kleinkram.core.delete_mission(
|
|
367
|
+
client=AuthenticatedClient(), mission_id=parse_uuid_like(mission_id)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def delete_project(project_id: IdLike) -> None:
|
|
372
|
+
kleinkram.core.delete_project(
|
|
373
|
+
client=AuthenticatedClient(), project_id=parse_uuid_like(project_id)
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def get_file(file_id: IdLike) -> File:
|
|
378
|
+
"""\
|
|
379
|
+
get a file by its id
|
|
380
|
+
"""
|
|
381
|
+
return kleinkram.api.routes.get_file(
|
|
382
|
+
AuthenticatedClient(), FileQuery(ids=[parse_uuid_like(file_id)])
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def get_mission(mission_id: IdLike) -> Mission:
|
|
387
|
+
"""\
|
|
388
|
+
get a mission by its id
|
|
389
|
+
"""
|
|
390
|
+
return kleinkram.api.routes.get_mission(
|
|
391
|
+
AuthenticatedClient(), MissionQuery(ids=[parse_uuid_like(mission_id)])
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def get_project(project_id: IdLike) -> Project:
|
|
396
|
+
"""\
|
|
397
|
+
get a project by its id
|
|
398
|
+
"""
|
|
399
|
+
return kleinkram.api.routes.get_project(
|
|
400
|
+
AuthenticatedClient(), ProjectQuery(ids=[parse_uuid_like(project_id)])
|
|
401
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: kleinkram
|
|
3
|
-
Version: 0.38.1.
|
|
3
|
+
Version: 0.38.1.dev20250113080249
|
|
4
4
|
Summary: give me your bags
|
|
5
5
|
Author: Cyrill Püntener, Dominique Garmier, Johann Schwabe
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -13,10 +13,10 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
13
13
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
14
|
Requires-Python: >=3.8
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
|
-
License-File: LICENSE
|
|
17
16
|
Requires-Dist: boto3
|
|
18
17
|
Requires-Dist: botocore
|
|
19
18
|
Requires-Dist: httpx
|
|
19
|
+
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: pyyaml
|
|
21
21
|
Requires-Dist: rich
|
|
22
22
|
Requires-Dist: tqdm
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
kleinkram/__init__.py,sha256=xIJqTJw2kbCGryGlCeAdpmtR1FTxmrW1MklUNQEaj74,1061
|
|
2
|
+
kleinkram/__main__.py,sha256=B9RiZxfO4jpCmWPUHyKJ7_EoZlEG4sPpH-nz7T_YhhQ,125
|
|
3
|
+
kleinkram/_version.py,sha256=QYJyRTcqFcJj4qWYpqs7WcoOP6jxDMqyvxLY-cD6KcE,129
|
|
4
|
+
kleinkram/auth.py,sha256=miNMmUu1XjT9DMNQu8BQoK2ygfUmXrnUV4D0zR156d4,2968
|
|
5
|
+
kleinkram/config.py,sha256=3FuIpq65SL0phetvOJftvG6VOAZ4RjnvLkcFX6pwuDY,5452
|
|
6
|
+
kleinkram/core.py,sha256=Q7OYIKPN9K6kxf9Eq7r5XRHPJ3RtT7SBZp_3_CS8yuY,8429
|
|
7
|
+
kleinkram/errors.py,sha256=4mygNxkf6IBgaiRWY95qu0v6z4TAXA3G6CUcXC9FU3s,772
|
|
8
|
+
kleinkram/main.py,sha256=BTE0mZN__xd46wBhFi6iBlK9eGGQvJ1LdUMsbnysLi0,172
|
|
9
|
+
kleinkram/models.py,sha256=8nJlPrKVLSmehspeuQSFV6nUo76JzehUn6KIZYH1xy4,1832
|
|
10
|
+
kleinkram/printing.py,sha256=fgSlfRaGqQ7dNiIZGMvEPxMatmUL3MrCvh2ibrz9b_s,9914
|
|
11
|
+
kleinkram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
kleinkram/types.py,sha256=nfDjj8TB1Jn5vqO0Xg6qhLOuKom9DDhe62BrngqnVGM,185
|
|
13
|
+
kleinkram/utils.py,sha256=XX5RjN_-B1eQX-ddlmHypNQQkpxPwwxe6sN0dMYo1uU,6137
|
|
14
|
+
kleinkram/wrappers.py,sha256=4xXU43eNnvMG2sssU330MmTLSSRdurOpnZ-zNGOGmt0,11342
|
|
15
|
+
kleinkram/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
kleinkram/api/client.py,sha256=t6pWY29bQi36IhfIy98Ijc-w-3LzcBTJxXprfPIdwJo,3614
|
|
17
|
+
kleinkram/api/deser.py,sha256=-eP0haBAFr-dRWJ1v-P5o_rxA8vOBlZMtAGXW8ItIAk,4870
|
|
18
|
+
kleinkram/api/file_transfer.py,sha256=VbVQh6F7r81207OIx8zwnRGhA6SpXmzBhJQHQgR8tso,12982
|
|
19
|
+
kleinkram/api/pagination.py,sha256=P_zPsBKlMWkmAv-YfUNHaGW-XLB_4U8BDMrKyiDFIXk,1370
|
|
20
|
+
kleinkram/api/query.py,sha256=gn5yf-eRB_Bcw2diLjt66yQtorrZMKdj5_oNA_oOhvc,3281
|
|
21
|
+
kleinkram/api/routes.py,sha256=UpaEIV4Vy9vOVCjaV_4Jk83AnB0ilFJWPeLiVpIPN94,12270
|
|
22
|
+
kleinkram/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
kleinkram/cli/_download.py,sha256=H4YlXJkZE4Md02nzgrO_i8Hsm4ZIejPsxBEKkcn4KHs,2371
|
|
24
|
+
kleinkram/cli/_endpoint.py,sha256=oY0p4bnuHLEDJCXtTmir4AHswcKAygZ8I4IWC3RFcKc,1796
|
|
25
|
+
kleinkram/cli/_list.py,sha256=5gI3aIUeKC0_eWPQqdFXSBBFvpkTTJSm31TamHa197c,3090
|
|
26
|
+
kleinkram/cli/_mission.py,sha256=zDFnOozOFckpuREFgIPt1IzG5q3b1bsNxYlWQoHoz5A,5301
|
|
27
|
+
kleinkram/cli/_project.py,sha256=N0C96NC_onCEwTteYp2wgkkwkdJt-1q43LFdqNXfjC8,3398
|
|
28
|
+
kleinkram/cli/_upload.py,sha256=gOhbjbmqhmwW7p6bWlSvI53vLHvBFO9QqD1kdU92I2k,2813
|
|
29
|
+
kleinkram/cli/_verify.py,sha256=0ABVa4U_WzaV36ClR8NsOIG7KAMRlnFmsbtnHhbWVj4,1742
|
|
30
|
+
kleinkram/cli/app.py,sha256=vbIP08b_qTItZGygpzO4rKX6ISJNypGSPNn8CahaLK0,6481
|
|
31
|
+
kleinkram/cli/error_handling.py,sha256=ZZ3xTych-BdVGAKMjHbtQRIFoILxYOD2yo8Zg1wdozA,1280
|
|
32
|
+
testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
+
testing/backend_fixtures.py,sha256=3BDHWrHz2awhItOEyb4LCSa3G2yKXP7WOqlrImshSyk,1660
|
|
34
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
tests/conftest.py,sha256=5MLYQOtQoXWl0TRkYntYKNdqpd4hl9m0XTRi5OXanYI,104
|
|
36
|
+
tests/test_config.py,sha256=gfQUjwJb2rfvrNbi7cfkNwLr_DFn4mWMM29l3bunsBk,3269
|
|
37
|
+
tests/test_core.py,sha256=JbzB05LWmaaP77uXeTOQtCJD2AJT0zO9zhDfcZ3GNH8,5139
|
|
38
|
+
tests/test_end_to_end.py,sha256=kIY62viZk2_d5HGt4GVNTkDjR0f1IkAv9OJ8HSqcBG8,3172
|
|
39
|
+
tests/test_fixtures.py,sha256=BiR_CRFgSZ-jh6uUcQTei2WNLhqMSPf5ORtrwn6cBZY,1002
|
|
40
|
+
tests/test_printing.py,sha256=qCr04OJVl5ouht9FoeWGKOi8MZXevVV1EDghzV1JaMc,1903
|
|
41
|
+
tests/test_query.py,sha256=fExmCKXLA7-9j2S2sF_sbvRX_2s6Cp3a7OTcqE25q9g,3864
|
|
42
|
+
tests/test_utils.py,sha256=4HpBHy9goIxUIbuoD3-GaNbDHyujPXPESzkVC3RTkf8,4148
|
|
43
|
+
tests/test_wrappers.py,sha256=TbcTyO2L7fslbzgfDdcVZkencxNQ8cGPZm_iB6c9d6Q,2673
|
|
44
|
+
kleinkram-0.38.1.dev20250113080249.dist-info/METADATA,sha256=_Jcd7lxd8ai8gqM9O2sd2jJBRVfgN00KVmjnj-IfjLo,2333
|
|
45
|
+
kleinkram-0.38.1.dev20250113080249.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
46
|
+
kleinkram-0.38.1.dev20250113080249.dist-info/entry_points.txt,sha256=SaB2l5aqhSr8gmaMw2kvQU90a8Bnl7PedU8cWYxkfYo,46
|
|
47
|
+
kleinkram-0.38.1.dev20250113080249.dist-info/top_level.txt,sha256=N3-sJagEHu1Tk1X6Dx1X1q0pLDNbDZpLzRxVftvepds,24
|
|
48
|
+
kleinkram-0.38.1.dev20250113080249.dist-info/RECORD,,
|
testing/__init__.py
ADDED
|
File without changes
|