kleinkram 0.39.0.dev20250224101309__py3-none-any.whl → 0.40.0__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/api/client.py +3 -1
- kleinkram/cli/app.py +10 -0
- kleinkram/config.py +23 -5
- kleinkram/printing.py +33 -10
- kleinkram/utils.py +1 -4
- {kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/METADATA +1 -1
- {kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/RECORD +12 -12
- {kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/WHEEL +1 -1
- tests/test_config.py +19 -1
- tests/test_printing.py +11 -0
- {kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/entry_points.txt +0 -0
- {kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/top_level.txt +0 -0
kleinkram/api/client.py
CHANGED
|
@@ -148,7 +148,9 @@ class AuthenticatedClient(httpx.Client):
|
|
|
148
148
|
raise NotAuthenticated
|
|
149
149
|
|
|
150
150
|
logger.info(f"retrying request {method} {full_url}")
|
|
151
|
-
resp = super().request(
|
|
151
|
+
resp = super().request(
|
|
152
|
+
method, full_url, params=httpx_params, *args, **kwargs
|
|
153
|
+
)
|
|
152
154
|
logger.info(f"got response {resp}")
|
|
153
155
|
return resp
|
|
154
156
|
else:
|
kleinkram/cli/app.py
CHANGED
|
@@ -29,6 +29,7 @@ from kleinkram.cli._upload import upload_typer
|
|
|
29
29
|
from kleinkram.cli._verify import verify_typer
|
|
30
30
|
from kleinkram.cli.error_handling import ErrorHandledTyper
|
|
31
31
|
from kleinkram.cli.error_handling import display_error
|
|
32
|
+
from kleinkram.config import MAX_TABLE_SIZE
|
|
32
33
|
from kleinkram.config import Config
|
|
33
34
|
from kleinkram.config import check_config_compatibility
|
|
34
35
|
from kleinkram.config import get_config
|
|
@@ -179,6 +180,11 @@ def cli(
|
|
|
179
180
|
None, "--version", "-v", callback=_version_callback
|
|
180
181
|
),
|
|
181
182
|
log_level: Optional[LogLevel] = typer.Option(None, help="Set log level."),
|
|
183
|
+
max_lines: int = typer.Option(
|
|
184
|
+
MAX_TABLE_SIZE,
|
|
185
|
+
"--max-lines",
|
|
186
|
+
help="Maximum number of lines when pretty printing tables. -1 for unlimited.",
|
|
187
|
+
),
|
|
182
188
|
):
|
|
183
189
|
if not check_config_compatibility():
|
|
184
190
|
typer.confirm("found incompatible config file, overwrite?", abort=True)
|
|
@@ -189,6 +195,10 @@ def cli(
|
|
|
189
195
|
shared_state.verbose = verbose
|
|
190
196
|
shared_state.debug = debug
|
|
191
197
|
|
|
198
|
+
if max_lines < 0 and max_lines != -1:
|
|
199
|
+
raise typer.BadParameter("`--max-lines` must be -1 or positive")
|
|
200
|
+
shared_state.max_table_size = max_lines
|
|
201
|
+
|
|
192
202
|
if shared_state.debug and log_level is None:
|
|
193
203
|
log_level = LogLevel.DEBUG
|
|
194
204
|
if log_level is None:
|
kleinkram/config.py
CHANGED
|
@@ -24,10 +24,12 @@ from rich.text import Text
|
|
|
24
24
|
|
|
25
25
|
from kleinkram._version import __local__
|
|
26
26
|
from kleinkram._version import __version__
|
|
27
|
+
from kleinkram.utils import format_traceback
|
|
27
28
|
|
|
28
29
|
logger = logging.getLogger(__name__)
|
|
29
30
|
|
|
30
31
|
CONFIG_PATH = Path().home() / ".kleinkram.json"
|
|
32
|
+
MAX_TABLE_SIZE = 256
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
class Environment(Enum):
|
|
@@ -74,8 +76,8 @@ def get_env() -> Environment:
|
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
ACTION_API_KEY = "KLEINKRAM_API_KEY"
|
|
77
|
-
ACTION_API = "
|
|
78
|
-
ACTION_S3 = "
|
|
79
|
+
ACTION_API = "KLEINKRAM_API_ENDPOINT"
|
|
80
|
+
ACTION_S3 = "KLEINKRAM_S3_ENDPOINT"
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
def _get_endpoint_from_action_env_vars() -> Optional[Endpoint]:
|
|
@@ -166,13 +168,28 @@ def _config_from_dict(dct: Dict[str, Any]) -> Config:
|
|
|
166
168
|
)
|
|
167
169
|
|
|
168
170
|
|
|
169
|
-
def
|
|
170
|
-
|
|
171
|
+
def _safe_config_write(
|
|
172
|
+
config: Config, path: Path, tmp_dir: Optional[Path] = None
|
|
173
|
+
) -> None:
|
|
174
|
+
fd, temp_path = tempfile.mkstemp(dir=tmp_dir)
|
|
171
175
|
with os.fdopen(fd, "w") as f:
|
|
172
176
|
json.dump(_config_to_dict(config), f)
|
|
173
177
|
os.replace(temp_path, path)
|
|
174
178
|
|
|
175
179
|
|
|
180
|
+
def _unsafe_config_write(config: Config, path: Path) -> None:
|
|
181
|
+
with open(path, "w") as f:
|
|
182
|
+
json.dump(_config_to_dict(config), f)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def save_config(config: Config, path: Path = CONFIG_PATH) -> None:
|
|
186
|
+
try:
|
|
187
|
+
_safe_config_write(config, path)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.warning(f"failed to safe write config {format_traceback(e)}")
|
|
190
|
+
_unsafe_config_write(config, path)
|
|
191
|
+
|
|
192
|
+
|
|
176
193
|
def _load_config_if_compatible(path: Path) -> Optional[Config]:
|
|
177
194
|
if not path.exists():
|
|
178
195
|
return None
|
|
@@ -234,7 +251,7 @@ def endpoint_table(config: Config) -> Table:
|
|
|
234
251
|
display_name = (
|
|
235
252
|
Text(f"* {name}", style="bold yellow")
|
|
236
253
|
if name == config.selected_endpoint
|
|
237
|
-
else Text(name)
|
|
254
|
+
else Text(f" {name}")
|
|
238
255
|
)
|
|
239
256
|
table.add_row(display_name, endpoint.api, endpoint.s3)
|
|
240
257
|
return table
|
|
@@ -245,6 +262,7 @@ class SharedState:
|
|
|
245
262
|
log_file: Optional[Path] = None
|
|
246
263
|
verbose: bool = True
|
|
247
264
|
debug: bool = False
|
|
265
|
+
max_table_size: int = MAX_TABLE_SIZE
|
|
248
266
|
|
|
249
267
|
|
|
250
268
|
SHARED_STATE = SharedState()
|
kleinkram/printing.py
CHANGED
|
@@ -17,6 +17,7 @@ from rich.console import Console
|
|
|
17
17
|
from rich.table import Table
|
|
18
18
|
from rich.text import Text
|
|
19
19
|
|
|
20
|
+
from kleinkram.config import get_shared_state
|
|
20
21
|
from kleinkram.core import FileVerificationStatus
|
|
21
22
|
from kleinkram.models import File
|
|
22
23
|
from kleinkram.models import FileState
|
|
@@ -46,6 +47,11 @@ FILE_VERIFICATION_STATUS_STYLES = {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
|
|
50
|
+
def _add_placeholder_row(table: Table, skipped: int) -> None:
|
|
51
|
+
first_column = f"... ({skipped} more)"
|
|
52
|
+
table.add_row(first_column, *["..." for _ in range(len(table.columns) - 1)])
|
|
53
|
+
|
|
54
|
+
|
|
49
55
|
def file_state_to_text(file_state: FileState) -> Text:
|
|
50
56
|
return Text(file_state.value, style=FILE_STATE_COLOR[file_state])
|
|
51
57
|
|
|
@@ -114,8 +120,11 @@ def projects_to_table(projects: Sequence[Project]) -> Table:
|
|
|
114
120
|
table.add_column("name")
|
|
115
121
|
table.add_column("description")
|
|
116
122
|
|
|
117
|
-
|
|
123
|
+
max_table_size = get_shared_state().max_table_size
|
|
124
|
+
for project in projects[:max_table_size]:
|
|
118
125
|
table.add_row(str(project.id), project.name, project.description)
|
|
126
|
+
if len(projects) > max_table_size:
|
|
127
|
+
_add_placeholder_row(table, skipped=len(projects) - max_table_size)
|
|
119
128
|
return table
|
|
120
129
|
|
|
121
130
|
|
|
@@ -136,10 +145,11 @@ def missions_to_table(missions: Sequence[Mission]) -> Table:
|
|
|
136
145
|
if not missions_tp:
|
|
137
146
|
return table
|
|
138
147
|
last_project: Optional[str] = None
|
|
139
|
-
|
|
148
|
+
max_table_size = get_shared_state().max_table_size
|
|
149
|
+
for project, _, mission in missions_tp[:max_table_size]:
|
|
140
150
|
# add delimiter row if project changes
|
|
141
151
|
if last_project is not None and last_project != project:
|
|
142
|
-
table.
|
|
152
|
+
table.add_section()
|
|
143
153
|
last_project = project
|
|
144
154
|
|
|
145
155
|
table.add_row(
|
|
@@ -149,6 +159,9 @@ def missions_to_table(missions: Sequence[Mission]) -> Table:
|
|
|
149
159
|
str(mission.number_of_files),
|
|
150
160
|
format_bytes(mission.size),
|
|
151
161
|
)
|
|
162
|
+
|
|
163
|
+
if len(missions_tp) > max_table_size:
|
|
164
|
+
_add_placeholder_row(table, skipped=len(missions_tp) - max_table_size)
|
|
152
165
|
return table
|
|
153
166
|
|
|
154
167
|
|
|
@@ -174,9 +187,10 @@ def files_to_table(
|
|
|
174
187
|
return table
|
|
175
188
|
|
|
176
189
|
last_mission: Optional[str] = None
|
|
177
|
-
|
|
190
|
+
max_table_size = get_shared_state().max_table_size
|
|
191
|
+
for _, mission, _, file in files_tp[:max_table_size]:
|
|
178
192
|
if last_mission is not None and last_mission != mission and delimiters:
|
|
179
|
-
table.
|
|
193
|
+
table.add_section()
|
|
180
194
|
last_mission = mission
|
|
181
195
|
|
|
182
196
|
table.add_row(
|
|
@@ -188,6 +202,10 @@ def files_to_table(
|
|
|
188
202
|
format_bytes(file.size),
|
|
189
203
|
", ".join(file.categories),
|
|
190
204
|
)
|
|
205
|
+
|
|
206
|
+
if len(files_tp) > max_table_size:
|
|
207
|
+
_add_placeholder_row(table, skipped=len(files_tp) - max_table_size)
|
|
208
|
+
|
|
191
209
|
return table
|
|
192
210
|
|
|
193
211
|
|
|
@@ -273,7 +291,8 @@ def print_file_verification_status(
|
|
|
273
291
|
either using pprint or as a list for piping
|
|
274
292
|
"""
|
|
275
293
|
if pprint:
|
|
276
|
-
|
|
294
|
+
table = file_verification_status_table(file_status)
|
|
295
|
+
Console().print(table)
|
|
277
296
|
else:
|
|
278
297
|
for path, status in file_status.items():
|
|
279
298
|
stream = (
|
|
@@ -288,7 +307,8 @@ def print_files(files: Sequence[File], *, pprint: bool) -> None:
|
|
|
288
307
|
either using pprint or as a list for piping
|
|
289
308
|
"""
|
|
290
309
|
if pprint:
|
|
291
|
-
|
|
310
|
+
table = files_to_table(files)
|
|
311
|
+
Console().print(table)
|
|
292
312
|
else:
|
|
293
313
|
for file in files:
|
|
294
314
|
stream = sys.stdout if file.state == FileState.OK else sys.stderr
|
|
@@ -301,7 +321,8 @@ def print_missions(missions: Sequence[Mission], *, pprint: bool) -> None:
|
|
|
301
321
|
either using pprint or as a list for piping
|
|
302
322
|
"""
|
|
303
323
|
if pprint:
|
|
304
|
-
|
|
324
|
+
table = missions_to_table(missions)
|
|
325
|
+
Console().print(table)
|
|
305
326
|
else:
|
|
306
327
|
for mission in missions:
|
|
307
328
|
print(mission.id)
|
|
@@ -313,7 +334,8 @@ def print_projects(projects: Sequence[Project], *, pprint: bool) -> None:
|
|
|
313
334
|
either using pprint or as a list for piping
|
|
314
335
|
"""
|
|
315
336
|
if pprint:
|
|
316
|
-
|
|
337
|
+
table = projects_to_table(projects)
|
|
338
|
+
Console().print(table)
|
|
317
339
|
else:
|
|
318
340
|
for project in projects:
|
|
319
341
|
print(project.id)
|
|
@@ -325,7 +347,8 @@ def print_file_info(file: File, *, pprint: bool) -> None:
|
|
|
325
347
|
either using pprint or as a list for piping
|
|
326
348
|
"""
|
|
327
349
|
if pprint:
|
|
328
|
-
|
|
350
|
+
table = file_info_table(file)
|
|
351
|
+
Console().print(table)
|
|
329
352
|
else:
|
|
330
353
|
file_dct = asdict(file)
|
|
331
354
|
for key in file_dct:
|
kleinkram/utils.py
CHANGED
|
@@ -40,10 +40,7 @@ def file_paths_from_files(
|
|
|
40
40
|
determines the destinations for a sequence of `File` objects,
|
|
41
41
|
possibly nested by project and mission
|
|
42
42
|
"""
|
|
43
|
-
if (
|
|
44
|
-
len(set([(file.project_id, file.mission_id) for file in files])) > 1
|
|
45
|
-
and not allow_nested
|
|
46
|
-
):
|
|
43
|
+
if len(set([file.mission_id for file in files])) > 1 and not allow_nested:
|
|
47
44
|
raise ValueError("files from multiple missions were selected")
|
|
48
45
|
elif not allow_nested:
|
|
49
46
|
return {dest / file.name: file for file in files}
|
|
@@ -2,18 +2,18 @@ kleinkram/__init__.py,sha256=xIJqTJw2kbCGryGlCeAdpmtR1FTxmrW1MklUNQEaj74,1061
|
|
|
2
2
|
kleinkram/__main__.py,sha256=B9RiZxfO4jpCmWPUHyKJ7_EoZlEG4sPpH-nz7T_YhhQ,125
|
|
3
3
|
kleinkram/_version.py,sha256=QYJyRTcqFcJj4qWYpqs7WcoOP6jxDMqyvxLY-cD6KcE,129
|
|
4
4
|
kleinkram/auth.py,sha256=XD_rHOyJmYYfO7QJf3TLYH5qXA22gXGWi7PT3jujlVs,2968
|
|
5
|
-
kleinkram/config.py,sha256=
|
|
5
|
+
kleinkram/config.py,sha256=_ZfOSfEZaK8ZbgoNRZL2h3ozZfxHnKqmLJ9mUIYOxBU,7413
|
|
6
6
|
kleinkram/core.py,sha256=Q7OYIKPN9K6kxf9Eq7r5XRHPJ3RtT7SBZp_3_CS8yuY,8429
|
|
7
7
|
kleinkram/errors.py,sha256=4mygNxkf6IBgaiRWY95qu0v6z4TAXA3G6CUcXC9FU3s,772
|
|
8
8
|
kleinkram/main.py,sha256=BTE0mZN__xd46wBhFi6iBlK9eGGQvJ1LdUMsbnysLi0,172
|
|
9
9
|
kleinkram/models.py,sha256=8nJlPrKVLSmehspeuQSFV6nUo76JzehUn6KIZYH1xy4,1832
|
|
10
|
-
kleinkram/printing.py,sha256=
|
|
10
|
+
kleinkram/printing.py,sha256=fpkJFeVp5pzZ_cZY6nzYDV_qDFWHKHxiW-DaH2PPNeY,12160
|
|
11
11
|
kleinkram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
kleinkram/types.py,sha256=nfDjj8TB1Jn5vqO0Xg6qhLOuKom9DDhe62BrngqnVGM,185
|
|
13
|
-
kleinkram/utils.py,sha256=
|
|
13
|
+
kleinkram/utils.py,sha256=fFQsq5isLjDC2Z-XUTiJzz30Wt9rFUi4391WXstusG0,6221
|
|
14
14
|
kleinkram/wrappers.py,sha256=4xXU43eNnvMG2sssU330MmTLSSRdurOpnZ-zNGOGmt0,11342
|
|
15
15
|
kleinkram/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
kleinkram/api/client.py,sha256=
|
|
16
|
+
kleinkram/api/client.py,sha256=1mQuguyYM3ghrADTlivQi9ybWjJzlb9emto5rPtJRkM,5112
|
|
17
17
|
kleinkram/api/deser.py,sha256=xRpYUFKZ0Luoo7XyAtYblJvprmpjNSZOiFVnFKmOzcM,4819
|
|
18
18
|
kleinkram/api/file_transfer.py,sha256=3wNlVQdjnRtxOzih5HhCTF18xPbYClFIDxCqbwkLl6c,12985
|
|
19
19
|
kleinkram/api/pagination.py,sha256=P_zPsBKlMWkmAv-YfUNHaGW-XLB_4U8BDMrKyiDFIXk,1370
|
|
@@ -28,23 +28,23 @@ kleinkram/cli/_mission.py,sha256=zDFnOozOFckpuREFgIPt1IzG5q3b1bsNxYlWQoHoz5A,530
|
|
|
28
28
|
kleinkram/cli/_project.py,sha256=N0C96NC_onCEwTteYp2wgkkwkdJt-1q43LFdqNXfjC8,3398
|
|
29
29
|
kleinkram/cli/_upload.py,sha256=gOhbjbmqhmwW7p6bWlSvI53vLHvBFO9QqD1kdU92I2k,2813
|
|
30
30
|
kleinkram/cli/_verify.py,sha256=0ABVa4U_WzaV36ClR8NsOIG7KAMRlnFmsbtnHhbWVj4,1742
|
|
31
|
-
kleinkram/cli/app.py,sha256=
|
|
31
|
+
kleinkram/cli/app.py,sha256=m2qq4z95QllvXnxh3koPp0kq06I5R9etsJV8qSV8TMk,7037
|
|
32
32
|
kleinkram/cli/error_handling.py,sha256=wK3tzeKVSrZm-xmiyzGLnGT2E4TRpyxhaak6GWGP7P8,1921
|
|
33
33
|
testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
testing/backend_fixtures.py,sha256=t5QWwyezHUhxxAlbUuE_eFmpyRaGbnWNNcGPwrO17JM,1571
|
|
35
35
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
tests/conftest.py,sha256=5MLYQOtQoXWl0TRkYntYKNdqpd4hl9m0XTRi5OXanYI,104
|
|
37
|
-
tests/test_config.py,sha256=
|
|
37
|
+
tests/test_config.py,sha256=BGIXTX0XRF634_NMG-ya8v0zruw4PZCZtFGhYVK4DCM,5867
|
|
38
38
|
tests/test_core.py,sha256=JbzB05LWmaaP77uXeTOQtCJD2AJT0zO9zhDfcZ3GNH8,5139
|
|
39
39
|
tests/test_end_to_end.py,sha256=0W5pUES5hek-pXq4NZtpPZqKTORkGCRsDv5_D3rDMjY,3372
|
|
40
40
|
tests/test_error_handling.py,sha256=qPSMKF1qsAHyUME0-krxbIrk38iGKkhAyAah-KwN4NE,1300
|
|
41
41
|
tests/test_fixtures.py,sha256=UlPmGbEsGvrDPsaStGMRjNvrVPGjCqOB0RMfLJq2VRA,1071
|
|
42
|
-
tests/test_printing.py,sha256=
|
|
42
|
+
tests/test_printing.py,sha256=Jz1AjqmqBRjp1JLm6H1oVJyvGaMPlahVXdKnd7UDQFc,2231
|
|
43
43
|
tests/test_query.py,sha256=fExmCKXLA7-9j2S2sF_sbvRX_2s6Cp3a7OTcqE25q9g,3864
|
|
44
44
|
tests/test_utils.py,sha256=eUBYrn3xrcgcaxm1X4fqZaX4tRvkbI6rh6BUbNbu9T0,4784
|
|
45
45
|
tests/test_wrappers.py,sha256=TbcTyO2L7fslbzgfDdcVZkencxNQ8cGPZm_iB6c9d6Q,2673
|
|
46
|
-
kleinkram-0.
|
|
47
|
-
kleinkram-0.
|
|
48
|
-
kleinkram-0.
|
|
49
|
-
kleinkram-0.
|
|
50
|
-
kleinkram-0.
|
|
46
|
+
kleinkram-0.40.0.dist-info/METADATA,sha256=7Ju-psj6tQUURUoDiWIhRtsRw-DqAK7_cFD9lTuSy4g,2742
|
|
47
|
+
kleinkram-0.40.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
48
|
+
kleinkram-0.40.0.dist-info/entry_points.txt,sha256=SaB2l5aqhSr8gmaMw2kvQU90a8Bnl7PedU8cWYxkfYo,46
|
|
49
|
+
kleinkram-0.40.0.dist-info/top_level.txt,sha256=N3-sJagEHu1Tk1X6Dx1X1q0pLDNbDZpLzRxVftvepds,24
|
|
50
|
+
kleinkram-0.40.0.dist-info/RECORD,,
|
tests/test_config.py
CHANGED
|
@@ -7,6 +7,7 @@ from unittest import mock
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
+
import kleinkram.config
|
|
10
11
|
from kleinkram.config import ACTION_API
|
|
11
12
|
from kleinkram.config import ACTION_API_KEY
|
|
12
13
|
from kleinkram.config import ACTION_S3
|
|
@@ -102,9 +103,26 @@ def test_save_and_load_config(config_path):
|
|
|
102
103
|
config = Config(version="foo")
|
|
103
104
|
|
|
104
105
|
assert not config_path.exists()
|
|
105
|
-
|
|
106
|
+
with mock.patch.object(kleinkram.config.logger, "warning") as mock_warning:
|
|
107
|
+
save_config(config, path=config_path)
|
|
108
|
+
mock_warning.assert_not_called()
|
|
109
|
+
|
|
106
110
|
assert config_path.exists()
|
|
111
|
+
loaded_config = _load_config(path=config_path)
|
|
112
|
+
assert loaded_config == config
|
|
113
|
+
|
|
107
114
|
|
|
115
|
+
def test_save_and_load_config_when_tmpfile_fails(config_path):
|
|
116
|
+
config = Config(version="foo")
|
|
117
|
+
|
|
118
|
+
assert not config_path.exists()
|
|
119
|
+
with mock.patch("tempfile.mkstemp", side_effect=Exception), mock.patch.object(
|
|
120
|
+
kleinkram.config.logger, "warning"
|
|
121
|
+
) as mock_warning:
|
|
122
|
+
save_config(config, path=config_path)
|
|
123
|
+
mock_warning.assert_called_once()
|
|
124
|
+
|
|
125
|
+
assert config_path.exists()
|
|
108
126
|
loaded_config = _load_config(path=config_path)
|
|
109
127
|
assert loaded_config == config
|
|
110
128
|
|
tests/test_printing.py
CHANGED
|
@@ -3,9 +3,11 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
|
+
from rich.table import Table
|
|
6
7
|
|
|
7
8
|
from kleinkram.models import MetadataValue
|
|
8
9
|
from kleinkram.models import MetadataValueType
|
|
10
|
+
from kleinkram.printing import _add_placeholder_row
|
|
9
11
|
from kleinkram.printing import format_bytes
|
|
10
12
|
from kleinkram.printing import parse_metadata_value
|
|
11
13
|
|
|
@@ -23,6 +25,15 @@ def test_format_bytes():
|
|
|
23
25
|
assert format_bytes(2**50) == "1.00 PB"
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
def test_add_placeholder_row():
|
|
29
|
+
table = Table("foo", "bar")
|
|
30
|
+
_add_placeholder_row(table, skipped=1)
|
|
31
|
+
|
|
32
|
+
assert table.row_count == 1
|
|
33
|
+
assert table.columns[0]._cells[-1] == "... (1 more)"
|
|
34
|
+
assert table.columns[1]._cells[-1] == "..."
|
|
35
|
+
|
|
36
|
+
|
|
26
37
|
def test_parse_metadata_value():
|
|
27
38
|
mv = MetadataValue(type_=MetadataValueType.STRING, value="foo")
|
|
28
39
|
assert parse_metadata_value(mv) == "foo"
|
{kleinkram-0.39.0.dev20250224101309.dist-info → kleinkram-0.40.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|