kleinkram 0.41.1.dev20250303132129__tar.gz → 0.41.2__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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/PKG-INFO +1 -1
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/client.py +31 -9
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/core.py +7 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/errors.py +11 -5
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/printing.py +1 -1
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/PKG-INFO +1 -1
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/setup.cfg +1 -1
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_core.py +13 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/README.md +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/__init__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/__main__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/_version.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/__init__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/deser.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/file_transfer.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/pagination.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/query.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/api/routes.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/auth.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/__init__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_download.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_endpoint.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_file.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_list.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_mission.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_project.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_upload.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/_verify.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/app.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/cli/error_handling.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/config.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/main.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/models.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/py.typed +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/types.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/utils.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram/wrappers.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/SOURCES.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/dependency_links.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/entry_points.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/requires.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/top_level.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/pyproject.toml +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/requirements.txt +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/setup.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/testing/__init__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/testing/backend_fixtures.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/__init__.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/conftest.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_config.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_end_to_end.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_error_handling.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_fixtures.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_printing.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_query.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_utils.py +0 -0
- {kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/tests/test_wrappers.py +0 -0
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from collections import abc
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
from threading import Lock
|
|
6
7
|
from typing import Any
|
|
7
8
|
from typing import List
|
|
@@ -13,6 +14,9 @@ from typing import Union
|
|
|
13
14
|
import httpx
|
|
14
15
|
from httpx._types import PrimitiveData
|
|
15
16
|
|
|
17
|
+
import kleinkram.errors
|
|
18
|
+
from kleinkram._version import __version__
|
|
19
|
+
from kleinkram.config import CONFIG_PATH
|
|
16
20
|
from kleinkram.config import Config
|
|
17
21
|
from kleinkram.config import Credentials
|
|
18
22
|
from kleinkram.config import get_config
|
|
@@ -26,6 +30,8 @@ COOKIE_AUTH_TOKEN = "authtoken"
|
|
|
26
30
|
COOKIE_REFRESH_TOKEN = "refreshtoken"
|
|
27
31
|
COOKIE_API_KEY = "clikey"
|
|
28
32
|
|
|
33
|
+
CLI_VERSION_HEADER = "Kleinkram-Client-Version"
|
|
34
|
+
|
|
29
35
|
|
|
30
36
|
Data = Union[PrimitiveData, Any]
|
|
31
37
|
NestedData = Mapping[str, Data]
|
|
@@ -65,10 +71,12 @@ class AuthenticatedClient(httpx.Client):
|
|
|
65
71
|
_config: Config
|
|
66
72
|
_config_lock: Lock
|
|
67
73
|
|
|
68
|
-
def __init__(
|
|
74
|
+
def __init__(
|
|
75
|
+
self, config_path: Path = CONFIG_PATH, *args: Any, **kwargs: Any
|
|
76
|
+
) -> None:
|
|
69
77
|
super().__init__(*args, **kwargs)
|
|
70
78
|
|
|
71
|
-
self._config = get_config()
|
|
79
|
+
self._config = get_config(path=config_path)
|
|
72
80
|
self._config_lock = Lock()
|
|
73
81
|
|
|
74
82
|
if self._config.credentials is None:
|
|
@@ -95,9 +103,7 @@ class AuthenticatedClient(httpx.Client):
|
|
|
95
103
|
self.cookies.set(COOKIE_REFRESH_TOKEN, refresh_token)
|
|
96
104
|
|
|
97
105
|
logger.info("refreshing token...")
|
|
98
|
-
response = self.post(
|
|
99
|
-
"/auth/refresh-token",
|
|
100
|
-
)
|
|
106
|
+
response = self.post("/auth/refresh-token")
|
|
101
107
|
response.raise_for_status()
|
|
102
108
|
new_access_token = response.cookies[COOKIE_AUTH_TOKEN]
|
|
103
109
|
creds = Credentials(auth_token=new_access_token, refresh_token=refresh_token)
|
|
@@ -110,6 +116,22 @@ class AuthenticatedClient(httpx.Client):
|
|
|
110
116
|
|
|
111
117
|
self.cookies.set(COOKIE_AUTH_TOKEN, new_access_token)
|
|
112
118
|
|
|
119
|
+
def _send_request_with_kleinkram_headers(
|
|
120
|
+
self, *args: Any, **kwargs: Any
|
|
121
|
+
) -> httpx.Response:
|
|
122
|
+
# add the cli version to the headers
|
|
123
|
+
headers = kwargs.get("headers") or {}
|
|
124
|
+
headers.setdefault(CLI_VERSION_HEADER, __version__)
|
|
125
|
+
kwargs["headers"] = headers
|
|
126
|
+
|
|
127
|
+
# send the request
|
|
128
|
+
response = super().request(*args, **kwargs)
|
|
129
|
+
|
|
130
|
+
# check version compatibility
|
|
131
|
+
if response.status_code == 426:
|
|
132
|
+
raise kleinkram.errors.UpdateCLIVersion
|
|
133
|
+
return response
|
|
134
|
+
|
|
113
135
|
def request(
|
|
114
136
|
self,
|
|
115
137
|
method: str,
|
|
@@ -128,7 +150,7 @@ class AuthenticatedClient(httpx.Client):
|
|
|
128
150
|
logger.info(f"requesting {method} {full_url}")
|
|
129
151
|
|
|
130
152
|
httpx_params = _convert_query_params_to_httpx_format(params or {})
|
|
131
|
-
response =
|
|
153
|
+
response = self._send_request_with_kleinkram_headers(
|
|
132
154
|
method, full_url, params=httpx_params, *args, **kwargs
|
|
133
155
|
)
|
|
134
156
|
|
|
@@ -148,10 +170,10 @@ class AuthenticatedClient(httpx.Client):
|
|
|
148
170
|
raise NotAuthenticated
|
|
149
171
|
|
|
150
172
|
logger.info(f"retrying request {method} {full_url}")
|
|
151
|
-
|
|
173
|
+
response = self._send_request_with_kleinkram_headers(
|
|
152
174
|
method, full_url, params=httpx_params, *args, **kwargs
|
|
153
175
|
)
|
|
154
|
-
logger.info(f"got response {
|
|
155
|
-
return
|
|
176
|
+
logger.info(f"got response {response}")
|
|
177
|
+
return response
|
|
156
178
|
else:
|
|
157
179
|
return response
|
|
@@ -210,6 +210,10 @@ def delete_files(*, client: AuthenticatedClient, file_ids: Collection[UUID]) ->
|
|
|
210
210
|
"""\
|
|
211
211
|
deletes multiple files accross multiple missions
|
|
212
212
|
"""
|
|
213
|
+
if not file_ids:
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
# we need to check that file_ids is not empty, otherwise this is bad
|
|
213
217
|
files = list(kleinkram.api.routes.get_files(client, FileQuery(ids=list(file_ids))))
|
|
214
218
|
|
|
215
219
|
# check if all file_ids were actually found
|
|
@@ -220,6 +224,9 @@ def delete_files(*, client: AuthenticatedClient, file_ids: Collection[UUID]) ->
|
|
|
220
224
|
f"file {file_id} not found, did not delete any files"
|
|
221
225
|
)
|
|
222
226
|
|
|
227
|
+
# to prevent catastrophic mistakes from happening *again*
|
|
228
|
+
assert set(file_ids) == set([file.id for file in files]), "unreachable"
|
|
229
|
+
|
|
223
230
|
# we can only batch delete files within the same mission
|
|
224
231
|
missions_to_files: Dict[UUID, List[UUID]] = {}
|
|
225
232
|
for file in files:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
LOGIN_MESSAGE = "Please login using `klein login`."
|
|
4
|
+
UPDATE_MESSAGE = "Please update your CLI using `pip install --upgrade kleinkram`."
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class ParsingError(Exception): ...
|
|
@@ -33,11 +34,6 @@ class FileNotFound(Exception): ...
|
|
|
33
34
|
class AccessDenied(Exception): ...
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
class NotAuthenticated(Exception):
|
|
37
|
-
def __init__(self) -> None:
|
|
38
|
-
super().__init__(LOGIN_MESSAGE)
|
|
39
|
-
|
|
40
|
-
|
|
41
37
|
class InvalidCLIVersion(Exception): ...
|
|
42
38
|
|
|
43
39
|
|
|
@@ -48,3 +44,13 @@ class FileNameNotSupported(Exception): ...
|
|
|
48
44
|
|
|
49
45
|
|
|
50
46
|
class InvalidMissionMetadata(Exception): ...
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NotAuthenticated(Exception):
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
super().__init__(LOGIN_MESSAGE)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UpdateCLIVersion(Exception):
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
super().__init__(UPDATE_MESSAGE)
|
|
@@ -273,7 +273,7 @@ def project_info_table(project: Project) -> Table:
|
|
|
273
273
|
|
|
274
274
|
|
|
275
275
|
def file_verification_status_table(
|
|
276
|
-
file_status: Mapping[Path, FileVerificationStatus]
|
|
276
|
+
file_status: Mapping[Path, FileVerificationStatus],
|
|
277
277
|
) -> Table:
|
|
278
278
|
table = Table(title="file status")
|
|
279
279
|
table.add_column("filename", style="cyan")
|
|
@@ -77,6 +77,19 @@ def test_delete_existing_files(mission):
|
|
|
77
77
|
assert not list_files(mission_ids=[mission.id], file_names=["*.bag"])
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
@pytest.mark.slow
|
|
81
|
+
def test_delete_working_as_expected_when_passing_empty_list(mission):
|
|
82
|
+
client = AuthenticatedClient()
|
|
83
|
+
|
|
84
|
+
# we need to filter by *.bag to not get flakyness due to conversion
|
|
85
|
+
n_files = len(list_files(mission_ids=[mission.id], file_names=["*.bag"]))
|
|
86
|
+
kleinkram.core.delete_files(client=client, file_ids=[])
|
|
87
|
+
n_files_after_delete = len(
|
|
88
|
+
list_files(mission_ids=[mission.id], file_names=["*.bag"])
|
|
89
|
+
)
|
|
90
|
+
assert n_files == n_files_after_delete
|
|
91
|
+
|
|
92
|
+
|
|
80
93
|
@pytest.mark.slow
|
|
81
94
|
def test_delete_non_existing_files():
|
|
82
95
|
client = AuthenticatedClient()
|
|
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
|
|
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
|
{kleinkram-0.41.1.dev20250303132129 → kleinkram-0.41.2}/kleinkram.egg-info/dependency_links.txt
RENAMED
|
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
|