tableio 0.8__tar.gz → 1.0__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.
- {tableio-0.8/tableio.egg-info → tableio-1.0}/PKG-INFO +6 -6
- {tableio-0.8 → tableio-1.0}/README.md +3 -3
- {tableio-0.8 → tableio-1.0}/README_pypi.md +3 -3
- {tableio-0.8 → tableio-1.0}/pyproject.toml +1 -1
- {tableio-0.8 → tableio-1.0}/setup.py +3 -3
- {tableio-0.8 → tableio-1.0}/src/tableio/access_capability.py +23 -10
- {tableio-0.8 → tableio-1.0}/src/tableio/config_data_apply.py +52 -12
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_spreadsheetbased.py +33 -16
- {tableio-0.8 → tableio-1.0/tableio.egg-info}/PKG-INFO +6 -6
- {tableio-0.8 → tableio-1.0}/tableio.egg-info/requires.txt +2 -2
- {tableio-0.8 → tableio-1.0}/LICENSE.txt +0 -0
- {tableio-0.8 → tableio-1.0}/setup.cfg +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/__init__.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/_archive_rewrite.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/border_helper.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/capability.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/color.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/config_data.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/config_data_describe.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/config_data_error.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/config_data_validate.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/factory.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/optional_args.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/py.typed +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/reg_pkg_formats.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_csv.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_openpyxl.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_pylightxl.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_xlsxwriter.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excelbased.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_mformat.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_mformatbased.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_ods_odfdo.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_textbased.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/tableio_types.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/value_type.py +0 -0
- {tableio-0.8 → tableio-1.0}/src/tableio/valueconversion.py +0 -0
- {tableio-0.8 → tableio-1.0}/tableio.egg-info/SOURCES.txt +0 -0
- {tableio-0.8 → tableio-1.0}/tableio.egg-info/dependency_links.txt +0 -0
- {tableio-0.8 → tableio-1.0}/tableio.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tableio
|
|
3
|
-
Version: 0
|
|
3
|
+
Version: 1.0
|
|
4
4
|
Summary: Uniform way to write table data to and read from different file formats
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -17,8 +17,8 @@ Requires-Dist: build>=1.5.0
|
|
|
17
17
|
Requires-Dist: wheel>=0.47.0
|
|
18
18
|
Requires-Dist: mformat-ext>=0.6
|
|
19
19
|
Requires-Dist: openpyxl>=3.1.5
|
|
20
|
-
Requires-Dist: types-openpyxl>=3.1.5.
|
|
21
|
-
Requires-Dist: odfdo>=3.22.
|
|
20
|
+
Requires-Dist: types-openpyxl>=3.1.5.20260518
|
|
21
|
+
Requires-Dist: odfdo>=3.22.8
|
|
22
22
|
Requires-Dist: xlsxwriter>=3.2.9
|
|
23
23
|
Requires-Dist: pylightxl>=1.61
|
|
24
24
|
Dynamic: author
|
|
@@ -137,9 +137,9 @@ a better start by reading the examples.
|
|
|
137
137
|
|
|
138
138
|
## Test summary
|
|
139
139
|
|
|
140
|
-
- Test result:
|
|
140
|
+
- Test result: 1347 passed in 22s
|
|
141
141
|
- No flake8 warnings.
|
|
142
142
|
- No mypy errors found.
|
|
143
143
|
- No python layout warnings.
|
|
144
|
-
- Built version(s): 0
|
|
145
|
-
- Build and test using Python 3.14.
|
|
144
|
+
- Built version(s): 1.0
|
|
145
|
+
- Build and test using Python 3.14.5
|
|
@@ -93,9 +93,9 @@ information about the build system. This README can also be viewed at
|
|
|
93
93
|
|
|
94
94
|
## Test summary
|
|
95
95
|
|
|
96
|
-
- Test result:
|
|
96
|
+
- Test result: 1347 passed in 22s
|
|
97
97
|
- No flake8 warnings.
|
|
98
98
|
- No mypy errors found.
|
|
99
99
|
- No python layout warnings.
|
|
100
|
-
- Built version(s): 0
|
|
101
|
-
- Build and test using Python 3.14.
|
|
100
|
+
- Built version(s): 1.0
|
|
101
|
+
- Build and test using Python 3.14.5
|
|
@@ -109,9 +109,9 @@ a better start by reading the examples.
|
|
|
109
109
|
|
|
110
110
|
## Test summary
|
|
111
111
|
|
|
112
|
-
- Test result:
|
|
112
|
+
- Test result: 1347 passed in 22s
|
|
113
113
|
- No flake8 warnings.
|
|
114
114
|
- No mypy errors found.
|
|
115
115
|
- No python layout warnings.
|
|
116
|
-
- Built version(s): 0
|
|
117
|
-
- Build and test using Python 3.14.
|
|
116
|
+
- Built version(s): 1.0
|
|
117
|
+
- Build and test using Python 3.14.5
|
|
@@ -5,7 +5,7 @@ from setuptools import setup
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='tableio',
|
|
8
|
-
version='0
|
|
8
|
+
version='1.0',
|
|
9
9
|
description='Uniform way to write table data to and read from different ' \
|
|
10
10
|
'file formats',
|
|
11
11
|
author='Tom Björkholm',
|
|
@@ -21,8 +21,8 @@ setup(
|
|
|
21
21
|
'wheel >= 0.47.0',
|
|
22
22
|
'mformat-ext >= 0.6',
|
|
23
23
|
'openpyxl >= 3.1.5',
|
|
24
|
-
'types-openpyxl >= 3.1.5.
|
|
25
|
-
'odfdo >= 3.22.
|
|
24
|
+
'types-openpyxl >= 3.1.5.20260518',
|
|
25
|
+
'odfdo >= 3.22.8',
|
|
26
26
|
'xlsxwriter >= 3.2.9',
|
|
27
27
|
'pylightxl >= 1.61'
|
|
28
28
|
]
|
|
@@ -4,16 +4,22 @@
|
|
|
4
4
|
# Copyright (c) 2026 Tom Björkholm
|
|
5
5
|
# MIT License
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
from enum import Enum, auto
|
|
8
8
|
from typing import NoReturn, TextIO
|
|
9
9
|
|
|
10
10
|
from tableio.capability import CAP_NEEDED, Capabilities
|
|
11
11
|
from tableio.tableio_types import FileAccess
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
class NoErrorOutput(Enum):
|
|
15
|
+
"""Text stream marker when helper errors should not be printed."""
|
|
16
|
+
|
|
17
|
+
NO_OUTPUT = auto()
|
|
18
|
+
"""Text stream marker when helper errors should not be printed."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
NO_ERROR_OUTPUT = NoErrorOutput.NO_OUTPUT
|
|
22
|
+
"""Text stream marker used when helper errors should not be printed."""
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
class InsufficientCapabilities(ValueError):
|
|
@@ -32,13 +38,17 @@ class InsufficientCapabilities(ValueError):
|
|
|
32
38
|
super().__init__(message)
|
|
33
39
|
|
|
34
40
|
|
|
35
|
-
def _raise_error(error_file: TextIO
|
|
41
|
+
def _raise_error(error_file: TextIO | NoErrorOutput,
|
|
42
|
+
error: Exception) -> NoReturn:
|
|
36
43
|
"""Write an error message and raise the error."""
|
|
44
|
+
if isinstance(error_file, NoErrorOutput):
|
|
45
|
+
raise error
|
|
37
46
|
error_file.write(str(error) + '\n')
|
|
38
47
|
raise error
|
|
39
48
|
|
|
40
49
|
|
|
41
|
-
def _check_access_value(file_access: FileAccess,
|
|
50
|
+
def _check_access_value(file_access: FileAccess,
|
|
51
|
+
error_file: TextIO | NoErrorOutput) -> None:
|
|
42
52
|
"""Raise if file access is not a supported FileAccess value."""
|
|
43
53
|
if not isinstance(file_access, FileAccess):
|
|
44
54
|
error = TypeError('file_access must be a FileAccess value.')
|
|
@@ -50,7 +60,7 @@ def _check_access_value(file_access: FileAccess, error_file: TextIO) -> None:
|
|
|
50
60
|
|
|
51
61
|
|
|
52
62
|
def _check_capabilities_value(capabilities: Capabilities,
|
|
53
|
-
error_file: TextIO) -> None:
|
|
63
|
+
error_file: TextIO | NoErrorOutput) -> None:
|
|
54
64
|
"""Raise if capabilities is not a Capabilities object."""
|
|
55
65
|
if not isinstance(capabilities, Capabilities):
|
|
56
66
|
error = TypeError('capabilities must be a Capabilities object.')
|
|
@@ -58,7 +68,8 @@ def _check_capabilities_value(capabilities: Capabilities,
|
|
|
58
68
|
|
|
59
69
|
|
|
60
70
|
def access_capabilities(file_access: FileAccess,
|
|
61
|
-
error_file: TextIO
|
|
71
|
+
error_file: TextIO | NoErrorOutput =
|
|
72
|
+
NO_ERROR_OUTPUT) -> Capabilities:
|
|
62
73
|
"""Return the capabilities implied by a file access mode.
|
|
63
74
|
|
|
64
75
|
Args:
|
|
@@ -80,7 +91,8 @@ def access_capabilities(file_access: FileAccess,
|
|
|
80
91
|
|
|
81
92
|
def add_access_capabilities(file_access: FileAccess,
|
|
82
93
|
capabilities: Capabilities,
|
|
83
|
-
error_file: TextIO
|
|
94
|
+
error_file: TextIO | NoErrorOutput =
|
|
95
|
+
NO_ERROR_OUTPUT) \
|
|
84
96
|
-> Capabilities:
|
|
85
97
|
"""Return capabilities with file access requirements added.
|
|
86
98
|
|
|
@@ -135,7 +147,8 @@ def _access_error_message(file_access: FileAccess) -> str:
|
|
|
135
147
|
|
|
136
148
|
def check_access_capabilities(file_access: FileAccess,
|
|
137
149
|
capabilities: Capabilities,
|
|
138
|
-
error_file: TextIO
|
|
150
|
+
error_file: TextIO | NoErrorOutput =
|
|
151
|
+
NO_ERROR_OUTPUT) -> None:
|
|
139
152
|
"""Raise if capabilities are not enough for requested access mode.
|
|
140
153
|
|
|
141
154
|
Args:
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Copyright (c) 2026 Tom Björkholm
|
|
5
5
|
# MIT License
|
|
6
6
|
|
|
7
|
-
from typing import Callable, Optional, TypeVar, cast
|
|
7
|
+
from typing import Callable, NoReturn, Optional, TypeVar, cast
|
|
8
8
|
|
|
9
9
|
from mformat.mformat import PathLike
|
|
10
10
|
from tableio.access_capability import add_access_capabilities
|
|
@@ -24,15 +24,14 @@ _FORMAT_PRIORITY = ('excel', 'ods', 'csv')
|
|
|
24
24
|
_ValueT = TypeVar('_ValueT')
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def _config_error(name: str, message: str) ->
|
|
27
|
+
def _config_error(name: str, message: str) -> NoReturn:
|
|
28
28
|
"""Raise one structured configuration error."""
|
|
29
29
|
raise ConfigError((ConfigIssue(name, message),))
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _check_default_input(capabilities: Capabilities, file_access: FileAccess,
|
|
33
33
|
format_name: Optional[str],
|
|
34
|
-
implementation: Optional[str]
|
|
35
|
-
include_all_options: bool) -> None:
|
|
34
|
+
implementation: Optional[str]) -> None:
|
|
36
35
|
"""Validate runtime values used for default selection."""
|
|
37
36
|
if not isinstance(capabilities, Capabilities):
|
|
38
37
|
_config_error('capabilities', 'must be a Capabilities object.')
|
|
@@ -42,8 +41,15 @@ def _check_default_input(capabilities: Capabilities, file_access: FileAccess,
|
|
|
42
41
|
_config_error('format_name', 'must be a string or None.')
|
|
43
42
|
if implementation is not None and not isinstance(implementation, str):
|
|
44
43
|
_config_error('implementation', 'must be a string or None.')
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _check_default_flags(include_all_options: bool,
|
|
47
|
+
impl_in_cfg: Optional[bool]) -> None:
|
|
48
|
+
"""Validate flags used for default selection."""
|
|
45
49
|
if not isinstance(include_all_options, bool):
|
|
46
50
|
_config_error('include_all_options', 'must be a bool.')
|
|
51
|
+
if impl_in_cfg is not None and not isinstance(impl_in_cfg, bool):
|
|
52
|
+
_config_error('impl_in_cfg', 'must be a bool or None.')
|
|
47
53
|
|
|
48
54
|
|
|
49
55
|
def _registered_formats_by_lower() -> dict[str, str]:
|
|
@@ -103,7 +109,6 @@ def _best_default_names(capabilities: Capabilities, format_name: Optional[str],
|
|
|
103
109
|
return selected_format, selected_impl
|
|
104
110
|
name = 'implementation' if implementation is not None else 'format_name'
|
|
105
111
|
_config_error(name, 'does not match registered TableIO backends.')
|
|
106
|
-
raise AssertionError('Unreachable default selection failure.')
|
|
107
112
|
|
|
108
113
|
|
|
109
114
|
def _base_arg_items(config: ConfigData) -> list[tuple[str, str, object]]:
|
|
@@ -184,7 +189,8 @@ def _filtered_args(config: ConfigData, capabilities: Optional[Capabilities],
|
|
|
184
189
|
return filtered
|
|
185
190
|
|
|
186
191
|
|
|
187
|
-
def _all_option_config(format_name: str,
|
|
192
|
+
def _all_option_config(format_name: str,
|
|
193
|
+
implementation: Optional[str]) -> ConfigData:
|
|
188
194
|
"""Return a configuration object with all options visible."""
|
|
189
195
|
csv = CsvConfigData(dialect=CsvDialect.UNIX, delimiter=',', quoting='all',
|
|
190
196
|
quotechar='"', lineterminator='\n', escapechar='\\')
|
|
@@ -198,10 +204,21 @@ def _all_option_config(format_name: str, implementation: str) -> ConfigData:
|
|
|
198
204
|
html=html, latex=latex)
|
|
199
205
|
|
|
200
206
|
|
|
201
|
-
def
|
|
207
|
+
def _include_default_impl(implementation: Optional[str],
|
|
208
|
+
include_all_options: bool,
|
|
209
|
+
impl_in_cfg: Optional[bool]) -> bool:
|
|
210
|
+
"""Return true if the default config should pin implementation."""
|
|
211
|
+
if impl_in_cfg is not None:
|
|
212
|
+
return impl_in_cfg
|
|
213
|
+
return implementation is not None or include_all_options
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# pylint: disable-next=too-many-arguments
|
|
217
|
+
def tio_config_default(capabilities: Capabilities, file_access: FileAccess, *,
|
|
202
218
|
format_name: Optional[str] = None,
|
|
203
219
|
implementation: Optional[str] = None,
|
|
204
|
-
include_all_options: bool = False
|
|
220
|
+
include_all_options: bool = False,
|
|
221
|
+
impl_in_cfg: Optional[bool] = None) -> ConfigData:
|
|
205
222
|
"""Return recommended default configuration data.
|
|
206
223
|
|
|
207
224
|
Default format and implementation selection first prefers implementations
|
|
@@ -211,6 +228,17 @@ def tio_config_default(capabilities: Capabilities, file_access: FileAccess,
|
|
|
211
228
|
If several implementations of the selected format match equally well,
|
|
212
229
|
their TableIO implementation priority is used.
|
|
213
230
|
|
|
231
|
+
Usually it is better to not include the implementation name in the
|
|
232
|
+
configuration, as that allows the TableIO library to choose the best
|
|
233
|
+
implementation based on the capabilities and file access at runtime.
|
|
234
|
+
If you want to include the implementation name in the configuration,
|
|
235
|
+
you can set the impl_in_cfg parameter to True. If you set it to False,
|
|
236
|
+
the implementation name will always be omitted from the configuration.
|
|
237
|
+
If you set it to None, the implementation name will normally be omitted
|
|
238
|
+
from the configuration, but if include_all_options is True or if
|
|
239
|
+
implementation was explicitly provided, the implementation name will be
|
|
240
|
+
included.
|
|
241
|
+
|
|
214
242
|
Args:
|
|
215
243
|
capabilities: Runtime capabilities the application intends to use.
|
|
216
244
|
file_access: Runtime file access requested by the application.
|
|
@@ -218,12 +246,21 @@ def tio_config_default(capabilities: Capabilities, file_access: FileAccess,
|
|
|
218
246
|
implementation: Optional preferred implementation name.
|
|
219
247
|
include_all_options: Include visible non-None values for all
|
|
220
248
|
configuration options, for teaching and configuration templates.
|
|
249
|
+
impl_in_cfg: Optional flag to include the implementation name in the
|
|
250
|
+
configuration. If None, the implementation name will normally be
|
|
251
|
+
omitted from the configuration, but if include_all_options is True
|
|
252
|
+
or if implementation was explicitly provided, the implementation
|
|
253
|
+
name will be included. If True, the implementation name will be
|
|
254
|
+
included in the configuration. If False, the implementation name
|
|
255
|
+
will always be omitted from the configuration.
|
|
221
256
|
Returns:
|
|
222
257
|
A configuration object containing durable user choices only.
|
|
223
258
|
"""
|
|
224
259
|
_check_default_input(capabilities, file_access, format_name,
|
|
225
|
-
implementation
|
|
260
|
+
implementation)
|
|
261
|
+
_check_default_flags(include_all_options, impl_in_cfg)
|
|
226
262
|
match_caps = add_access_capabilities(file_access, capabilities)
|
|
263
|
+
selected_impl: Optional[str] = None
|
|
227
264
|
selected_format, selected_impl = _best_default_names(match_caps,
|
|
228
265
|
format_name,
|
|
229
266
|
implementation)
|
|
@@ -231,6 +268,9 @@ def tio_config_default(capabilities: Capabilities, file_access: FileAccess,
|
|
|
231
268
|
selected_format = format_name
|
|
232
269
|
if implementation is not None:
|
|
233
270
|
selected_impl = implementation
|
|
271
|
+
if not _include_default_impl(implementation, include_all_options,
|
|
272
|
+
impl_in_cfg):
|
|
273
|
+
selected_impl = None
|
|
234
274
|
if include_all_options:
|
|
235
275
|
config = _all_option_config(selected_format, selected_impl)
|
|
236
276
|
else:
|
|
@@ -341,9 +381,9 @@ def tio_config_trim(config: ConfigData,
|
|
|
341
381
|
filtered_names),
|
|
342
382
|
line_length=_kept(config.line_length, 'line_length',
|
|
343
383
|
filtered_names),
|
|
344
|
-
table_max_line_length=_kept(
|
|
345
|
-
|
|
346
|
-
|
|
384
|
+
table_max_line_length=_kept(config.table_max_line_length,
|
|
385
|
+
'table_max_line_length',
|
|
386
|
+
filtered_names),
|
|
347
387
|
table_alignment=_kept(config.table_alignment,
|
|
348
388
|
'table_alignment', filtered_names),
|
|
349
389
|
csv=csv, html=html, latex=latex)
|
|
@@ -50,6 +50,16 @@ class _ScanResult(NamedTuple):
|
|
|
50
50
|
next_read_row: int
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
class _ReadLimits(NamedTuple):
|
|
54
|
+
"""Resolved row and column limits for one section read."""
|
|
55
|
+
|
|
56
|
+
left: int
|
|
57
|
+
top: int
|
|
58
|
+
bottom: int
|
|
59
|
+
scan_right: int
|
|
60
|
+
fixed_right: Optional[int]
|
|
61
|
+
|
|
62
|
+
|
|
53
63
|
class _SheetState(NamedTuple):
|
|
54
64
|
"""Sequential state tracked for one sheet during an open session."""
|
|
55
65
|
|
|
@@ -295,8 +305,7 @@ class TableIOSpreadsheetBased(TableIO):
|
|
|
295
305
|
if read_sheet is not write_sheet:
|
|
296
306
|
self._write_value_to_sheet(read_sheet, row, column, None)
|
|
297
307
|
|
|
298
|
-
def _read_limits(
|
|
299
|
-
self, box: Optional[Box]) -> tuple[int, int, int, Optional[int]]:
|
|
308
|
+
def _read_limits(self, box: Optional[Box]) -> _ReadLimits:
|
|
300
309
|
"""Return the row and column limits for a read operation."""
|
|
301
310
|
read_sheet = self._read_sheet()
|
|
302
311
|
left = 0 if box is None else box.left
|
|
@@ -304,8 +313,10 @@ class TableIOSpreadsheetBased(TableIO):
|
|
|
304
313
|
bottom = box.bottom if box is not None and \
|
|
305
314
|
box.bottom is not None else self._scan_limit_bottom(read_sheet,
|
|
306
315
|
top)
|
|
307
|
-
|
|
308
|
-
|
|
316
|
+
fixed_right = box.right if box is not None else None
|
|
317
|
+
scan_right = self._scan_limit_right(read_sheet, left, fixed_right)
|
|
318
|
+
return _ReadLimits(left=left, top=top, bottom=bottom,
|
|
319
|
+
scan_right=scan_right, fixed_right=fixed_right)
|
|
309
320
|
|
|
310
321
|
def _scan_limit_bottom(self, sheet: object, top: int) -> int:
|
|
311
322
|
"""Return the exclusive bottom limit used when scanning rows."""
|
|
@@ -353,20 +364,26 @@ class TableIOSpreadsheetBased(TableIO):
|
|
|
353
364
|
def _scan_section(self, box: Optional[Box]) -> _ScanResult:
|
|
354
365
|
"""Scan the next readable section on the active sheet."""
|
|
355
366
|
sheet = self._read_sheet()
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
367
|
+
limits = self._read_limits(box)
|
|
368
|
+
left = limits.left
|
|
369
|
+
bottom = limits.bottom
|
|
370
|
+
scan_right = limits.scan_right
|
|
371
|
+
fixed_right = limits.fixed_right
|
|
372
|
+
row = limits.top
|
|
373
|
+
last_read_row = limits.top - 1
|
|
374
|
+
while row < bottom and self._row_is_empty(sheet, row, left,
|
|
375
|
+
scan_right):
|
|
360
376
|
last_read_row = row
|
|
361
377
|
row += 1
|
|
362
378
|
headings: list[str] = []
|
|
363
|
-
while row < bottom and self._row_is_heading(sheet, row, left,
|
|
364
|
-
bottom):
|
|
365
|
-
|
|
366
|
-
|
|
379
|
+
while row < bottom and self._row_is_heading(sheet, row, left,
|
|
380
|
+
scan_right, bottom):
|
|
381
|
+
headings.append(value_to_str(self._cell_value(sheet, row, left),
|
|
382
|
+
none_is_empty=True))
|
|
367
383
|
last_read_row = row
|
|
368
384
|
row += 1
|
|
369
|
-
while row < bottom and self._row_is_empty(sheet, row, left,
|
|
385
|
+
while row < bottom and self._row_is_empty(sheet, row, left,
|
|
386
|
+
scan_right):
|
|
370
387
|
last_read_row = row
|
|
371
388
|
row += 1
|
|
372
389
|
table_top = row
|
|
@@ -374,17 +391,17 @@ class TableIOSpreadsheetBased(TableIO):
|
|
|
374
391
|
table_right = left
|
|
375
392
|
while row < bottom:
|
|
376
393
|
nonempty_columns = self._row_nonempty_columns(sheet, row, left,
|
|
377
|
-
|
|
394
|
+
scan_right)
|
|
378
395
|
if not nonempty_columns:
|
|
379
396
|
last_read_row = row
|
|
380
397
|
row += 1
|
|
381
398
|
break
|
|
382
399
|
last_read_row = row
|
|
383
400
|
table_bottom = row + 1
|
|
384
|
-
if
|
|
401
|
+
if fixed_right is None:
|
|
385
402
|
table_right = max(table_right, max(nonempty_columns) + 1)
|
|
386
403
|
else:
|
|
387
|
-
table_right =
|
|
404
|
+
table_right = fixed_right
|
|
388
405
|
row += 1
|
|
389
406
|
return _ScanResult(headings=headings, table_top=table_top,
|
|
390
407
|
table_bottom=table_bottom, table_left=left,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tableio
|
|
3
|
-
Version: 0
|
|
3
|
+
Version: 1.0
|
|
4
4
|
Summary: Uniform way to write table data to and read from different file formats
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -17,8 +17,8 @@ Requires-Dist: build>=1.5.0
|
|
|
17
17
|
Requires-Dist: wheel>=0.47.0
|
|
18
18
|
Requires-Dist: mformat-ext>=0.6
|
|
19
19
|
Requires-Dist: openpyxl>=3.1.5
|
|
20
|
-
Requires-Dist: types-openpyxl>=3.1.5.
|
|
21
|
-
Requires-Dist: odfdo>=3.22.
|
|
20
|
+
Requires-Dist: types-openpyxl>=3.1.5.20260518
|
|
21
|
+
Requires-Dist: odfdo>=3.22.8
|
|
22
22
|
Requires-Dist: xlsxwriter>=3.2.9
|
|
23
23
|
Requires-Dist: pylightxl>=1.61
|
|
24
24
|
Dynamic: author
|
|
@@ -137,9 +137,9 @@ a better start by reading the examples.
|
|
|
137
137
|
|
|
138
138
|
## Test summary
|
|
139
139
|
|
|
140
|
-
- Test result:
|
|
140
|
+
- Test result: 1347 passed in 22s
|
|
141
141
|
- No flake8 warnings.
|
|
142
142
|
- No mypy errors found.
|
|
143
143
|
- No python layout warnings.
|
|
144
|
-
- Built version(s): 0
|
|
145
|
-
- Build and test using Python 3.14.
|
|
144
|
+
- Built version(s): 1.0
|
|
145
|
+
- Build and test using Python 3.14.5
|
|
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
|