parq-cli 0.2.1__tar.gz → 0.2.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.
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.gitignore +3 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/CHANGELOG.md +8 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/PKG-INFO +3 -2
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/cli.py +26 -12
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/_common.py +1 -1
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/_xlsx.py +24 -4
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/output.py +24 -5
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/plain_output.py +26 -4
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/reader.py +18 -1
- {parq_cli-0.2.1 → parq_cli-0.2.2}/pyproject.toml +3 -2
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_cli.py +25 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_output.py +33 -0
- parq_cli-0.2.2/tests/test_output_not_input.py +26 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_plain_output.py +18 -0
- parq_cli-0.2.2/tests/test_xlsx_defused.py +31 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/uv.lock +101 -87
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.claude/settings.json +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.github/workflows/publish.yml +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.github/workflows/test.yml +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.python-version +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.tmp-bench/csv-large.csv +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.tmp-bench/csv-small.csv +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.tmp-bench/parquet-large.parquet +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/.tmp-bench/parquet-small.parquet +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/AGENTS.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/CLAUDE.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/LICENSE +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/README.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/README_CN.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/data/test.parquet +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/docs/QUICKSTART.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/docs/RELEASE.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/examples/README.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/examples/create_sample_data.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/__init__.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/__main__.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/__init__.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/_chunk_writers.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/_csv.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/parq/formats/_parquet.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/scripts/README.md +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/scripts/bump_version.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/scripts/check_version.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/scripts/performance_benchmark.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/scripts/publish.sh +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/__init__.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/conftest.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_main_entry.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_package_meta.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_performance.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_reader.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_release_scripts.py +0 -0
- {parq_cli-0.2.1 → parq_cli-0.2.2}/tests/test_workflows.py +0 -0
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
- `merge --force` and `convert` now refuse an output path that is one of the inputs. Previously `--force` deleted that input before reading it.
|
|
6
|
+
- XLSX workbook XML is parsed with `defusedxml` (added to the `xlsx` extra).
|
|
7
|
+
- Table output escapes cell markup and summarizes binary values (#4).
|
|
8
|
+
- Errors include the file path (#5).
|
|
9
|
+
- Dependency security updates: certifi, urllib3, idna, pygments, cryptography, pyarrow.
|
|
10
|
+
|
|
3
11
|
## 0.2.1
|
|
4
12
|
|
|
5
13
|
- `diff` and `merge` honor the global `--delimiter` and `--sheet` options.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: parq-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A command-line tool for inspecting tabular files: Parquet, CSV, TSV, and XLSX
|
|
5
5
|
Project-URL: Homepage, https://github.com/Tendo33/parq-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/Tendo33/parq-cli
|
|
@@ -26,10 +26,11 @@ Provides-Extra: dev
|
|
|
26
26
|
Requires-Dist: build>=1.2.0; extra == 'dev'
|
|
27
27
|
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
28
28
|
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
29
|
-
Requires-Dist: ruff
|
|
29
|
+
Requires-Dist: ruff==0.14.0; extra == 'dev'
|
|
30
30
|
Requires-Dist: twine>=5.1.0; extra == 'dev'
|
|
31
31
|
Requires-Dist: vulture>=2.14; extra == 'dev'
|
|
32
32
|
Provides-Extra: xlsx
|
|
33
|
+
Requires-Dist: defusedxml>=0.7.1; extra == 'xlsx'
|
|
33
34
|
Requires-Dist: openpyxl>=3.1.0; extra == 'xlsx'
|
|
34
35
|
Requires-Dist: python-calamine>=0.8.0; extra == 'xlsx'
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
@@ -129,18 +129,20 @@ def _run_with_error_handling(
|
|
|
129
129
|
operation: Callable[[Any], None],
|
|
130
130
|
*,
|
|
131
131
|
generic_error_prefix: str,
|
|
132
|
+
file_path: Optional[Path] = None,
|
|
132
133
|
) -> None:
|
|
133
134
|
"""Execute a CLI operation with consistent error handling."""
|
|
135
|
+
path_text = str(file_path) if file_path is not None else None
|
|
134
136
|
formatter = _get_formatter(ctx)
|
|
135
137
|
try:
|
|
136
138
|
operation(formatter)
|
|
137
139
|
except typer.Exit:
|
|
138
140
|
raise
|
|
139
141
|
except (FileNotFoundError, ValueError, FileExistsError) as e:
|
|
140
|
-
formatter.print_error(str(e))
|
|
142
|
+
formatter.print_error(str(e), file_path=path_text)
|
|
141
143
|
raise typer.Exit(code=1) from e
|
|
142
144
|
except Exception as e:
|
|
143
|
-
formatter.print_error(f"{generic_error_prefix}: {e}")
|
|
145
|
+
formatter.print_error(f"{generic_error_prefix}: {e}", file_path=path_text)
|
|
144
146
|
raise typer.Exit(code=1) from e
|
|
145
147
|
|
|
146
148
|
|
|
@@ -208,7 +210,9 @@ def meta(
|
|
|
208
210
|
reader = _get_reader(str(file), _get_state(ctx))
|
|
209
211
|
formatter.print_metadata(reader.get_metadata_dict(fast=fast))
|
|
210
212
|
|
|
211
|
-
_run_with_error_handling(
|
|
213
|
+
_run_with_error_handling(
|
|
214
|
+
ctx, operation, generic_error_prefix="Failed to read file", file_path=file
|
|
215
|
+
)
|
|
212
216
|
|
|
213
217
|
|
|
214
218
|
@app.command()
|
|
@@ -226,7 +230,9 @@ def schema(
|
|
|
226
230
|
reader = _get_reader(str(file), _get_state(ctx))
|
|
227
231
|
formatter.print_schema(reader.get_schema_info())
|
|
228
232
|
|
|
229
|
-
_run_with_error_handling(
|
|
233
|
+
_run_with_error_handling(
|
|
234
|
+
ctx, operation, generic_error_prefix="Failed to read file", file_path=file
|
|
235
|
+
)
|
|
230
236
|
|
|
231
237
|
|
|
232
238
|
@app.command()
|
|
@@ -250,7 +256,9 @@ def head(
|
|
|
250
256
|
table = reader.read_head(n, columns=_parse_column_list(columns))
|
|
251
257
|
formatter.print_table(table, f"First {n} Rows")
|
|
252
258
|
|
|
253
|
-
_run_with_error_handling(
|
|
259
|
+
_run_with_error_handling(
|
|
260
|
+
ctx, operation, generic_error_prefix="Failed to read file", file_path=file
|
|
261
|
+
)
|
|
254
262
|
|
|
255
263
|
|
|
256
264
|
@app.command()
|
|
@@ -274,7 +282,9 @@ def tail(
|
|
|
274
282
|
table = reader.read_tail(n, columns=_parse_column_list(columns))
|
|
275
283
|
formatter.print_table(table, f"Last {n} Rows")
|
|
276
284
|
|
|
277
|
-
_run_with_error_handling(
|
|
285
|
+
_run_with_error_handling(
|
|
286
|
+
ctx, operation, generic_error_prefix="Failed to read file", file_path=file
|
|
287
|
+
)
|
|
278
288
|
|
|
279
289
|
|
|
280
290
|
@app.command()
|
|
@@ -292,7 +302,9 @@ def count(
|
|
|
292
302
|
reader = _get_reader(str(file), _get_state(ctx))
|
|
293
303
|
formatter.print_count(reader.num_rows)
|
|
294
304
|
|
|
295
|
-
_run_with_error_handling(
|
|
305
|
+
_run_with_error_handling(
|
|
306
|
+
ctx, operation, generic_error_prefix="Failed to read file", file_path=file
|
|
307
|
+
)
|
|
296
308
|
|
|
297
309
|
|
|
298
310
|
@app.command()
|
|
@@ -397,10 +409,10 @@ def split(
|
|
|
397
409
|
except typer.Exit:
|
|
398
410
|
raise
|
|
399
411
|
except (FileNotFoundError, ValueError, FileExistsError) as e:
|
|
400
|
-
formatter.print_error(str(e))
|
|
412
|
+
formatter.print_error(str(e), file_path=str(file))
|
|
401
413
|
raise typer.Exit(code=1) from e
|
|
402
414
|
except Exception as e:
|
|
403
|
-
formatter.print_error(f"Failed to split file: {e}")
|
|
415
|
+
formatter.print_error(f"Failed to split file: {e}", file_path=str(file))
|
|
404
416
|
raise typer.Exit(code=1) from e
|
|
405
417
|
|
|
406
418
|
|
|
@@ -430,7 +442,9 @@ def stats(
|
|
|
430
442
|
reader.get_stats(columns=_parse_column_list(columns), limit=limit, top_n=top_n)
|
|
431
443
|
)
|
|
432
444
|
|
|
433
|
-
_run_with_error_handling(
|
|
445
|
+
_run_with_error_handling(
|
|
446
|
+
ctx, operation, generic_error_prefix="Failed to compute stats", file_path=file
|
|
447
|
+
)
|
|
434
448
|
|
|
435
449
|
|
|
436
450
|
@app.command()
|
|
@@ -511,10 +525,10 @@ def convert(
|
|
|
511
525
|
except typer.Exit:
|
|
512
526
|
raise
|
|
513
527
|
except (FileNotFoundError, ValueError, FileExistsError) as e:
|
|
514
|
-
formatter.print_error(str(e))
|
|
528
|
+
formatter.print_error(str(e), file_path=str(source))
|
|
515
529
|
raise typer.Exit(code=1) from e
|
|
516
530
|
except Exception as e:
|
|
517
|
-
formatter.print_error(f"Failed to convert file: {e}")
|
|
531
|
+
formatter.print_error(f"Failed to convert file: {e}", file_path=str(source))
|
|
518
532
|
raise typer.Exit(code=1) from e
|
|
519
533
|
|
|
520
534
|
|
|
@@ -218,7 +218,7 @@ def _compute_stats_from_batches(
|
|
|
218
218
|
values_list = vc.field("values").to_pylist()
|
|
219
219
|
counts_list = vc.field("counts").to_pylist()
|
|
220
220
|
value_counts = state["_value_counts"]
|
|
221
|
-
for value, count in zip(values_list, counts_list):
|
|
221
|
+
for value, count in zip(values_list, counts_list, strict=True):
|
|
222
222
|
if value is None:
|
|
223
223
|
continue
|
|
224
224
|
key = str(value)
|
|
@@ -6,7 +6,6 @@ Reading uses python-calamine. Writing stays on openpyxl in the chunk writer.
|
|
|
6
6
|
|
|
7
7
|
import math
|
|
8
8
|
import zipfile
|
|
9
|
-
import xml.etree.ElementTree as ET
|
|
10
9
|
from datetime import date, datetime, time
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from typing import Any, Iterator, List, Optional
|
|
@@ -46,8 +45,22 @@ def _require_calamine() -> Any:
|
|
|
46
45
|
return python_calamine
|
|
47
46
|
|
|
48
47
|
|
|
48
|
+
def _require_defused_element_tree() -> Any:
|
|
49
|
+
"""Import defusedxml lazily; workbook XML is untrusted input."""
|
|
50
|
+
try:
|
|
51
|
+
import defusedxml.ElementTree as defused_et
|
|
52
|
+
except ImportError as e:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"XLSX support requires 'defusedxml'. Install it with: pip install 'parq-cli[xlsx]'"
|
|
55
|
+
) from e
|
|
56
|
+
return defused_et
|
|
57
|
+
|
|
58
|
+
|
|
49
59
|
def _active_sheet_index(file_path: Path) -> int:
|
|
50
60
|
"""Return the workbook's active sheet index without parsing every cell."""
|
|
61
|
+
ET = _require_defused_element_tree()
|
|
62
|
+
from defusedxml import DefusedXmlException
|
|
63
|
+
|
|
51
64
|
try:
|
|
52
65
|
with zipfile.ZipFile(file_path) as workbook_zip:
|
|
53
66
|
root = ET.fromstring(workbook_zip.read("xl/workbook.xml"))
|
|
@@ -55,7 +68,14 @@ def _active_sheet_index(file_path: Path) -> int:
|
|
|
55
68
|
if view is None:
|
|
56
69
|
return 0
|
|
57
70
|
return int(view.attrib.get("activeTab", "0"))
|
|
58
|
-
except (
|
|
71
|
+
except (
|
|
72
|
+
KeyError,
|
|
73
|
+
ValueError,
|
|
74
|
+
ET.ParseError,
|
|
75
|
+
DefusedXmlException,
|
|
76
|
+
zipfile.BadZipFile,
|
|
77
|
+
OSError,
|
|
78
|
+
):
|
|
59
79
|
return 0
|
|
60
80
|
|
|
61
81
|
|
|
@@ -172,7 +192,7 @@ def _schema_from_types(headers: List[str], inferred_types: List[Optional[pa.Data
|
|
|
172
192
|
return pa.schema(
|
|
173
193
|
[
|
|
174
194
|
pa.field(name, inferred_type or pa.null(), nullable=True)
|
|
175
|
-
for name, inferred_type in zip(headers, inferred_types)
|
|
195
|
+
for name, inferred_type in zip(headers, inferred_types, strict=True)
|
|
176
196
|
]
|
|
177
197
|
)
|
|
178
198
|
|
|
@@ -275,7 +295,7 @@ def _iter_row_batches(
|
|
|
275
295
|
row[idx] if idx < len(row) else None,
|
|
276
296
|
field,
|
|
277
297
|
)
|
|
278
|
-
for column, idx, field in zip(selected_columns, selected_indices, selected_fields)
|
|
298
|
+
for column, idx, field in zip(selected_columns, selected_indices, selected_fields, strict=True)
|
|
279
299
|
}
|
|
280
300
|
)
|
|
281
301
|
if len(buffered_rows) >= batch_size:
|
|
@@ -5,14 +5,17 @@ Handles pretty-printing of Parquet data and metadata.
|
|
|
5
5
|
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Dict, List
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
9
|
|
|
10
10
|
import pyarrow as pa
|
|
11
11
|
from rich import box
|
|
12
12
|
from rich.console import Console
|
|
13
|
+
from rich.markup import escape
|
|
13
14
|
from rich.panel import Panel
|
|
14
15
|
from rich.table import Table
|
|
15
16
|
|
|
17
|
+
from parq.plain_output import summarize_binary as _summarize_cell
|
|
18
|
+
|
|
16
19
|
console = Console()
|
|
17
20
|
|
|
18
21
|
|
|
@@ -37,6 +40,17 @@ def _safe_text(text: str, fallback: str) -> str:
|
|
|
37
40
|
return text if _supports_text(text) else fallback
|
|
38
41
|
|
|
39
42
|
|
|
43
|
+
_MAX_CELL_CHARS = 200
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _format_cell(value: Any) -> str:
|
|
47
|
+
"""Render a table cell as markup-safe, length-limited text."""
|
|
48
|
+
text = str(_summarize_cell(value))
|
|
49
|
+
if len(text) > _MAX_CELL_CHARS:
|
|
50
|
+
text = text[: _MAX_CELL_CHARS - 3] + "..."
|
|
51
|
+
return escape(text)
|
|
52
|
+
|
|
53
|
+
|
|
40
54
|
class OutputFormatter:
|
|
41
55
|
"""Formatter for displaying tabular data and metadata."""
|
|
42
56
|
|
|
@@ -123,7 +137,10 @@ class OutputFormatter:
|
|
|
123
137
|
batch_dict = batch.to_pydict()
|
|
124
138
|
for row_idx in range(len(batch)):
|
|
125
139
|
table.add_row(
|
|
126
|
-
*[
|
|
140
|
+
*[
|
|
141
|
+
_format_cell(batch_dict[col_name][row_idx])
|
|
142
|
+
for col_name in arrow_table.column_names
|
|
143
|
+
]
|
|
127
144
|
)
|
|
128
145
|
|
|
129
146
|
console.print(table)
|
|
@@ -140,10 +157,12 @@ class OutputFormatter:
|
|
|
140
157
|
console.print(panel)
|
|
141
158
|
|
|
142
159
|
@staticmethod
|
|
143
|
-
def print_error(message: str) -> None:
|
|
144
|
-
"""Print error message."""
|
|
160
|
+
def print_error(message: str, file_path: Optional[str] = None) -> None:
|
|
161
|
+
"""Print error message, prefixed with the offending file when known."""
|
|
162
|
+
if file_path and file_path not in message:
|
|
163
|
+
message = f"{file_path}: {message}"
|
|
145
164
|
console.print(
|
|
146
|
-
f"[bold red]{_safe_text(chr(0x274C) + ' Error:', 'Error:')}[/bold red] {message}"
|
|
165
|
+
f"[bold red]{_safe_text(chr(0x274C) + ' Error:', 'Error:')}[/bold red] {escape(message)}"
|
|
147
166
|
)
|
|
148
167
|
|
|
149
168
|
@staticmethod
|
|
@@ -4,11 +4,24 @@ import csv
|
|
|
4
4
|
import json
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Dict, List
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
8
|
|
|
9
9
|
import pyarrow as pa
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def summarize_binary(value: Any) -> Any:
|
|
13
|
+
"""Replace raw binary payloads (also inside structs/lists) with a short summary."""
|
|
14
|
+
if isinstance(value, (bytes, bytearray, memoryview)):
|
|
15
|
+
return f"<binary {len(bytes(value))} bytes>"
|
|
16
|
+
if isinstance(value, dict):
|
|
17
|
+
return {key: summarize_binary(item) for key, item in value.items()}
|
|
18
|
+
if isinstance(value, list):
|
|
19
|
+
return [summarize_binary(item) for item in value]
|
|
20
|
+
if isinstance(value, tuple):
|
|
21
|
+
return tuple(summarize_binary(item) for item in value)
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
|
|
12
25
|
class PlainOutputFormatter:
|
|
13
26
|
"""Tab-separated plain text output with minimal overhead."""
|
|
14
27
|
|
|
@@ -20,6 +33,10 @@ class PlainOutputFormatter:
|
|
|
20
33
|
def _normalize_value(value: Any) -> Any:
|
|
21
34
|
if value is None:
|
|
22
35
|
return ""
|
|
36
|
+
if isinstance(value, (bytes, bytearray, memoryview, dict, list, tuple)):
|
|
37
|
+
value = summarize_binary(value)
|
|
38
|
+
if not isinstance(value, str):
|
|
39
|
+
value = str(value)
|
|
23
40
|
if isinstance(value, str):
|
|
24
41
|
return (
|
|
25
42
|
value.replace("\\", "\\\\")
|
|
@@ -68,7 +85,9 @@ class PlainOutputFormatter:
|
|
|
68
85
|
print(count)
|
|
69
86
|
|
|
70
87
|
@staticmethod
|
|
71
|
-
def print_error(message: str) -> None:
|
|
88
|
+
def print_error(message: str, file_path: Optional[str] = None) -> None:
|
|
89
|
+
if file_path and file_path not in message:
|
|
90
|
+
message = f"{file_path}: {message}"
|
|
72
91
|
print(f"Error: {message}", file=sys.stderr)
|
|
73
92
|
|
|
74
93
|
@staticmethod
|
|
@@ -191,8 +210,11 @@ class JsonOutputFormatter:
|
|
|
191
210
|
print(json.dumps({"count": count}))
|
|
192
211
|
|
|
193
212
|
@staticmethod
|
|
194
|
-
def print_error(message: str) -> None:
|
|
195
|
-
|
|
213
|
+
def print_error(message: str, file_path: Optional[str] = None) -> None:
|
|
214
|
+
payload: Dict[str, Any] = {"error": message}
|
|
215
|
+
if file_path is not None:
|
|
216
|
+
payload["file_path"] = file_path
|
|
217
|
+
print(json.dumps(payload), file=sys.stderr)
|
|
196
218
|
|
|
197
219
|
@staticmethod
|
|
198
220
|
def print_success(message: str) -> None:
|
|
@@ -419,8 +419,9 @@ class MultiFormatReader:
|
|
|
419
419
|
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
420
420
|
) -> int:
|
|
421
421
|
"""Convert the input file to a single supported output file."""
|
|
422
|
-
self._ensure_xlsx_types_for_full_read()
|
|
423
422
|
output_path = Path(output_path)
|
|
423
|
+
_reject_output_matching_input(output_path, [self.file_path])
|
|
424
|
+
self._ensure_xlsx_types_for_full_read()
|
|
424
425
|
if self._parquet_reader is not None:
|
|
425
426
|
rows_written = self._parquet_reader.convert_file(
|
|
426
427
|
output_path, columns=columns, force=force, progress_callback=progress_callback
|
|
@@ -921,6 +922,21 @@ def diff_files(
|
|
|
921
922
|
}
|
|
922
923
|
|
|
923
924
|
|
|
925
|
+
def _reject_output_matching_input(
|
|
926
|
+
output_path: Path, input_paths: List[Union[str, Path]]
|
|
927
|
+
) -> None:
|
|
928
|
+
"""Refuse to write onto an input file (``--force`` would delete it first)."""
|
|
929
|
+
if not output_path.exists():
|
|
930
|
+
return
|
|
931
|
+
for input_path in input_paths:
|
|
932
|
+
try:
|
|
933
|
+
same = output_path.samefile(input_path)
|
|
934
|
+
except OSError:
|
|
935
|
+
continue
|
|
936
|
+
if same:
|
|
937
|
+
raise ValueError(f"Output file must not be one of the inputs: {output_path}")
|
|
938
|
+
|
|
939
|
+
|
|
924
940
|
def merge_files(
|
|
925
941
|
input_paths: List[Union[str, Path]],
|
|
926
942
|
output_path: Union[str, Path],
|
|
@@ -934,6 +950,7 @@ def merge_files(
|
|
|
934
950
|
raise ValueError("At least one input file must be provided")
|
|
935
951
|
|
|
936
952
|
output_path = Path(output_path)
|
|
953
|
+
_reject_output_matching_input(output_path, input_paths)
|
|
937
954
|
if output_path.exists():
|
|
938
955
|
if force:
|
|
939
956
|
output_path.unlink()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "parq-cli"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "A command-line tool for inspecting tabular files: Parquet, CSV, TSV, and XLSX"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -38,12 +38,13 @@ parq = "parq.cli:app"
|
|
|
38
38
|
xlsx = [
|
|
39
39
|
"openpyxl>=3.1.0",
|
|
40
40
|
"python-calamine>=0.8.0",
|
|
41
|
+
"defusedxml>=0.7.1",
|
|
41
42
|
]
|
|
42
43
|
dev = [
|
|
43
44
|
"build>=1.2.0",
|
|
44
45
|
"pytest>=8.0.0",
|
|
45
46
|
"pytest-cov>=4.1.0",
|
|
46
|
-
"ruff
|
|
47
|
+
"ruff==0.14.0",
|
|
47
48
|
"twine>=5.1.0",
|
|
48
49
|
"vulture>=2.14",
|
|
49
50
|
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Tests for CLI commands.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
@@ -808,3 +809,27 @@ class TestCLI:
|
|
|
808
809
|
assert "--output" in plain
|
|
809
810
|
assert "--delimiter" in plain
|
|
810
811
|
assert "--sheet" in plain
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def test_json_error_includes_file_path(tmp_path):
|
|
815
|
+
"""Errors in JSON mode carry the offending file path (issue #5)."""
|
|
816
|
+
bad = tmp_path / "broken.parquet"
|
|
817
|
+
bad.write_bytes(b"not a parquet file")
|
|
818
|
+
|
|
819
|
+
result = runner.invoke(app, ["-o", "json", "meta", str(bad)])
|
|
820
|
+
|
|
821
|
+
assert result.exit_code == 1
|
|
822
|
+
payload = json.loads(result.stderr.strip().splitlines()[-1])
|
|
823
|
+
assert payload["file_path"] == str(bad)
|
|
824
|
+
assert "error" in payload
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def test_text_error_mentions_file_path(tmp_path):
|
|
828
|
+
"""Errors in plain mode mention the offending file path (issue #5)."""
|
|
829
|
+
bad = tmp_path / "broken.parquet"
|
|
830
|
+
bad.write_bytes(b"not a parquet file")
|
|
831
|
+
|
|
832
|
+
result = runner.invoke(app, ["-o", "plain", "head", str(bad)])
|
|
833
|
+
|
|
834
|
+
assert result.exit_code == 1
|
|
835
|
+
assert str(bad) in result.stderr
|
|
@@ -73,3 +73,36 @@ def test_print_split_result_renders_existing_files(monkeypatch, tmp_path):
|
|
|
73
73
|
assert "Output Files" in text
|
|
74
74
|
assert "5 B" in text
|
|
75
75
|
assert "part-001.parquet" not in text
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_print_table_handles_binary_values(monkeypatch):
|
|
79
|
+
"""Binary cells (issue #4) must not be parsed as Rich markup or dumped in full."""
|
|
80
|
+
import pyarrow as pa
|
|
81
|
+
|
|
82
|
+
console = _recording_console()
|
|
83
|
+
monkeypatch.setattr(output_module, "console", console)
|
|
84
|
+
payload = b"\x87[/<\x97\xf6]" * 100
|
|
85
|
+
table = pa.table(
|
|
86
|
+
{
|
|
87
|
+
"raw": pa.array([payload], type=pa.binary()),
|
|
88
|
+
"image": pa.array([{"bytes": payload, "path": "a.png"}]),
|
|
89
|
+
"label": ["[/bold]"],
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
OutputFormatter.print_table(table)
|
|
94
|
+
|
|
95
|
+
text = console.export_text()
|
|
96
|
+
assert "<binary 700 bytes>" in text
|
|
97
|
+
assert "a.png" in text
|
|
98
|
+
assert "[/bold]" in text
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_print_error_escapes_markup(monkeypatch):
|
|
102
|
+
"""Error messages containing Rich-like tags are printed literally."""
|
|
103
|
+
console = _recording_console()
|
|
104
|
+
monkeypatch.setattr(output_module, "console", console)
|
|
105
|
+
|
|
106
|
+
OutputFormatter.print_error("closing tag '[/<x]' at position 3")
|
|
107
|
+
|
|
108
|
+
assert "[/<x]" in console.export_text()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pyarrow as pa
|
|
2
|
+
import pyarrow.csv as pacsv
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from parq.reader import MultiFormatReader, merge_files
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_merge_force_refuses_to_overwrite_an_input(tmp_path):
|
|
9
|
+
left = tmp_path / "a.csv"
|
|
10
|
+
right = tmp_path / "b.csv"
|
|
11
|
+
pacsv.write_csv(pa.table({"id": [1]}), left)
|
|
12
|
+
pacsv.write_csv(pa.table({"id": [2]}), right)
|
|
13
|
+
|
|
14
|
+
with pytest.raises(ValueError, match="must not be one of the inputs"):
|
|
15
|
+
merge_files([left, right], left, force=True)
|
|
16
|
+
# The input must survive (previously --force unlinked it before reading).
|
|
17
|
+
assert pacsv.read_csv(left).num_rows == 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_convert_force_refuses_to_overwrite_its_input(tmp_path):
|
|
21
|
+
src = tmp_path / "a.csv"
|
|
22
|
+
pacsv.write_csv(pa.table({"id": [1, 2]}), src)
|
|
23
|
+
|
|
24
|
+
with pytest.raises(ValueError, match="must not be one of the inputs"):
|
|
25
|
+
MultiFormatReader(str(src)).convert_file(src, force=True)
|
|
26
|
+
assert pacsv.read_csv(src).num_rows == 2
|
|
@@ -56,3 +56,21 @@ class TestJsonOutputFormatter:
|
|
|
56
56
|
data = json.loads(capsys.readouterr().out)
|
|
57
57
|
assert len(data["rows"]) == 2
|
|
58
58
|
assert data["rows"][0]["id"] == 1
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_plain_table_summarizes_binary_values(capsys):
|
|
62
|
+
"""Binary cells (issue #4) are summarized instead of dumped in plain mode."""
|
|
63
|
+
import pyarrow as pa
|
|
64
|
+
|
|
65
|
+
payload = b"\x00\x01[/x]" * 10
|
|
66
|
+
table = pa.table(
|
|
67
|
+
{
|
|
68
|
+
"raw": pa.array([payload], type=pa.binary()),
|
|
69
|
+
"image": pa.array([{"bytes": payload, "path": "a.png"}]),
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
PlainOutputFormatter.print_table(table)
|
|
73
|
+
out = capsys.readouterr().out
|
|
74
|
+
assert "<binary 60 bytes>" in out
|
|
75
|
+
assert "a.png" in out
|
|
76
|
+
assert "\\x00" not in out
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import zipfile
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
pytest.importorskip("defusedxml")
|
|
6
|
+
|
|
7
|
+
from parq.formats._xlsx import _active_sheet_index # noqa: E402
|
|
8
|
+
|
|
9
|
+
_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _workbook(tmp_path, workbook_xml: str):
|
|
13
|
+
path = tmp_path / "book.xlsx"
|
|
14
|
+
with zipfile.ZipFile(path, "w") as zf:
|
|
15
|
+
zf.writestr("xl/workbook.xml", workbook_xml)
|
|
16
|
+
return path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_active_sheet_index_reads_active_tab(tmp_path):
|
|
20
|
+
xml = f'<workbook xmlns="{_NS}"><bookViews><workbookView activeTab="2"/></bookViews></workbook>'
|
|
21
|
+
assert _active_sheet_index(_workbook(tmp_path, xml)) == 2
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_active_sheet_index_rejects_entity_expansion(tmp_path):
|
|
25
|
+
xml = (
|
|
26
|
+
'<?xml version="1.0"?>'
|
|
27
|
+
'<!DOCTYPE workbook [<!ENTITY a "1"><!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">]>'
|
|
28
|
+
f'<workbook xmlns="{_NS}"><bookViews><workbookView activeTab="&b;"/></bookViews></workbook>'
|
|
29
|
+
)
|
|
30
|
+
# defusedxml refuses entity declarations; we fall back to the first sheet.
|
|
31
|
+
assert _active_sheet_index(_workbook(tmp_path, xml)) == 0
|
|
@@ -29,11 +29,11 @@ wheels = [
|
|
|
29
29
|
|
|
30
30
|
[[package]]
|
|
31
31
|
name = "certifi"
|
|
32
|
-
version = "2026.
|
|
32
|
+
version = "2026.10.23"
|
|
33
33
|
source = { registry = "https://pypi.org/simple" }
|
|
34
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
34
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" }
|
|
35
35
|
wheels = [
|
|
36
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
36
|
+
{ url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" },
|
|
37
37
|
]
|
|
38
38
|
|
|
39
39
|
[[package]]
|
|
@@ -309,45 +309,45 @@ toml = [
|
|
|
309
309
|
|
|
310
310
|
[[package]]
|
|
311
311
|
name = "cryptography"
|
|
312
|
-
version = "
|
|
312
|
+
version = "50.0.0"
|
|
313
313
|
source = { registry = "https://pypi.org/simple" }
|
|
314
314
|
dependencies = [
|
|
315
315
|
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
|
316
316
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
317
317
|
]
|
|
318
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
318
|
+
sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" }
|
|
319
319
|
wheels = [
|
|
320
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
321
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
322
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
323
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
324
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
325
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
326
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
327
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
328
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
329
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
330
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
331
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
332
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
333
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
334
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
335
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
336
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
337
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
338
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
339
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
340
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
341
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
342
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
343
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
344
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
345
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
346
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
347
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
348
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
349
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
350
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
320
|
+
{ url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" },
|
|
321
|
+
{ url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" },
|
|
322
|
+
{ url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" },
|
|
323
|
+
{ url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" },
|
|
324
|
+
{ url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" },
|
|
325
|
+
{ url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" },
|
|
326
|
+
{ url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" },
|
|
327
|
+
{ url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" },
|
|
328
|
+
{ url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" },
|
|
329
|
+
{ url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" },
|
|
330
|
+
{ url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" },
|
|
331
|
+
{ url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" },
|
|
332
|
+
{ url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" },
|
|
333
|
+
{ url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" },
|
|
334
|
+
{ url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" },
|
|
335
|
+
{ url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" },
|
|
336
|
+
{ url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" },
|
|
337
|
+
{ url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" },
|
|
338
|
+
{ url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" },
|
|
339
|
+
{ url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" },
|
|
340
|
+
{ url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" },
|
|
341
|
+
{ url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" },
|
|
342
|
+
{ url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" },
|
|
343
|
+
{ url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" },
|
|
344
|
+
{ url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" },
|
|
345
|
+
{ url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" },
|
|
346
|
+
{ url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" },
|
|
347
|
+
{ url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" },
|
|
348
|
+
{ url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" },
|
|
349
|
+
{ url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" },
|
|
350
|
+
{ url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" },
|
|
351
351
|
]
|
|
352
352
|
|
|
353
353
|
[[package]]
|
|
@@ -394,11 +394,11 @@ wheels = [
|
|
|
394
394
|
|
|
395
395
|
[[package]]
|
|
396
396
|
name = "idna"
|
|
397
|
-
version = "3.
|
|
397
|
+
version = "3.20"
|
|
398
398
|
source = { registry = "https://pypi.org/simple" }
|
|
399
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
399
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" }
|
|
400
400
|
wheels = [
|
|
401
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
401
|
+
{ url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" },
|
|
402
402
|
]
|
|
403
403
|
|
|
404
404
|
[[package]]
|
|
@@ -572,7 +572,7 @@ wheels = [
|
|
|
572
572
|
|
|
573
573
|
[[package]]
|
|
574
574
|
name = "parq-cli"
|
|
575
|
-
version = "0.2.
|
|
575
|
+
version = "0.2.2"
|
|
576
576
|
source = { editable = "." }
|
|
577
577
|
dependencies = [
|
|
578
578
|
{ name = "pyarrow" },
|
|
@@ -603,7 +603,7 @@ requires-dist = [
|
|
|
603
603
|
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" },
|
|
604
604
|
{ name = "python-calamine", marker = "extra == 'xlsx'", specifier = ">=0.8.0" },
|
|
605
605
|
{ name = "rich", specifier = ">=13.0.0" },
|
|
606
|
-
{ name = "ruff", marker = "extra == 'dev'", specifier = "
|
|
606
|
+
{ name = "ruff", marker = "extra == 'dev'", specifier = "==0.14.0" },
|
|
607
607
|
{ name = "twine", marker = "extra == 'dev'", specifier = ">=5.1.0" },
|
|
608
608
|
{ name = "typer", specifier = ">=0.15.0" },
|
|
609
609
|
{ name = "vulture", marker = "extra == 'dev'", specifier = ">=2.14" },
|
|
@@ -621,45 +621,59 @@ wheels = [
|
|
|
621
621
|
|
|
622
622
|
[[package]]
|
|
623
623
|
name = "pyarrow"
|
|
624
|
-
version = "
|
|
624
|
+
version = "23.0.1"
|
|
625
625
|
source = { registry = "https://pypi.org/simple" }
|
|
626
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
626
|
+
sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" }
|
|
627
627
|
wheels = [
|
|
628
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
629
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
630
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
631
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
632
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
633
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
634
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
635
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
636
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
637
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
638
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
639
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
640
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
641
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
642
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
643
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
644
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
645
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
646
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
647
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
648
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
649
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
650
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
651
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
652
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
653
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
654
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
655
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
656
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
657
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
658
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
659
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
660
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
661
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
662
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
628
|
+
{ url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390, upload-time = "2026-02-16T10:08:08.654Z" },
|
|
629
|
+
{ url = "https://files.pythonhosted.org/packages/bc/8e/4be5617b4aaae0287f621ad31c6036e5f63118cfca0dc57d42121ff49b51/pyarrow-23.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c", size = 35853761, upload-time = "2026-02-16T10:08:17.811Z" },
|
|
630
|
+
{ url = "https://files.pythonhosted.org/packages/2e/08/3e56a18819462210432ae37d10f5c8eed3828be1d6c751b6e6a2e93c286a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258", size = 44493116, upload-time = "2026-02-16T10:08:25.792Z" },
|
|
631
|
+
{ url = "https://files.pythonhosted.org/packages/f8/82/c40b68001dbec8a3faa4c08cd8c200798ac732d2854537c5449dc859f55a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2", size = 47564532, upload-time = "2026-02-16T10:08:34.27Z" },
|
|
632
|
+
{ url = "https://files.pythonhosted.org/packages/20/bc/73f611989116b6f53347581b02177f9f620efdf3cd3f405d0e83cdf53a83/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5", size = 48183685, upload-time = "2026-02-16T10:08:42.889Z" },
|
|
633
|
+
{ url = "https://files.pythonhosted.org/packages/b0/cc/6c6b3ecdae2a8c3aced99956187e8302fc954cc2cca2a37cf2111dad16ce/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222", size = 50605582, upload-time = "2026-02-16T10:08:51.641Z" },
|
|
634
|
+
{ url = "https://files.pythonhosted.org/packages/8d/94/d359e708672878d7638a04a0448edf7c707f9e5606cee11e15aaa5c7535a/pyarrow-23.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d", size = 27521148, upload-time = "2026-02-16T10:08:58.077Z" },
|
|
635
|
+
{ url = "https://files.pythonhosted.org/packages/b0/41/8e6b6ef7e225d4ceead8459427a52afdc23379768f54dd3566014d7618c1/pyarrow-23.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb", size = 34302230, upload-time = "2026-02-16T10:09:03.859Z" },
|
|
636
|
+
{ url = "https://files.pythonhosted.org/packages/bf/4a/1472c00392f521fea03ae93408bf445cc7bfa1ab81683faf9bc188e36629/pyarrow-23.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350", size = 35850050, upload-time = "2026-02-16T10:09:11.877Z" },
|
|
637
|
+
{ url = "https://files.pythonhosted.org/packages/0c/b2/bd1f2f05ded56af7f54d702c8364c9c43cd6abb91b0e9933f3d77b4f4132/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd", size = 44491918, upload-time = "2026-02-16T10:09:18.144Z" },
|
|
638
|
+
{ url = "https://files.pythonhosted.org/packages/0b/62/96459ef5b67957eac38a90f541d1c28833d1b367f014a482cb63f3b7cd2d/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9", size = 47562811, upload-time = "2026-02-16T10:09:25.792Z" },
|
|
639
|
+
{ url = "https://files.pythonhosted.org/packages/7d/94/1170e235add1f5f45a954e26cd0e906e7e74e23392dcb560de471f7366ec/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701", size = 48183766, upload-time = "2026-02-16T10:09:34.645Z" },
|
|
640
|
+
{ url = "https://files.pythonhosted.org/packages/0e/2d/39a42af4570377b99774cdb47f63ee6c7da7616bd55b3d5001aa18edfe4f/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78", size = 50607669, upload-time = "2026-02-16T10:09:44.153Z" },
|
|
641
|
+
{ url = "https://files.pythonhosted.org/packages/00/ca/db94101c187f3df742133ac837e93b1f269ebdac49427f8310ee40b6a58f/pyarrow-23.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919", size = 27527698, upload-time = "2026-02-16T10:09:50.263Z" },
|
|
642
|
+
{ url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" },
|
|
643
|
+
{ url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" },
|
|
644
|
+
{ url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" },
|
|
645
|
+
{ url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" },
|
|
646
|
+
{ url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" },
|
|
647
|
+
{ url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" },
|
|
648
|
+
{ url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" },
|
|
649
|
+
{ url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" },
|
|
650
|
+
{ url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" },
|
|
651
|
+
{ url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" },
|
|
652
|
+
{ url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" },
|
|
653
|
+
{ url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" },
|
|
654
|
+
{ url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" },
|
|
655
|
+
{ url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" },
|
|
656
|
+
{ url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" },
|
|
657
|
+
{ url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" },
|
|
658
|
+
{ url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" },
|
|
659
|
+
{ url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" },
|
|
660
|
+
{ url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" },
|
|
661
|
+
{ url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" },
|
|
662
|
+
{ url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" },
|
|
663
|
+
{ url = "https://files.pythonhosted.org/packages/8d/1b/6da9a89583ce7b23ac611f183ae4843cd3a6cf54f079549b0e8c14031e73/pyarrow-23.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca", size = 34238755, upload-time = "2026-02-16T10:12:32.819Z" },
|
|
664
|
+
{ url = "https://files.pythonhosted.org/packages/ae/b5/d58a241fbe324dbaeb8df07be6af8752c846192d78d2272e551098f74e88/pyarrow-23.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1", size = 35847826, upload-time = "2026-02-16T10:12:38.949Z" },
|
|
665
|
+
{ url = "https://files.pythonhosted.org/packages/54/a5/8cbc83f04aba433ca7b331b38f39e000efd9f0c7ce47128670e737542996/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb", size = 44536859, upload-time = "2026-02-16T10:12:45.467Z" },
|
|
666
|
+
{ url = "https://files.pythonhosted.org/packages/36/2e/c0f017c405fcdc252dbccafbe05e36b0d0eb1ea9a958f081e01c6972927f/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1", size = 47614443, upload-time = "2026-02-16T10:12:55.525Z" },
|
|
667
|
+
{ url = "https://files.pythonhosted.org/packages/af/6b/2314a78057912f5627afa13ba43809d9d653e6630859618b0fd81a4e0759/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886", size = 48232991, upload-time = "2026-02-16T10:13:04.729Z" },
|
|
668
|
+
{ url = "https://files.pythonhosted.org/packages/40/f2/1bcb1d3be3460832ef3370d621142216e15a2c7c62602a4ea19ec240dd64/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f", size = 50645077, upload-time = "2026-02-16T10:13:14.147Z" },
|
|
669
|
+
{ url = "https://files.pythonhosted.org/packages/eb/3f/b1da7b61cd66566a4d4c8383d376c606d1c34a906c3f1cb35c479f59d1aa/pyarrow-23.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5", size = 28234271, upload-time = "2026-02-16T10:14:09.397Z" },
|
|
670
|
+
{ url = "https://files.pythonhosted.org/packages/b5/78/07f67434e910a0f7323269be7bfbf58699bd0c1d080b18a1ab49ba943fe8/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d", size = 34488692, upload-time = "2026-02-16T10:13:21.541Z" },
|
|
671
|
+
{ url = "https://files.pythonhosted.org/packages/50/76/34cf7ae93ece1f740a04910d9f7e80ba166b9b4ab9596a953e9e62b90fe1/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f", size = 35964383, upload-time = "2026-02-16T10:13:28.63Z" },
|
|
672
|
+
{ url = "https://files.pythonhosted.org/packages/46/90/459b827238936d4244214be7c684e1b366a63f8c78c380807ae25ed92199/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814", size = 44538119, upload-time = "2026-02-16T10:13:35.506Z" },
|
|
673
|
+
{ url = "https://files.pythonhosted.org/packages/28/a1/93a71ae5881e99d1f9de1d4554a87be37da11cd6b152239fb5bd924fdc64/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d", size = 47571199, upload-time = "2026-02-16T10:13:42.504Z" },
|
|
674
|
+
{ url = "https://files.pythonhosted.org/packages/88/a3/d2c462d4ef313521eaf2eff04d204ac60775263f1fb08c374b543f79f610/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7", size = 48259435, upload-time = "2026-02-16T10:13:49.226Z" },
|
|
675
|
+
{ url = "https://files.pythonhosted.org/packages/cc/f1/11a544b8c3d38a759eb3fbb022039117fd633e9a7b19e4841cc3da091915/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690", size = 50629149, upload-time = "2026-02-16T10:13:57.238Z" },
|
|
676
|
+
{ url = "https://files.pythonhosted.org/packages/50/f2/c0e76a0b451ffdf0cf788932e182758eb7558953f4f27f1aff8e2518b653/pyarrow-23.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce", size = 28365807, upload-time = "2026-02-16T10:14:03.892Z" },
|
|
663
677
|
]
|
|
664
678
|
|
|
665
679
|
[[package]]
|
|
@@ -673,11 +687,11 @@ wheels = [
|
|
|
673
687
|
|
|
674
688
|
[[package]]
|
|
675
689
|
name = "pygments"
|
|
676
|
-
version = "2.
|
|
690
|
+
version = "2.21.0"
|
|
677
691
|
source = { registry = "https://pypi.org/simple" }
|
|
678
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
692
|
+
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
|
|
679
693
|
wheels = [
|
|
680
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
694
|
+
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
|
|
681
695
|
]
|
|
682
696
|
|
|
683
697
|
[[package]]
|
|
@@ -691,7 +705,7 @@ wheels = [
|
|
|
691
705
|
|
|
692
706
|
[[package]]
|
|
693
707
|
name = "pytest"
|
|
694
|
-
version = "
|
|
708
|
+
version = "9.0.3"
|
|
695
709
|
source = { registry = "https://pypi.org/simple" }
|
|
696
710
|
dependencies = [
|
|
697
711
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
@@ -702,9 +716,9 @@ dependencies = [
|
|
|
702
716
|
{ name = "pygments" },
|
|
703
717
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
704
718
|
]
|
|
705
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
719
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
|
706
720
|
wheels = [
|
|
707
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
721
|
+
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
|
708
722
|
]
|
|
709
723
|
|
|
710
724
|
[[package]]
|
|
@@ -1035,11 +1049,11 @@ wheels = [
|
|
|
1035
1049
|
|
|
1036
1050
|
[[package]]
|
|
1037
1051
|
name = "urllib3"
|
|
1038
|
-
version = "2.
|
|
1052
|
+
version = "2.8.0"
|
|
1039
1053
|
source = { registry = "https://pypi.org/simple" }
|
|
1040
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1054
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" }
|
|
1041
1055
|
wheels = [
|
|
1042
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1056
|
+
{ url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" },
|
|
1043
1057
|
]
|
|
1044
1058
|
|
|
1045
1059
|
[[package]]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|