python-calamine-reducto 0.7.16__tar.gz → 0.7.18__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.
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/.github/workflows/CI.yml +45 -1
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/CLAUDE.md +4 -3
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/Cargo.lock +2 -2
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/Cargo.toml +1 -1
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/PKG-INFO +1 -1
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/README.md +22 -25
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/python/python_calamine/__init__.py +0 -2
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/python/python_calamine/_python_calamine.pyi +154 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/lib.rs +3 -6
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/errors.rs +21 -0
- python_calamine_reducto-0.7.18/src/types/grid.rs +1244 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/mod.rs +1 -1
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/workbook.rs +541 -609
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/create_test_fixture.py +1 -1
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_band.py +1 -1
- python_calamine_reducto-0.7.18/tests/test_bounded_reads.py +182 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_grid.py +87 -486
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_tables.py +1 -3
- python_calamine_reducto-0.7.18/tests/test_trim_header_only.py +133 -0
- python_calamine_reducto-0.7.18/tests/test_uncached_formulas.py +88 -0
- python_calamine_reducto-0.7.18/tests/xlsx_builder.py +57 -0
- python_calamine_reducto-0.7.16/src/types/grid.rs +0 -2141
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/.github/workflows/release.yml +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/.gitignore +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/.pre-commit-config.yaml +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/.python-version +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/LICENSE +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/build.rs +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/pyproject.toml +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/python/python_calamine/py.typed +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/renovate.json +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/cell.rs +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/sheet.rs +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/src/types/table.rs +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/__init__.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/conftest.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/create_empty_merge_fixture.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/create_hidden_col_range_fixture.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/create_richtext_fixture.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/any_sheets.ods +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/any_sheets.xls +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/any_sheets.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/any_sheets.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/base.ods +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/base.xls +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/base.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/base.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/dropdown_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/empty_file.ods +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/empty_file.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/empty_file.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/empty_merge_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/formula_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/grid_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/hidden_col_range_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/image_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/issue139.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/multi_image_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/password.ods +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/password.xls +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/password.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/password.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/richtext_test.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/table-multiple.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/table-multiple.xlsx +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/data/temperature-table.xlsb +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_base.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_images.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_pandas.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/tests/test_sheet_type.py +0 -0
- {python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/uv.lock +0 -0
|
@@ -53,10 +53,54 @@ jobs:
|
|
|
53
53
|
|
|
54
54
|
- run: pytest
|
|
55
55
|
|
|
56
|
+
lint:
|
|
57
|
+
name: rust lint and unit tests
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v6
|
|
61
|
+
|
|
62
|
+
- name: configure git for private deps
|
|
63
|
+
run: git config --global url."https://x-access-token:${{ secrets.GH_PAT }}@github.com/".insteadOf "https://github.com/"
|
|
64
|
+
|
|
65
|
+
- name: install rust stable
|
|
66
|
+
uses: dtolnay/rust-toolchain@master
|
|
67
|
+
with:
|
|
68
|
+
toolchain: stable
|
|
69
|
+
components: clippy, rustfmt
|
|
70
|
+
|
|
71
|
+
- name: cache rust
|
|
72
|
+
uses: Swatinem/rust-cache@v2
|
|
73
|
+
with:
|
|
74
|
+
key: v3
|
|
75
|
+
|
|
76
|
+
- name: set up python
|
|
77
|
+
uses: actions/setup-python@v6
|
|
78
|
+
with:
|
|
79
|
+
python-version: "3.13"
|
|
80
|
+
|
|
81
|
+
- run: cargo fmt --check
|
|
82
|
+
|
|
83
|
+
- run: cargo clippy --all-targets -- -D warnings
|
|
84
|
+
env:
|
|
85
|
+
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
|
|
86
|
+
|
|
87
|
+
- run: cargo test
|
|
88
|
+
env:
|
|
89
|
+
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
|
|
90
|
+
|
|
91
|
+
- run: pip install numpy "mypy~=1.19.0"
|
|
92
|
+
|
|
93
|
+
- run: pip install -e .
|
|
94
|
+
env:
|
|
95
|
+
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
|
|
96
|
+
|
|
97
|
+
- name: stubtest
|
|
98
|
+
run: python -m mypy.stubtest python_calamine
|
|
99
|
+
|
|
56
100
|
# https://github.com/marketplace/actions/alls-green#why used for branch protection checks
|
|
57
101
|
check:
|
|
58
102
|
if: always()
|
|
59
|
-
needs: [test]
|
|
103
|
+
needs: [test, lint]
|
|
60
104
|
runs-on: ubuntu-latest
|
|
61
105
|
steps:
|
|
62
106
|
- name: Decide whether the needed jobs succeeded or failed
|
|
@@ -100,13 +100,14 @@ python tests/create_test_fixture.py
|
|
|
100
100
|
|
|
101
101
|
| Path | What |
|
|
102
102
|
|------|------|
|
|
103
|
-
| `src/types/grid.rs` | CellGrid, CellProxy
|
|
104
|
-
| `src/types/workbook.rs` |
|
|
103
|
+
| `src/types/grid.rs` | CellGrid, CellProxy — the core Reducto addition |
|
|
104
|
+
| `src/types/workbook.rs` | CalamineWorkbook: grid builders, bounds, layout, formulas |
|
|
105
105
|
| `src/types/mod.rs` | Module exports |
|
|
106
106
|
| `src/lib.rs` | PyO3 module registration |
|
|
107
107
|
| `python/python_calamine/__init__.py` | Python re-exports |
|
|
108
108
|
| `Cargo.toml` | Points calamine + ssfmt deps to reductoai forks |
|
|
109
|
-
| `tests/test_grid.py` |
|
|
109
|
+
| `tests/test_grid.py` | CellGrid API tests |
|
|
110
|
+
| `tests/test_bounded_reads.py` | Reads whose cost must follow the filled cells |
|
|
110
111
|
|
|
111
112
|
## Architecture
|
|
112
113
|
|
|
@@ -80,7 +80,7 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
|
80
80
|
[[package]]
|
|
81
81
|
name = "calamine"
|
|
82
82
|
version = "0.32.0"
|
|
83
|
-
source = "git+https://github.com/reductoai/calamine.git?branch=main#
|
|
83
|
+
source = "git+https://github.com/reductoai/calamine.git?branch=main#297ee32c41c7449e22f9fb94e93c0291b0b4d15d"
|
|
84
84
|
dependencies = [
|
|
85
85
|
"atoi_simd",
|
|
86
86
|
"byteorder",
|
|
@@ -612,7 +612,7 @@ dependencies = [
|
|
|
612
612
|
|
|
613
613
|
[[package]]
|
|
614
614
|
name = "python-calamine-reducto"
|
|
615
|
-
version = "0.7.
|
|
615
|
+
version = "0.7.18"
|
|
616
616
|
dependencies = [
|
|
617
617
|
"calamine",
|
|
618
618
|
"chrono",
|
|
@@ -15,43 +15,40 @@ Our additions live on `main`.
|
|
|
15
15
|
|
|
16
16
|
## What we added
|
|
17
17
|
|
|
18
|
-
###
|
|
19
|
-
|
|
20
|
-
- **`
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
### Workbook bindings (`workbook.rs`)
|
|
19
|
+
|
|
20
|
+
- **`get_sheet_bounds(name)`** → `(max_filled_row, max_filled_col)`. xlsx streams the cells.
|
|
21
|
+
- **`get_sheet_grid_streaming(name, include_styles=True, ...)`** →
|
|
22
|
+
`(grid, declared_rows, declared_cols, dropped_cells)`. Streams xlsx cells with no dense
|
|
23
|
+
range and trims sparse outlier cells.
|
|
24
|
+
- **`for_each_band(name, band_rows, callback, ...)`** → streams the sheet as row bands.
|
|
25
|
+
- **`get_sheet_grid(name, rows, cols, include_styles=True)`** → `CellGrid` from the dense range.
|
|
25
26
|
- **`get_sheet_layout(name)`** → dict with row heights, column widths, defaults
|
|
26
|
-
- **`
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
- **`get_sheet_formulas`**, **`get_sheet_merged_ranges`**, **`get_sheet_images`**,
|
|
28
|
+
**`get_sheet_data_validations`**, **`get_sheet_freeze_panes`**,
|
|
29
|
+
**`get_sheet_filled_count`**, **`get_sheet_uncached_formula_count`**,
|
|
30
|
+
**`get_all_tables_metadata`**
|
|
29
31
|
|
|
30
32
|
### CellGrid (`grid.rs`) — Rust-backed 2D grid
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
All cell data stays in Rust. The API converts a grid to a numpy structured array once,
|
|
35
|
+
with `fill_numpy_array`.
|
|
34
36
|
|
|
35
37
|
**Types:**
|
|
36
|
-
- `CellGrid` — the main 2D grid (values, styles, merges, header, colors, formulas)
|
|
37
|
-
|
|
38
|
-
- `
|
|
38
|
+
- `CellGrid` — the main 2D grid (values, styles, merges, header, colors, formulas).
|
|
39
|
+
Styles and color names are stored as indexes into small palettes.
|
|
40
|
+
- `CellProxy` — single-cell accessor (`cell["value"]`)
|
|
39
41
|
|
|
40
42
|
**Access patterns:**
|
|
41
43
|
- `grid[r, c]` → `CellProxy`
|
|
42
|
-
- `grid[r1:r2, c1:c2]` → `CellGridView`
|
|
43
44
|
- `grid["field_name"]` → numpy 2D array of that field
|
|
44
45
|
- `grid["field_name"] = value` → broadcast or copy from numpy
|
|
45
|
-
|
|
46
|
-
**Grid operations:**
|
|
47
|
-
- `vstack(other)` / `hstack(other)` — concatenation
|
|
48
|
-
- `slice_copy(r1, r2, c1, c2)` — independent copy of region
|
|
49
|
-
- `copy()` — deep copy
|
|
50
46
|
- `CellGrid.empty(rows, cols)` — create empty grid
|
|
51
|
-
- `masked_copy_from(source, mask, ...)` — boolean mask copy
|
|
52
47
|
|
|
53
48
|
**Formatting:**
|
|
54
|
-
- `
|
|
49
|
+
- `apply_formatting_rust()` — SSF number formats in Rust
|
|
50
|
+
- `apply_dateutil_reformat(date_parse)`, `apply_color_names(converter)`,
|
|
51
|
+
`propagate_merged_values()`
|
|
55
52
|
- `apply_raw_as_formatted()` — `str(raw)` for large sheets
|
|
56
53
|
|
|
57
54
|
### Performance
|
|
@@ -88,8 +85,8 @@ git merge upstream/master
|
|
|
88
85
|
|
|
89
86
|
| File | What |
|
|
90
87
|
|------|------|
|
|
91
|
-
| `src/types/grid.rs` | CellGrid, CellProxy
|
|
92
|
-
| `src/types/workbook.rs` |
|
|
88
|
+
| `src/types/grid.rs` | CellGrid, CellProxy |
|
|
89
|
+
| `src/types/workbook.rs` | CalamineWorkbook: grid builders, bounds, layout, formulas |
|
|
93
90
|
| `src/types/sheet.rs` | CalamineSheet, CalamineCellIterator |
|
|
94
91
|
| `src/types/cell.rs` | CellValue enum → Python type conversion |
|
|
95
92
|
| `src/lib.rs` | PyO3 module registration |
|
{python_calamine_reducto-0.7.16 → python_calamine_reducto-0.7.18}/python/python_calamine/__init__.py
RENAMED
|
@@ -4,7 +4,6 @@ from ._python_calamine import (
|
|
|
4
4
|
CalamineTable,
|
|
5
5
|
CalamineWorkbook,
|
|
6
6
|
CellGrid,
|
|
7
|
-
CellGridView,
|
|
8
7
|
CellProxy,
|
|
9
8
|
PasswordError,
|
|
10
9
|
SheetMetadata,
|
|
@@ -26,7 +25,6 @@ __all__ = (
|
|
|
26
25
|
"CalamineTable",
|
|
27
26
|
"CalamineWorkbook",
|
|
28
27
|
"CellGrid",
|
|
29
|
-
"CellGridView",
|
|
30
28
|
"CellProxy",
|
|
31
29
|
"PasswordError",
|
|
32
30
|
"SheetMetadata",
|
|
@@ -6,6 +6,10 @@ import enum
|
|
|
6
6
|
import os
|
|
7
7
|
import types
|
|
8
8
|
import typing
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
9
13
|
|
|
10
14
|
@typing.type_check_only
|
|
11
15
|
class ReadBuffer(typing.Protocol):
|
|
@@ -174,6 +178,92 @@ class CalamineTable:
|
|
|
174
178
|
]:
|
|
175
179
|
"""Returning data from table as list of lists."""
|
|
176
180
|
|
|
181
|
+
@typing.type_check_only
|
|
182
|
+
class TableMetadata(typing.TypedDict):
|
|
183
|
+
name: str
|
|
184
|
+
sheet: str
|
|
185
|
+
columns: list[str]
|
|
186
|
+
start: tuple[int, int]
|
|
187
|
+
"""First data row and column, 0-based (the header row is above it)."""
|
|
188
|
+
end: tuple[int, int]
|
|
189
|
+
|
|
190
|
+
@typing.type_check_only
|
|
191
|
+
class SheetLayout(typing.TypedDict):
|
|
192
|
+
column_widths: list[tuple[int, float, bool]]
|
|
193
|
+
"""`(col, width, hidden)` for each column with a `<col>` entry."""
|
|
194
|
+
row_heights: list[tuple[int, float, bool]]
|
|
195
|
+
"""`(row, height, hidden)` for each row with a custom height or flag."""
|
|
196
|
+
default_column_width: float | None
|
|
197
|
+
default_row_height: float | None
|
|
198
|
+
|
|
199
|
+
@typing.type_check_only
|
|
200
|
+
class DataValidation(typing.TypedDict):
|
|
201
|
+
type: str
|
|
202
|
+
sqref: str
|
|
203
|
+
formula1: str | None
|
|
204
|
+
formula2: str | None
|
|
205
|
+
allow_blank: bool
|
|
206
|
+
|
|
207
|
+
@typing.final
|
|
208
|
+
class CellProxy:
|
|
209
|
+
"""One cell of a `CellGrid`. Read a field with `cell["value"]`."""
|
|
210
|
+
|
|
211
|
+
def __getitem__(self, key: str, /) -> typing.Any: ...
|
|
212
|
+
|
|
213
|
+
@typing.final
|
|
214
|
+
class CellGrid:
|
|
215
|
+
"""A sheet grid held in Rust. The API converts it to numpy with `fill_numpy_array`."""
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def shape(self) -> tuple[int, int]: ...
|
|
219
|
+
@property
|
|
220
|
+
def size(self) -> int: ...
|
|
221
|
+
@property
|
|
222
|
+
def uncached_formula_count(self) -> int | None:
|
|
223
|
+
"""Formula cells with no cached value (streaming xlsx grids), else `None`."""
|
|
224
|
+
|
|
225
|
+
def __len__(self) -> int: ...
|
|
226
|
+
@classmethod
|
|
227
|
+
def empty(cls, rows: int, cols: int) -> CellGrid:
|
|
228
|
+
"""An empty grid whose `merge_parent` is `(-1, -1)` for every cell."""
|
|
229
|
+
|
|
230
|
+
@typing.overload
|
|
231
|
+
def __getitem__(self, key: str, /) -> npt.NDArray[typing.Any]:
|
|
232
|
+
"""The numpy array of one field, e.g. `grid["value"]`."""
|
|
233
|
+
|
|
234
|
+
@typing.overload
|
|
235
|
+
def __getitem__(self, key: tuple[int, int], /) -> CellProxy: ...
|
|
236
|
+
def __setitem__(
|
|
237
|
+
self,
|
|
238
|
+
key: typing.Literal["table_member", "header", "value"],
|
|
239
|
+
value: bool | npt.NDArray[np.bool_] | None,
|
|
240
|
+
/,
|
|
241
|
+
) -> None: ...
|
|
242
|
+
def set_cell_formula(self, row: int, col: int, formula: str) -> None: ...
|
|
243
|
+
def set_cell_value(self, row: int, col: int, value: str) -> None: ...
|
|
244
|
+
def set_field_range(
|
|
245
|
+
self,
|
|
246
|
+
field: typing.Literal["table_member", "header"],
|
|
247
|
+
r1: int,
|
|
248
|
+
r2: int,
|
|
249
|
+
c1: int,
|
|
250
|
+
c2: int,
|
|
251
|
+
value: bool,
|
|
252
|
+
) -> None: ...
|
|
253
|
+
def apply_formatting_rust(self) -> None:
|
|
254
|
+
"""Format each value with its number format (SSF, in Rust)."""
|
|
255
|
+
|
|
256
|
+
def apply_dateutil_reformat(self, date_parse: Callable[[str], float | None]) -> None:
|
|
257
|
+
"""Reformat text cells that have a date format. `date_parse` gives a date serial."""
|
|
258
|
+
|
|
259
|
+
def apply_raw_as_formatted(self) -> None: ...
|
|
260
|
+
def apply_color_names(self, converter: Callable[[str], str | None]) -> None:
|
|
261
|
+
"""Set `cell_textcolor` and `cell_highlight` from the hex colors of each style."""
|
|
262
|
+
|
|
263
|
+
def propagate_merged_values(self) -> None: ...
|
|
264
|
+
def fill_numpy_array(self, arr: npt.NDArray[np.void]) -> None:
|
|
265
|
+
"""Move every field into `arr`. The grid is empty after this call."""
|
|
266
|
+
|
|
177
267
|
@typing.final
|
|
178
268
|
class CalamineWorkbook:
|
|
179
269
|
path: str | None
|
|
@@ -276,6 +366,68 @@ class CalamineWorkbook:
|
|
|
276
366
|
TableNotFound: If table not found in workbook.
|
|
277
367
|
"""
|
|
278
368
|
|
|
369
|
+
def get_all_tables_metadata(self) -> list[TableMetadata]: ...
|
|
370
|
+
def get_sheet_layout(self, name: str) -> SheetLayout: ...
|
|
371
|
+
def get_sheet_grid(
|
|
372
|
+
self,
|
|
373
|
+
name: str,
|
|
374
|
+
max_filled_row: int,
|
|
375
|
+
max_filled_col: int,
|
|
376
|
+
include_styles: bool = True,
|
|
377
|
+
) -> CellGrid:
|
|
378
|
+
"""Build a grid from the dense range. Bounds of 0 compute them from the data."""
|
|
379
|
+
|
|
380
|
+
def get_sheet_grid_streaming(
|
|
381
|
+
self,
|
|
382
|
+
name: str,
|
|
383
|
+
include_styles: bool = True,
|
|
384
|
+
trigger_cells: int = 2_000_000,
|
|
385
|
+
trigger_density: float = 0.001,
|
|
386
|
+
coverage: float = 0.999,
|
|
387
|
+
trim_header_only: bool = False,
|
|
388
|
+
) -> tuple[CellGrid, int, int, int]:
|
|
389
|
+
"""Stream a grid. Returns `(grid, declared_rows, declared_cols, dropped_cells)`.
|
|
390
|
+
|
|
391
|
+
`trim_header_only` also drops the trailing columns whose only value is in the header row.
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
def get_sheet_filled_count(self, name: str) -> tuple[int, int, int]:
|
|
395
|
+
"""`(filled, max_filled_row, max_filled_col)` in one streaming pass. xlsx only."""
|
|
396
|
+
|
|
397
|
+
def get_sheet_uncached_formula_count(self, name: str) -> int | None: ...
|
|
398
|
+
def for_each_band(
|
|
399
|
+
self,
|
|
400
|
+
name: str,
|
|
401
|
+
band_rows: int,
|
|
402
|
+
callback: Callable[[CellGrid, int], object],
|
|
403
|
+
include_styles: bool = True,
|
|
404
|
+
trigger_cells: int = 2_000_000,
|
|
405
|
+
trigger_density: float = 0.001,
|
|
406
|
+
coverage: float = 0.999,
|
|
407
|
+
propagate_merges: bool = False,
|
|
408
|
+
trim_header_only: bool = False,
|
|
409
|
+
) -> tuple[int, int, int]:
|
|
410
|
+
"""Stream the sheet as row bands. Returns `(declared_rows, declared_cols, dropped_cells)`."""
|
|
411
|
+
|
|
412
|
+
def get_sheet_formulas(self, name: str) -> list[tuple[int, int, str]]:
|
|
413
|
+
"""`(row, col, formula)` for each formula cell. Each formula starts with `=`."""
|
|
414
|
+
|
|
415
|
+
def get_sheet_images(self, name: str) -> list[tuple[bytes, str, int, int]]:
|
|
416
|
+
"""`(data, extension, anchor_row, anchor_col)` for each image."""
|
|
417
|
+
|
|
418
|
+
def get_sheet_data_validations(self, name: str) -> list[DataValidation]: ...
|
|
419
|
+
def get_sheet_freeze_panes(self, name: str) -> tuple[int, int]:
|
|
420
|
+
"""`(frozen_rows, frozen_cols)`."""
|
|
421
|
+
|
|
422
|
+
def get_sheet_merged_ranges(self, name: str) -> list[tuple[int, int, int, int]]:
|
|
423
|
+
"""`(start_row, start_col, end_row, end_col)`, 0-based and inclusive."""
|
|
424
|
+
|
|
425
|
+
def get_sheet_bounds(self, name: str) -> tuple[int, int]:
|
|
426
|
+
"""`(max_filled_row, max_filled_col)`, 1-based and exclusive.
|
|
427
|
+
|
|
428
|
+
xlsx does not count `""` cells, as `get_sheet_grid_streaming`. Other formats count them.
|
|
429
|
+
"""
|
|
430
|
+
|
|
279
431
|
class CalamineError(Exception): ...
|
|
280
432
|
class PasswordError(CalamineError): ...
|
|
281
433
|
class WorksheetNotFound(CalamineError): ...
|
|
@@ -301,6 +453,8 @@ __all__ = [
|
|
|
301
453
|
"CalamineSheet",
|
|
302
454
|
"CalamineTable",
|
|
303
455
|
"CalamineWorkbook",
|
|
456
|
+
"CellGrid",
|
|
457
|
+
"CellProxy",
|
|
304
458
|
"PasswordError",
|
|
305
459
|
"SheetMetadata",
|
|
306
460
|
"SheetTypeEnum",
|
|
@@ -2,10 +2,9 @@ use pyo3::prelude::*;
|
|
|
2
2
|
|
|
3
3
|
mod types;
|
|
4
4
|
use crate::types::{
|
|
5
|
-
CalamineError, CalamineSheet, CalamineTable, CalamineWorkbook, CellGrid,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
WorksheetNotFound, XmlError, ZipError,
|
|
5
|
+
CalamineError, CalamineSheet, CalamineTable, CalamineWorkbook, CellGrid, CellProxy, CellValue,
|
|
6
|
+
Error, PasswordError, SheetMetadata, SheetTypeEnum, SheetVisibleEnum, TableNotFound,
|
|
7
|
+
TablesNotLoaded, TablesNotSupported, WorkbookClosed, WorksheetNotFound, XmlError, ZipError,
|
|
9
8
|
};
|
|
10
9
|
|
|
11
10
|
#[pyfunction]
|
|
@@ -29,8 +28,6 @@ fn _python_calamine(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
|
29
28
|
m.add_class::<CalamineTable>()?;
|
|
30
29
|
m.add_class::<CellGrid>()?;
|
|
31
30
|
m.add_class::<CellProxy>()?;
|
|
32
|
-
m.add_class::<CellGridView>()?;
|
|
33
|
-
m.add_class::<CellGridIterator>()?;
|
|
34
31
|
m.add("CalamineError", py.get_type::<CalamineError>())?;
|
|
35
32
|
m.add("PasswordError", py.get_type::<PasswordError>())?;
|
|
36
33
|
m.add("WorksheetNotFound", py.get_type::<WorksheetNotFound>())?;
|
|
@@ -20,6 +20,24 @@ create_exception!(python_calamine, TablesNotSupported, CalamineError);
|
|
|
20
20
|
create_exception!(python_calamine, TablesNotLoaded, CalamineError);
|
|
21
21
|
create_exception!(python_calamine, TableNotFound, CalamineError);
|
|
22
22
|
|
|
23
|
+
impl From<CalamineCrateError> for Error {
|
|
24
|
+
fn from(err: CalamineCrateError) -> Self {
|
|
25
|
+
Error::Calamine(err)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
impl From<XlsxError> for Error {
|
|
30
|
+
fn from(err: XlsxError) -> Self {
|
|
31
|
+
Error::Calamine(err.into())
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl From<XlsbError> for Error {
|
|
36
|
+
fn from(err: XlsbError) -> Self {
|
|
37
|
+
Error::Calamine(err.into())
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
impl From<Error> for PyErr {
|
|
24
42
|
fn from(val: Error) -> Self {
|
|
25
43
|
match val {
|
|
@@ -52,6 +70,7 @@ impl From<Error> for PyErr {
|
|
|
52
70
|
XlsxError::XmlEof(error) => XmlError::new_err(error.to_string()),
|
|
53
71
|
XlsxError::Password => PasswordError::new_err(err.to_string()),
|
|
54
72
|
XlsxError::TableNotFound(error) => TableNotFound::new_err(error.to_string()),
|
|
73
|
+
XlsxError::TablesNotLoaded => TablesNotLoaded::new_err(err.to_string()),
|
|
55
74
|
XlsxError::WorksheetNotFound(error) => {
|
|
56
75
|
WorksheetNotFound::new_err(error.to_string())
|
|
57
76
|
}
|
|
@@ -63,6 +82,8 @@ impl From<Error> for PyErr {
|
|
|
63
82
|
XlsbError::Xml(error) => XmlError::new_err(error.to_string()),
|
|
64
83
|
XlsbError::XmlAttr(error) => XmlError::new_err(error.to_string()),
|
|
65
84
|
XlsbError::Password => PasswordError::new_err(err.to_string()),
|
|
85
|
+
XlsbError::TableNotFound(error) => TableNotFound::new_err(error.to_string()),
|
|
86
|
+
XlsbError::TablesNotLoaded => TablesNotLoaded::new_err(err.to_string()),
|
|
66
87
|
XlsbError::WorksheetNotFound(error) => {
|
|
67
88
|
WorksheetNotFound::new_err(error.to_string())
|
|
68
89
|
}
|