kleinkram 0.43.2.dev20250331124109__py3-none-any.whl → 0.58.0.dev20260110152317__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.
Files changed (44) hide show
  1. kleinkram/api/client.py +6 -18
  2. kleinkram/api/deser.py +152 -1
  3. kleinkram/api/file_transfer.py +202 -101
  4. kleinkram/api/pagination.py +11 -2
  5. kleinkram/api/query.py +10 -10
  6. kleinkram/api/routes.py +192 -59
  7. kleinkram/auth.py +108 -7
  8. kleinkram/cli/_action.py +131 -0
  9. kleinkram/cli/_download.py +8 -19
  10. kleinkram/cli/_endpoint.py +2 -4
  11. kleinkram/cli/_file.py +6 -18
  12. kleinkram/cli/_file_validator.py +125 -0
  13. kleinkram/cli/_list.py +5 -15
  14. kleinkram/cli/_mission.py +24 -28
  15. kleinkram/cli/_project.py +10 -26
  16. kleinkram/cli/_run.py +220 -0
  17. kleinkram/cli/_upload.py +58 -26
  18. kleinkram/cli/_verify.py +59 -16
  19. kleinkram/cli/app.py +56 -17
  20. kleinkram/cli/error_handling.py +1 -3
  21. kleinkram/config.py +6 -21
  22. kleinkram/core.py +53 -43
  23. kleinkram/errors.py +12 -0
  24. kleinkram/models.py +51 -1
  25. kleinkram/printing.py +229 -18
  26. kleinkram/utils.py +10 -24
  27. kleinkram/wrappers.py +54 -30
  28. {kleinkram-0.43.2.dev20250331124109.dist-info → kleinkram-0.58.0.dev20260110152317.dist-info}/METADATA +6 -4
  29. kleinkram-0.58.0.dev20260110152317.dist-info/RECORD +53 -0
  30. {kleinkram-0.43.2.dev20250331124109.dist-info → kleinkram-0.58.0.dev20260110152317.dist-info}/WHEEL +1 -1
  31. {kleinkram-0.43.2.dev20250331124109.dist-info → kleinkram-0.58.0.dev20260110152317.dist-info}/top_level.txt +0 -1
  32. {testing → tests}/backend_fixtures.py +27 -3
  33. tests/conftest.py +1 -1
  34. tests/generate_test_data.py +314 -0
  35. tests/test_config.py +2 -6
  36. tests/test_core.py +11 -31
  37. tests/test_end_to_end.py +3 -5
  38. tests/test_fixtures.py +3 -5
  39. tests/test_printing.py +9 -11
  40. tests/test_utils.py +1 -3
  41. tests/test_wrappers.py +9 -27
  42. kleinkram-0.43.2.dev20250331124109.dist-info/RECORD +0 -50
  43. testing/__init__.py +0 -0
  44. {kleinkram-0.43.2.dev20250331124109.dist-info → kleinkram-0.58.0.dev20260110152317.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,314 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import struct
5
+ import time
6
+
7
+ from rosbags.rosbag1 import Writer
8
+
9
+
10
+ def serialize_string(data):
11
+ """Serialize a string for ROS1 (std_msgs/String)."""
12
+ encoded = data.encode("utf-8")
13
+ return struct.pack("<I", len(encoded)) + encoded
14
+
15
+
16
+ def serialize_time(secs, nsecs):
17
+ return struct.pack("<II", secs, nsecs)
18
+
19
+
20
+ def serialize_header(seq, secs, nsecs, frame_id):
21
+ return struct.pack("<I", seq) + serialize_time(secs, nsecs) + serialize_string(frame_id)
22
+
23
+
24
+ def serialize_log(seq, secs, nsecs, frame_id, level, name, msg, file, function, line, topics):
25
+ # rosgraph_msgs/Log
26
+ # Header header
27
+ # byte level
28
+ # string name
29
+ # string msg
30
+ # string file
31
+ # string function
32
+ # uint32 line
33
+ # string[] topics
34
+ data = serialize_header(seq, secs, nsecs, frame_id)
35
+ data += struct.pack("<b", level)
36
+ data += serialize_string(name)
37
+ data += serialize_string(msg)
38
+ data += serialize_string(file)
39
+ data += serialize_string(function)
40
+ data += struct.pack("<I", line)
41
+ data += struct.pack("<I", len(topics))
42
+ for t in topics:
43
+ data += serialize_string(t)
44
+ return data
45
+
46
+
47
+ def serialize_temperature(seq, secs, nsecs, frame_id, temp, variance):
48
+ # sensor_msgs/Temperature
49
+ # Header header
50
+ # float64 temperature
51
+ # float64 variance
52
+ data = serialize_header(seq, secs, nsecs, frame_id)
53
+ data += struct.pack("<dd", temp, variance)
54
+ return data
55
+
56
+
57
+ def serialize_time_reference(seq, secs, nsecs, frame_id, ref_secs, ref_nsecs, source):
58
+ # sensor_msgs/TimeReference
59
+ # Header header
60
+ # time time_ref
61
+ # string source
62
+ data = serialize_header(seq, secs, nsecs, frame_id)
63
+ data += serialize_time(ref_secs, ref_nsecs)
64
+ data += serialize_string(source)
65
+ return data
66
+
67
+
68
+ def serialize_twist_stamped(seq, secs, nsecs, frame_id, linear, angular):
69
+ # geometry_msgs/TwistStamped
70
+ # Header header
71
+ # Twist twist (Vector3 linear, Vector3 angular)
72
+ data = serialize_header(seq, secs, nsecs, frame_id)
73
+ data += struct.pack("<ddd", linear[0], linear[1], linear[2])
74
+ data += struct.pack("<ddd", angular[0], angular[1], angular[2])
75
+ return data
76
+
77
+
78
+ def serialize_tf_message(transforms):
79
+ # tf2_msgs/TFMessage
80
+ # geometry_msgs/TransformStamped[] transforms
81
+ data = struct.pack("<I", len(transforms))
82
+ for t in transforms:
83
+ # TransformStamped
84
+ # Header header
85
+ # string child_frame_id
86
+ # Transform transform (Vector3 translation, Quaternion rotation)
87
+ data += serialize_header(t["seq"], t["secs"], t["nsecs"], t["frame_id"])
88
+ data += serialize_string(t["child_frame_id"])
89
+ data += struct.pack("<ddd", t["tx"], t["ty"], t["tz"])
90
+ data += struct.pack("<dddd", t["rx"], t["ry"], t["rz"], t["rw"])
91
+ return data
92
+
93
+
94
+ def generate_bag(filename, target_size):
95
+ # Adjust payload to be smaller for small files
96
+ payload_size = 1024
97
+ if target_size > 1024 * 1024:
98
+ payload_size = 1024 * 1024
99
+
100
+ if target_size < 2000: # Very small files
101
+ payload_size = 100
102
+
103
+ payload = "x" * payload_size
104
+ serialized_msg = serialize_string(payload)
105
+
106
+ # Calculate number of messages needed
107
+ # Approximate overhead per message in bag file is ~30-50 bytes (record header) + connection ref
108
+ # We'll assume overhead is small compared to payload for large files, but significant for small ones.
109
+ # We'll just write until we think we are close.
110
+
111
+ msg_size = len(serialized_msg) + 50 # rough estimate including record headers
112
+ num_msgs = int(target_size / msg_size) + 1
113
+ if num_msgs < 1:
114
+ num_msgs = 1
115
+
116
+ print(f"Generating {filename} (~{target_size} bytes) with {num_msgs} messages of payload {payload_size}...")
117
+
118
+ if os.path.exists(filename):
119
+ os.remove(filename)
120
+
121
+ with Writer(filename) as writer:
122
+ # Add a connection
123
+ # msg_def for std_msgs/String is just "string data"
124
+ conn = writer.add_connection(
125
+ topic="/test_topic",
126
+ msgtype="std_msgs/msg/String",
127
+ msgdef="string data",
128
+ md5sum="992ce8a1687cec8c8bd883ec73ca41d1",
129
+ )
130
+ timestamp = 1000
131
+ for i in range(num_msgs):
132
+ writer.write(conn, timestamp + i, serialized_msg)
133
+
134
+
135
+ def generate_frontend_bag(filename):
136
+ print(f"Generating frontend test bag: {filename}")
137
+ if os.path.exists(filename):
138
+ os.remove(filename)
139
+
140
+ with Writer(filename) as writer:
141
+ # 1. rosgraph_msgs/Log
142
+ conn_log = writer.add_connection(
143
+ topic="/rosout",
144
+ msgtype="rosgraph_msgs/msg/Log",
145
+ msgdef="Header header\nbyte level\nstring name\nstring msg\nstring file\nstring function\nuint32 "
146
+ "line\nstring[] topics\n================================================================================\n"
147
+ "MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id",
148
+ md5sum="acffd30cd6b6de30f120938c17c593fb",
149
+ )
150
+ # 2. sensor_msgs/Temperature
151
+ conn_temp = writer.add_connection(
152
+ topic="/sensors/temperature",
153
+ msgtype="sensor_msgs/msg/Temperature",
154
+ msgdef="Header header\nfloat64 temperature\nfloat64 variance"
155
+ "\n================================================================================\n"
156
+ "MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id",
157
+ md5sum="ff71b307acdbe7c871a5a6d7edce2f6e",
158
+ )
159
+ # 3. sensor_msgs/TimeReference
160
+ conn_time = writer.add_connection(
161
+ topic="/time_ref",
162
+ msgtype="sensor_msgs/msg/TimeReference",
163
+ msgdef="Header header\ntime time_ref\nstring source\n"
164
+ "================================================================================\n"
165
+ "MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id",
166
+ md5sum="fded64a0265108ba86c3d38fb11c0c16",
167
+ )
168
+ # 4. geometry_msgs/TwistStamped
169
+ conn_twist = writer.add_connection(
170
+ topic="/cmd_vel",
171
+ msgtype="geometry_msgs/msg/TwistStamped",
172
+ msgdef="Header header\ngeometry_msgs/Twist twist\n"
173
+ "================================================================================\n"
174
+ "MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id\n"
175
+ "================================================================================\n"
176
+ "MSG: geometry_msgs/Twist\nVector3 linear\nVector3 angular\n"
177
+ "================================================================================\n"
178
+ "MSG: geometry_msgs/Vector3\nfloat64 x\nfloat64 y\nfloat64 z",
179
+ md5sum="98d34b0043a2093cf9d9345ab6eef12e",
180
+ )
181
+ # 5. tf2_msgs/TFMessage
182
+ conn_tf = writer.add_connection(
183
+ topic="/tf",
184
+ msgtype="tf2_msgs/msg/TFMessage",
185
+ msgdef="geometry_msgs/TransformStamped[] transforms\n"
186
+ "================================================================================\n"
187
+ "MSG: geometry_msgs/TransformStamped\nHeader header\nstring child_frame_id\n"
188
+ "geometry_msgs/Transform transform\n"
189
+ "================================================================================\n"
190
+ "MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id\n"
191
+ "================================================================================\n"
192
+ "MSG: geometry_msgs/Transform\ngeometry_msgs/Vector3 translation\n"
193
+ "geometry_msgs/Quaternion rotation\n"
194
+ "================================================================================\n"
195
+ "MSG: geometry_msgs/Vector3\nfloat64 x\nfloat64 y\nfloat64 z\n"
196
+ "================================================================================\n"
197
+ "MSG: geometry_msgs/Quaternion\nfloat64 x\nfloat64 y\nfloat64 z\nfloat64 w",
198
+ md5sum="94810edda583a504dfda3829e70d7eec",
199
+ )
200
+
201
+ # Write messages
202
+ for i in range(100):
203
+ timestamp = 1000 + i * 100000000 # 100ms steps
204
+ secs = int(timestamp / 1000000000)
205
+ nsecs = timestamp % 1000000000
206
+
207
+ # Log
208
+ writer.write(
209
+ conn_log,
210
+ timestamp,
211
+ serialize_log(
212
+ i,
213
+ secs,
214
+ nsecs,
215
+ "",
216
+ 2,
217
+ "test_node",
218
+ f"Log message {i}",
219
+ "test.cpp",
220
+ "main",
221
+ i,
222
+ ["/rosout"],
223
+ ),
224
+ )
225
+
226
+ # Temperature (sine wave)
227
+ import math
228
+
229
+ temp = 20.0 + 5.0 * math.sin(i * 0.1)
230
+ writer.write(
231
+ conn_temp,
232
+ timestamp,
233
+ serialize_temperature(i, secs, nsecs, "sensor_frame", temp, 0.1),
234
+ )
235
+
236
+ # TimeReference
237
+ writer.write(
238
+ conn_time,
239
+ timestamp,
240
+ serialize_time_reference(i, secs, nsecs, "time_frame", secs, nsecs, "GPS"),
241
+ )
242
+
243
+ # TwistStamped (circle)
244
+ writer.write(
245
+ conn_twist,
246
+ timestamp,
247
+ serialize_twist_stamped(i, secs, nsecs, "base_link", [1.0, 0.0, 0.0], [0.0, 0.0, 0.5]),
248
+ )
249
+
250
+ # TF
251
+ writer.write(
252
+ conn_tf,
253
+ timestamp,
254
+ serialize_tf_message(
255
+ [
256
+ {
257
+ "seq": i,
258
+ "secs": secs,
259
+ "nsecs": nsecs,
260
+ "frame_id": "map",
261
+ "child_frame_id": "base_link",
262
+ "tx": i * 0.1,
263
+ "ty": 0.0,
264
+ "tz": 0.0,
265
+ "rx": 0.0,
266
+ "ry": 0.0,
267
+ "rz": 0.0,
268
+ "rw": 1.0,
269
+ }
270
+ ]
271
+ ),
272
+ )
273
+
274
+
275
+ def main():
276
+ data_dir = os.path.join(os.path.dirname(__file__), "data")
277
+ os.makedirs(data_dir, exist_ok=True)
278
+
279
+ files = {
280
+ "10_KB.bag": 10 * 1024,
281
+ "50_KB.bag": 50 * 1024,
282
+ "1_MB.bag": 1 * 1024 * 1024,
283
+ "17_MB.bag": 17 * 1024 * 1024,
284
+ "125_MB.bag": 125 * 1024 * 1024,
285
+ }
286
+
287
+ for filename, size in files.items():
288
+ filepath = os.path.join(data_dir, filename)
289
+ generate_bag(filepath, size)
290
+
291
+ # Generate backend fixtures
292
+ backend_fixtures_dir = os.path.join(os.path.dirname(__file__), "../../backend/tests/fixtures")
293
+ os.makedirs(backend_fixtures_dir, exist_ok=True)
294
+ generate_bag(os.path.join(backend_fixtures_dir, "test.bag"), 10 * 1024)
295
+ generate_bag(os.path.join(backend_fixtures_dir, "to_delete.bag"), 10 * 1024)
296
+ generate_bag(os.path.join(backend_fixtures_dir, "file1.bag"), 10 * 1024)
297
+ generate_bag(os.path.join(backend_fixtures_dir, "file2.bag"), 10 * 1024)
298
+ generate_bag(os.path.join(backend_fixtures_dir, "move_me.bag"), 10 * 1024)
299
+ generate_bag(os.path.join(backend_fixtures_dir, "move_me.bag"), 10 * 1024)
300
+ generate_bag(os.path.join(backend_fixtures_dir, "state_test.bag"), 10 * 1024)
301
+ generate_frontend_bag(os.path.join(data_dir, "frontend_test.bag"))
302
+
303
+ # Generate dummy MCAP and YAML
304
+ with open(os.path.join(data_dir, "test.yaml"), "w") as f:
305
+ f.write("test: true\nvalue: 123\n")
306
+
307
+ with open(os.path.join(data_dir, "test.mcap"), "wb") as f:
308
+ f.write(b"\x89MCAP\x30\r\n") # Minimal magic bytes
309
+
310
+ print("Done.")
311
+
312
+
313
+ if __name__ == "__main__":
314
+ main()
tests/test_config.py CHANGED
@@ -72,9 +72,7 @@ def test_load_config_default(config_path):
72
72
  assert config.selected_endpoint == get_env().value
73
73
 
74
74
 
75
- def test_load_default_config_with_env_var_api_key_specified(
76
- config_path, set_api_key_env
77
- ):
75
+ def test_load_default_config_with_env_var_api_key_specified(config_path, set_api_key_env):
78
76
  assert set_api_key_env is None
79
77
 
80
78
  config = _load_config(path=config_path)
@@ -87,9 +85,7 @@ def test_load_default_config_with_env_var_api_key_specified(
87
85
  assert not config_path.exists()
88
86
 
89
87
 
90
- def test_load_default_config_with_env_var_endpoints_specified(
91
- config_path, set_endpoint_env
92
- ):
88
+ def test_load_default_config_with_env_var_endpoints_specified(config_path, set_endpoint_env):
93
89
  assert set_endpoint_env is None
94
90
  config = _load_config(path=config_path)
95
91
 
tests/test_core.py CHANGED
@@ -17,43 +17,33 @@ from kleinkram.api.query import MissionQuery
17
17
  from kleinkram.api.query import ProjectQuery
18
18
  from kleinkram.errors import MissionNotFound
19
19
  from kleinkram.models import FileVerificationStatus
20
- from testing.backend_fixtures import DATA_FILES
20
+ from tests.backend_fixtures import DATA_FILES
21
21
 
22
22
 
23
23
  @pytest.mark.slow
24
24
  def test_upload_create(project):
25
25
  mission_name = token_hex(8)
26
- mission_query = MissionQuery(
27
- patterns=[mission_name], project_query=ProjectQuery(ids=[project.id])
28
- )
26
+ mission_query = MissionQuery(patterns=[mission_name], project_query=ProjectQuery(ids=[project.id]))
29
27
 
30
28
  client = AuthenticatedClient()
31
- kleinkram.core.upload(
32
- client=client, query=mission_query, file_paths=DATA_FILES, create=True
33
- )
29
+ kleinkram.core.upload(client=client, query=mission_query, file_paths=DATA_FILES, create=True)
34
30
 
35
31
  mission = list_missions(mission_names=[mission_name])[0]
36
32
  assert mission.project_id == project.id
37
33
  assert mission.name == mission_name
38
34
 
39
35
  files = list_files(mission_ids=[mission.id])
40
- assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
41
- [file.name for file in DATA_FILES]
42
- )
36
+ assert set([file.name for file in files if file.name.endswith(".bag")]) == set([file.name for file in DATA_FILES])
43
37
 
44
38
 
45
39
  @pytest.mark.slow
46
40
  def test_upload_no_create(project):
47
41
  mission_name = token_hex(8)
48
- mission_query = MissionQuery(
49
- patterns=[mission_name], project_query=ProjectQuery(ids=[project.id])
50
- )
42
+ mission_query = MissionQuery(patterns=[mission_name], project_query=ProjectQuery(ids=[project.id]))
51
43
 
52
44
  client = AuthenticatedClient()
53
45
  with pytest.raises(MissionNotFound):
54
- kleinkram.core.upload(
55
- client=client, query=mission_query, file_paths=DATA_FILES, create=False
56
- )
46
+ kleinkram.core.upload(client=client, query=mission_query, file_paths=DATA_FILES, create=False)
57
47
 
58
48
 
59
49
  @pytest.mark.slow
@@ -64,9 +54,7 @@ def test_upload_to_existing_mission(empty_mission):
64
54
  kleinkram.core.upload(client=client, query=mission_query, file_paths=DATA_FILES)
65
55
 
66
56
  files = list_files(mission_ids=[empty_mission.id])
67
- assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
68
- [file.name for file in DATA_FILES]
69
- )
57
+ assert set([file.name for file in files if file.name.endswith(".bag")]) == set([file.name for file in DATA_FILES])
70
58
 
71
59
 
72
60
  @pytest.mark.slow
@@ -84,9 +72,7 @@ def test_delete_working_as_expected_when_passing_empty_list(mission):
84
72
  # we need to filter by *.bag to not get flakyness due to conversion
85
73
  n_files = len(list_files(mission_ids=[mission.id], file_names=["*.bag"]))
86
74
  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
- )
75
+ n_files_after_delete = len(list_files(mission_ids=[mission.id], file_names=["*.bag"]))
90
76
  assert n_files == n_files_after_delete
91
77
 
92
78
 
@@ -132,9 +118,7 @@ def test_create_update_delete_project():
132
118
  assert project.description == "test"
133
119
 
134
120
  new_name = token_hex(8)
135
- kleinkram.core.update_project(
136
- client=client, project_id=project.id, new_name=new_name, description="new desc"
137
- )
121
+ kleinkram.core.update_project(client=client, project_id=project.id, new_name=new_name, description="new desc")
138
122
 
139
123
  project = list_projects(project_ids=[project.id])[0]
140
124
  assert project.name == new_name
@@ -162,13 +146,9 @@ def test_verify(mission):
162
146
  client = AuthenticatedClient()
163
147
  query = MissionQuery(ids=[mission.id])
164
148
 
165
- verify_status = kleinkram.core.verify(
166
- client=client, query=query, file_paths=DATA_FILES, skip_hash=True
167
- )
149
+ verify_status = kleinkram.core.verify(client=client, query=query, file_paths=DATA_FILES, skip_hash=True)
168
150
 
169
- assert all(
170
- status == FileVerificationStatus.UPLAODED for status in verify_status.values()
171
- )
151
+ assert all(status == FileVerificationStatus.UPLOADED for status in verify_status.values())
172
152
 
173
153
 
174
154
  @pytest.mark.slow
tests/test_end_to_end.py CHANGED
@@ -16,7 +16,7 @@ VERBOSE = True
16
16
 
17
17
  CLI = "klein"
18
18
  PROJECT_NAME = "automated-testing"
19
- DATA_PATH = Path(__file__).parent.parent / "data" / "testing"
19
+ DATA_PATH = Path(__file__).parent / "data"
20
20
 
21
21
 
22
22
  @pytest.fixture(scope="session")
@@ -48,9 +48,7 @@ def test_upload_verify_update_download_mission(project, tmp_path, api):
48
48
 
49
49
  mission_name = secrets.token_hex(8)
50
50
  upload = f"{CLI} upload -p {project.name} -m {mission_name} --create {DATA_PATH.absolute()}/*.bag"
51
- verify = (
52
- f"{CLI} verify -p {project.name} -m {mission_name} {DATA_PATH.absolute()}/*.bag"
53
- )
51
+ verify = f"{CLI} verify -p {project.name} -m {mission_name} {DATA_PATH.absolute()}/*.bag"
54
52
  # update = f"{CLI} mission update -p {project.name} -m {mission_name} --metadata {DATA_PATH.absolute()}/metadata.yaml"
55
53
  download = f"{CLI} download -p {project.name} -m {mission_name} --dest {tmp_path.absolute()}"
56
54
  delete_file = f"{CLI} file delete -p {project.name} -m {mission_name} -f {file_names[0].name} -y"
@@ -69,7 +67,7 @@ def test_list_files(project, mission, api):
69
67
  assert run_cmd(f"{CLI} list files -p {project.name}") == 0
70
68
  assert run_cmd(f"{CLI} list files -p {project.name} -m {mission.name}") == 0
71
69
  assert run_cmd(f"{CLI} list files") == 0
72
- assert run_cmd(f"{CLI} list files -p {mission.name}") == 0
70
+ assert run_cmd(f"{CLI} list files -p {project.name}") == 0
73
71
  assert run_cmd(f'{CLI} list files -p "*" -m "*" "*"') == 0
74
72
 
75
73
 
tests/test_fixtures.py CHANGED
@@ -5,8 +5,8 @@ import pytest
5
5
  from kleinkram import list_files
6
6
  from kleinkram import list_missions
7
7
  from kleinkram import list_projects
8
- from testing.backend_fixtures import DATA_FILES
9
- from testing.backend_fixtures import PROJECT_DESCRIPTION
8
+ from tests.backend_fixtures import DATA_FILES
9
+ from tests.backend_fixtures import PROJECT_DESCRIPTION
10
10
 
11
11
 
12
12
  @pytest.mark.slow
@@ -22,9 +22,7 @@ def test_mission_fixture(mission, project):
22
22
 
23
23
  files = list_files(mission_ids=[mission.id])
24
24
 
25
- assert set([file.name for file in files if file.name.endswith(".bag")]) == set(
26
- [file.name for file in DATA_FILES]
27
- )
25
+ assert set([file.name for file in files if file.name.endswith(".bag")]) == set([file.name for file in DATA_FILES])
28
26
 
29
27
 
30
28
  @pytest.mark.slow
tests/test_printing.py CHANGED
@@ -15,14 +15,14 @@ from kleinkram.printing import parse_metadata_value
15
15
  def test_format_bytes():
16
16
  assert format_bytes(0) == "0 B"
17
17
  assert format_bytes(1) == "1 B"
18
- assert format_bytes(1000) == "1000 B"
19
- assert format_bytes(1024) == "1.00 KB"
20
- assert format_bytes(1025) == "1.00 KB"
21
- assert format_bytes(2048) == "2.00 KB"
22
- assert format_bytes(2**20) == "1.00 MB"
23
- assert format_bytes(2**30) == "1.00 GB"
24
- assert format_bytes(2**40) == "1.00 TB"
25
- assert format_bytes(2**50) == "1.00 PB"
18
+ assert format_bytes(999) == "999 B"
19
+ assert format_bytes(1000) == "1.00 KB"
20
+ assert format_bytes(1001) == "1.00 KB"
21
+ assert format_bytes(2000) == "2.00 KB"
22
+ assert format_bytes(10**6) == "1.00 MB"
23
+ assert format_bytes(10**9) == "1.00 GB"
24
+ assert format_bytes(10**12) == "1.00 TB"
25
+ assert format_bytes(10**15) == "1.00 PB"
26
26
 
27
27
 
28
28
  def test_add_placeholder_row():
@@ -48,9 +48,7 @@ def test_parse_metadata_value():
48
48
  mv = MetadataValue(type_=MetadataValueType.BOOLEAN, value="false")
49
49
  assert parse_metadata_value(mv) is False # noqa
50
50
  mv = MetadataValue(type_=MetadataValueType.DATE, value="2021-01-01T00:00:00Z")
51
- assert parse_metadata_value(mv) == datetime.datetime(
52
- 2021, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
53
- )
51
+ assert parse_metadata_value(mv) == datetime.datetime(2021, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
54
52
 
55
53
 
56
54
  @pytest.mark.skip
tests/test_utils.py CHANGED
@@ -85,9 +85,7 @@ def test_is_valid_uuid4():
85
85
  "invalid_sybmols_____.txt",
86
86
  id="invalid symbols",
87
87
  ),
88
- pytest.param(
89
- Path(f'{"a" * 100}.txt'), f'{"a" * 40}38bf3e475f.txt', id="too long"
90
- ),
88
+ pytest.param(Path(f'{"a" * 100}.txt'), f'{"a" * 40}38bf3e475f.txt', id="too long"),
91
89
  pytest.param(Path(f'{"a" * 50}.txt'), f'{"a" * 50}.txt', id="max length"),
92
90
  pytest.param(Path("in/a/folder.txt"), "folder.txt", id="in folder"),
93
91
  ],
tests/test_wrappers.py CHANGED
@@ -12,37 +12,25 @@ from kleinkram.wrappers import _args_to_project_query
12
12
 
13
13
  def test_args_to_project_query() -> None:
14
14
  assert _args_to_project_query() == ProjectQuery()
15
- assert _args_to_project_query(project_names=["test"]) == ProjectQuery(
16
- patterns=["test"]
17
- )
15
+ assert _args_to_project_query(project_names=["test"]) == ProjectQuery(patterns=["test"])
18
16
 
19
17
  _id = uuid4()
20
18
  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])
19
+ assert _args_to_project_query(project_names=["test"], project_ids=[_id]) == ProjectQuery(patterns=["test"], ids=[_id])
24
20
  assert _args_to_project_query(project_ids=[str(_id)]) == ProjectQuery(ids=[_id])
25
21
 
26
22
 
27
23
  def test_args_to_mission_query() -> None:
28
24
  assert _args_to_mission_query() == MissionQuery()
29
- assert _args_to_mission_query(mission_names=["test"]) == MissionQuery(
30
- patterns=["test"]
31
- )
25
+ assert _args_to_mission_query(mission_names=["test"]) == MissionQuery(patterns=["test"])
32
26
 
33
27
  _id = uuid4()
34
28
  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])
29
+ assert _args_to_mission_query(mission_names=["test"], mission_ids=[_id]) == MissionQuery(patterns=["test"], ids=[_id])
38
30
  assert _args_to_mission_query(mission_ids=[str(_id)]) == MissionQuery(ids=[_id])
39
31
 
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
- )
32
+ assert _args_to_mission_query(project_names=["test"]) == MissionQuery(project_query=ProjectQuery(patterns=["test"]))
33
+ assert _args_to_mission_query(project_ids=[_id]) == MissionQuery(project_query=ProjectQuery(ids=[_id]))
46
34
 
47
35
 
48
36
  def test_args_to_file_query() -> None:
@@ -51,17 +39,11 @@ def test_args_to_file_query() -> None:
51
39
 
52
40
  _id = uuid4()
53
41
  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
- )
42
+ assert _args_to_file_query(file_names=["test"], file_ids=[_id]) == FileQuery(patterns=["test"], ids=[_id])
57
43
  assert _args_to_file_query(file_ids=[str(_id)]) == FileQuery(ids=[_id])
58
44
 
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
- )
45
+ assert _args_to_file_query(mission_names=["test"]) == FileQuery(mission_query=MissionQuery(patterns=["test"]))
46
+ assert _args_to_file_query(mission_ids=[_id]) == FileQuery(mission_query=MissionQuery(ids=[_id]))
65
47
 
66
48
  assert _args_to_file_query(project_names=["test"]) == FileQuery(
67
49
  mission_query=MissionQuery(project_query=ProjectQuery(patterns=["test"]))
@@ -1,50 +0,0 @@
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=XD_rHOyJmYYfO7QJf3TLYH5qXA22gXGWi7PT3jujlVs,2968
5
- kleinkram/config.py,sha256=nx6uSM5nLP4SKe8b9VAx4KDtCCwtyshXmzbEJcUwpsY,7411
6
- kleinkram/core.py,sha256=kD2aqN_VhlUGZV4ocij8M_VZCQwFsCf42mJmdupRQVU,8678
7
- kleinkram/errors.py,sha256=qa98YvhDbLqX60P8bcMcFmHy4HxgYNlSROXud8Kj-P4,965
8
- kleinkram/main.py,sha256=BTE0mZN__xd46wBhFi6iBlK9eGGQvJ1LdUMsbnysLi0,172
9
- kleinkram/models.py,sha256=8nJlPrKVLSmehspeuQSFV6nUo76JzehUn6KIZYH1xy4,1832
10
- kleinkram/printing.py,sha256=_vIjs-lPVgv21ER5q5iYtx44OTs5y8wyd32w27WqocM,12161
11
- kleinkram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- kleinkram/types.py,sha256=nfDjj8TB1Jn5vqO0Xg6qhLOuKom9DDhe62BrngqnVGM,185
13
- kleinkram/utils.py,sha256=AsKZxEGStn03L2tqqiMVCQCrDyl8HhwOfpa3no4dfYc,6767
14
- kleinkram/wrappers.py,sha256=4xXU43eNnvMG2sssU330MmTLSSRdurOpnZ-zNGOGmt0,11342
15
- kleinkram/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- kleinkram/api/client.py,sha256=VwuT97_WdbDpcVGwMXB0fRnUoQnUSf7BOP5eXUFokfI,5932
17
- kleinkram/api/deser.py,sha256=xRpYUFKZ0Luoo7XyAtYblJvprmpjNSZOiFVnFKmOzcM,4819
18
- kleinkram/api/file_transfer.py,sha256=_uYYJs1iND4YNpg8_-VUo6zu1DuHV8Or2KkGSVAAL0o,15278
19
- kleinkram/api/pagination.py,sha256=P_zPsBKlMWkmAv-YfUNHaGW-XLB_4U8BDMrKyiDFIXk,1370
20
- kleinkram/api/query.py,sha256=9Exi4hJR7Ml38_zjAcOvSEoIAxZLlpM6QwwzO9fs5Gk,3293
21
- kleinkram/api/routes.py,sha256=x0IfzQO5RAC3rohDSpdUNlVOWl221gh4e0fbMKfGrQA,12251
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/_file.py,sha256=Q2fLDdUyfHFmdGC6wIxMqgEl0F76qszhzWJrRV5rTBM,2973
26
- kleinkram/cli/_list.py,sha256=5gI3aIUeKC0_eWPQqdFXSBBFvpkTTJSm31TamHa197c,3090
27
- kleinkram/cli/_mission.py,sha256=zDFnOozOFckpuREFgIPt1IzG5q3b1bsNxYlWQoHoz5A,5301
28
- kleinkram/cli/_project.py,sha256=N0C96NC_onCEwTteYp2wgkkwkdJt-1q43LFdqNXfjC8,3398
29
- kleinkram/cli/_upload.py,sha256=gOhbjbmqhmwW7p6bWlSvI53vLHvBFO9QqD1kdU92I2k,2813
30
- kleinkram/cli/_verify.py,sha256=0ABVa4U_WzaV36ClR8NsOIG7KAMRlnFmsbtnHhbWVj4,1742
31
- kleinkram/cli/app.py,sha256=m2qq4z95QllvXnxh3koPp0kq06I5R9etsJV8qSV8TMk,7037
32
- kleinkram/cli/error_handling.py,sha256=wK3tzeKVSrZm-xmiyzGLnGT2E4TRpyxhaak6GWGP7P8,1921
33
- testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- testing/backend_fixtures.py,sha256=t5QWwyezHUhxxAlbUuE_eFmpyRaGbnWNNcGPwrO17JM,1571
35
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- tests/conftest.py,sha256=5MLYQOtQoXWl0TRkYntYKNdqpd4hl9m0XTRi5OXanYI,104
37
- tests/test_config.py,sha256=uvVh1iUSCoZc8YioxS_GjF-J7m4JE2z4XUbwK_4wDV0,5855
38
- tests/test_core.py,sha256=9oAb-jxWlRI6EzMwe-oStUFpK4R6ad5vzy95gZgAeuk,5623
39
- tests/test_end_to_end.py,sha256=0W5pUES5hek-pXq4NZtpPZqKTORkGCRsDv5_D3rDMjY,3372
40
- tests/test_error_handling.py,sha256=qPSMKF1qsAHyUME0-krxbIrk38iGKkhAyAah-KwN4NE,1300
41
- tests/test_fixtures.py,sha256=UlPmGbEsGvrDPsaStGMRjNvrVPGjCqOB0RMfLJq2VRA,1071
42
- tests/test_printing.py,sha256=Jz1AjqmqBRjp1JLm6H1oVJyvGaMPlahVXdKnd7UDQFc,2231
43
- tests/test_query.py,sha256=fExmCKXLA7-9j2S2sF_sbvRX_2s6Cp3a7OTcqE25q9g,3864
44
- tests/test_utils.py,sha256=eUBYrn3xrcgcaxm1X4fqZaX4tRvkbI6rh6BUbNbu9T0,4784
45
- tests/test_wrappers.py,sha256=TbcTyO2L7fslbzgfDdcVZkencxNQ8cGPZm_iB6c9d6Q,2673
46
- kleinkram-0.43.2.dev20250331124109.dist-info/METADATA,sha256=oBBV9v06ABIXRmyZQFocEwD5qO1ICqaYSpT2mOBjxas,2825
47
- kleinkram-0.43.2.dev20250331124109.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
48
- kleinkram-0.43.2.dev20250331124109.dist-info/entry_points.txt,sha256=SaB2l5aqhSr8gmaMw2kvQU90a8Bnl7PedU8cWYxkfYo,46
49
- kleinkram-0.43.2.dev20250331124109.dist-info/top_level.txt,sha256=N3-sJagEHu1Tk1X6Dx1X1q0pLDNbDZpLzRxVftvepds,24
50
- kleinkram-0.43.2.dev20250331124109.dist-info/RECORD,,
testing/__init__.py DELETED
File without changes