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.
Files changed (41) hide show
  1. {tableio-0.8/tableio.egg-info → tableio-1.0}/PKG-INFO +6 -6
  2. {tableio-0.8 → tableio-1.0}/README.md +3 -3
  3. {tableio-0.8 → tableio-1.0}/README_pypi.md +3 -3
  4. {tableio-0.8 → tableio-1.0}/pyproject.toml +1 -1
  5. {tableio-0.8 → tableio-1.0}/setup.py +3 -3
  6. {tableio-0.8 → tableio-1.0}/src/tableio/access_capability.py +23 -10
  7. {tableio-0.8 → tableio-1.0}/src/tableio/config_data_apply.py +52 -12
  8. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_spreadsheetbased.py +33 -16
  9. {tableio-0.8 → tableio-1.0/tableio.egg-info}/PKG-INFO +6 -6
  10. {tableio-0.8 → tableio-1.0}/tableio.egg-info/requires.txt +2 -2
  11. {tableio-0.8 → tableio-1.0}/LICENSE.txt +0 -0
  12. {tableio-0.8 → tableio-1.0}/setup.cfg +0 -0
  13. {tableio-0.8 → tableio-1.0}/src/tableio/__init__.py +0 -0
  14. {tableio-0.8 → tableio-1.0}/src/tableio/_archive_rewrite.py +0 -0
  15. {tableio-0.8 → tableio-1.0}/src/tableio/border_helper.py +0 -0
  16. {tableio-0.8 → tableio-1.0}/src/tableio/capability.py +0 -0
  17. {tableio-0.8 → tableio-1.0}/src/tableio/color.py +0 -0
  18. {tableio-0.8 → tableio-1.0}/src/tableio/config_data.py +0 -0
  19. {tableio-0.8 → tableio-1.0}/src/tableio/config_data_describe.py +0 -0
  20. {tableio-0.8 → tableio-1.0}/src/tableio/config_data_error.py +0 -0
  21. {tableio-0.8 → tableio-1.0}/src/tableio/config_data_validate.py +0 -0
  22. {tableio-0.8 → tableio-1.0}/src/tableio/factory.py +0 -0
  23. {tableio-0.8 → tableio-1.0}/src/tableio/optional_args.py +0 -0
  24. {tableio-0.8 → tableio-1.0}/src/tableio/py.typed +0 -0
  25. {tableio-0.8 → tableio-1.0}/src/tableio/reg_pkg_formats.py +0 -0
  26. {tableio-0.8 → tableio-1.0}/src/tableio/tableio.py +0 -0
  27. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_csv.py +0 -0
  28. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_openpyxl.py +0 -0
  29. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_pylightxl.py +0 -0
  30. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excel_xlsxwriter.py +0 -0
  31. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_excelbased.py +0 -0
  32. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_mformat.py +0 -0
  33. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_mformatbased.py +0 -0
  34. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_ods_odfdo.py +0 -0
  35. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_textbased.py +0 -0
  36. {tableio-0.8 → tableio-1.0}/src/tableio/tableio_types.py +0 -0
  37. {tableio-0.8 → tableio-1.0}/src/tableio/value_type.py +0 -0
  38. {tableio-0.8 → tableio-1.0}/src/tableio/valueconversion.py +0 -0
  39. {tableio-0.8 → tableio-1.0}/tableio.egg-info/SOURCES.txt +0 -0
  40. {tableio-0.8 → tableio-1.0}/tableio.egg-info/dependency_links.txt +0 -0
  41. {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.8
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.20260402
21
- Requires-Dist: odfdo>=3.22.7
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: 1311 passed in 19s
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.8
145
- - Build and test using Python 3.14.4
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: 1311 passed in 19s
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.8
101
- - Build and test using Python 3.14.4
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: 1311 passed in 19s
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.8
117
- - Build and test using Python 3.14.4
116
+ - Built version(s): 1.0
117
+ - Build and test using Python 3.14.5
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tableio"
7
- version = "0.8"
7
+ version = "1.0"
8
8
  authors = [
9
9
  { name="Tom Björkholm", email="klausuler_linnet0q@icloud.com" },
10
10
  ]
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='tableio',
8
- version='0.8',
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.20260402',
25
- 'odfdo >= 3.22.7',
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 os
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
- # pylint: disable-next=consider-using-with
15
- NO_ERROR_OUTPUT: TextIO = open(os.devnull, 'w', encoding='utf-8')
16
- """Text stream used when helper errors should not be printed."""
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, error: Exception) -> NoReturn:
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, error_file: TextIO) -> None:
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 = NO_ERROR_OUTPUT) -> Capabilities:
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 = NO_ERROR_OUTPUT) \
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 = NO_ERROR_OUTPUT) -> None:
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) -> None:
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, implementation: str) -> ConfigData:
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 tio_config_default(capabilities: Capabilities, file_access: FileAccess,
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) -> ConfigData:
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, include_all_options)
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
- config.table_max_line_length,
346
- 'table_max_line_length', filtered_names),
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
- right = box.right if box is not None else None
308
- return left, top, bottom, right
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
- left, top, bottom, right = self._read_limits(box)
357
- row = top
358
- last_read_row = top - 1
359
- while row < bottom and self._row_is_empty(sheet, row, left, right):
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, right,
364
- bottom):
365
- heading = self._cell_value(sheet, row, left)
366
- headings.append(value_to_str(heading, none_is_empty=True))
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, right):
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
- right)
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 right is None:
401
+ if fixed_right is None:
385
402
  table_right = max(table_right, max(nonempty_columns) + 1)
386
403
  else:
387
- table_right = 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.8
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.20260402
21
- Requires-Dist: odfdo>=3.22.7
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: 1311 passed in 19s
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.8
145
- - Build and test using Python 3.14.4
144
+ - Built version(s): 1.0
145
+ - Build and test using Python 3.14.5
@@ -4,7 +4,7 @@ build>=1.5.0
4
4
  wheel>=0.47.0
5
5
  mformat-ext>=0.6
6
6
  openpyxl>=3.1.5
7
- types-openpyxl>=3.1.5.20260402
8
- odfdo>=3.22.7
7
+ types-openpyxl>=3.1.5.20260518
8
+ odfdo>=3.22.8
9
9
  xlsxwriter>=3.2.9
10
10
  pylightxl>=1.61
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