kleinkram 0.38.1.dev20241212075157__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.

Files changed (57) hide show
  1. kleinkram/__init__.py +33 -2
  2. kleinkram/api/client.py +21 -16
  3. kleinkram/api/deser.py +165 -0
  4. kleinkram/api/file_transfer.py +13 -24
  5. kleinkram/api/pagination.py +56 -0
  6. kleinkram/api/query.py +111 -0
  7. kleinkram/api/routes.py +270 -97
  8. kleinkram/auth.py +21 -20
  9. kleinkram/cli/__init__.py +0 -0
  10. kleinkram/{commands/download.py → cli/_download.py} +18 -44
  11. kleinkram/cli/_endpoint.py +58 -0
  12. kleinkram/{commands/list.py → cli/_list.py} +25 -38
  13. kleinkram/cli/_mission.py +153 -0
  14. kleinkram/cli/_project.py +99 -0
  15. kleinkram/cli/_upload.py +84 -0
  16. kleinkram/cli/_verify.py +56 -0
  17. kleinkram/{app.py → cli/app.py} +50 -22
  18. kleinkram/cli/error_handling.py +44 -0
  19. kleinkram/config.py +141 -107
  20. kleinkram/core.py +251 -3
  21. kleinkram/errors.py +13 -45
  22. kleinkram/main.py +1 -1
  23. kleinkram/models.py +48 -149
  24. kleinkram/printing.py +325 -0
  25. kleinkram/py.typed +0 -0
  26. kleinkram/types.py +9 -0
  27. kleinkram/utils.py +82 -27
  28. kleinkram/wrappers.py +401 -0
  29. {kleinkram-0.38.1.dev20241212075157.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/METADATA +3 -3
  30. kleinkram-0.38.1.dev20250113080249.dist-info/RECORD +48 -0
  31. {kleinkram-0.38.1.dev20241212075157.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/WHEEL +1 -1
  32. {kleinkram-0.38.1.dev20241212075157.dist-info → kleinkram-0.38.1.dev20250113080249.dist-info}/top_level.txt +1 -0
  33. testing/__init__.py +0 -0
  34. testing/backend_fixtures.py +69 -0
  35. tests/conftest.py +7 -0
  36. tests/test_config.py +115 -0
  37. tests/test_core.py +165 -0
  38. tests/test_end_to_end.py +29 -39
  39. tests/test_fixtures.py +29 -0
  40. tests/test_printing.py +62 -0
  41. tests/test_query.py +138 -0
  42. tests/test_utils.py +34 -24
  43. tests/test_wrappers.py +71 -0
  44. kleinkram/api/parsing.py +0 -86
  45. kleinkram/commands/__init__.py +0 -1
  46. kleinkram/commands/endpoint.py +0 -62
  47. kleinkram/commands/mission.py +0 -69
  48. kleinkram/commands/project.py +0 -24
  49. kleinkram/commands/upload.py +0 -164
  50. kleinkram/commands/verify.py +0 -142
  51. kleinkram/consts.py +0 -8
  52. kleinkram/enums.py +0 -10
  53. kleinkram/resources.py +0 -158
  54. kleinkram-0.38.1.dev20241212075157.dist-info/LICENSE +0 -674
  55. kleinkram-0.38.1.dev20241212075157.dist-info/RECORD +0 -37
  56. tests/test_resources.py +0 -137
  57. {kleinkram-0.38.1.dev20241212075157.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 rich.console import Console
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: List[str]) -> Tuple[List[UUID], List[str]]:
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
- if file.is_dir():
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 noop(*args: Any, **kwargs: Any) -> None:
50
- _ = args, kwargs # suppress unused variable warning
51
- return
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
1
+ Metadata-Version: 2.2
2
2
  Name: kleinkram
3
- Version: 0.38.1.dev20241212075157
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
testing/__init__.py ADDED
File without changes