tableio 0.7__tar.gz → 0.8__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.7/tableio.egg-info → tableio-0.8}/PKG-INFO +4 -3
- {tableio-0.7 → tableio-0.8}/README.md +3 -2
- {tableio-0.7 → tableio-0.8}/README_pypi.md +3 -2
- {tableio-0.7 → tableio-0.8}/pyproject.toml +1 -1
- {tableio-0.7 → tableio-0.8}/setup.py +1 -1
- {tableio-0.7 → tableio-0.8}/src/tableio/__init__.py +23 -1
- tableio-0.8/src/tableio/access_capability.py +156 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/border_helper.py +26 -50
- {tableio-0.7 → tableio-0.8}/src/tableio/capability.py +1 -2
- tableio-0.8/src/tableio/config_data.py +117 -0
- tableio-0.8/src/tableio/config_data_apply.py +403 -0
- tableio-0.8/src/tableio/config_data_describe.py +224 -0
- tableio-0.8/src/tableio/config_data_error.py +55 -0
- tableio-0.8/src/tableio/config_data_validate.py +334 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/factory.py +57 -79
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio.py +17 -32
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_csv.py +1 -2
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_excel_openpyxl.py +22 -35
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_excel_pylightxl.py +21 -33
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_excel_xlsxwriter.py +15 -27
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_excelbased.py +4 -6
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_mformat.py +49 -82
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_mformatbased.py +8 -14
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_ods_odfdo.py +15 -26
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_spreadsheetbased.py +28 -37
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_textbased.py +1 -2
- {tableio-0.7 → tableio-0.8}/src/tableio/value_type.py +6 -12
- {tableio-0.7 → tableio-0.8}/src/tableio/valueconversion.py +4 -8
- {tableio-0.7 → tableio-0.8/tableio.egg-info}/PKG-INFO +4 -3
- {tableio-0.7 → tableio-0.8}/tableio.egg-info/SOURCES.txt +6 -0
- {tableio-0.7 → tableio-0.8}/LICENSE.txt +0 -0
- {tableio-0.7 → tableio-0.8}/setup.cfg +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/_archive_rewrite.py +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/color.py +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/optional_args.py +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/py.typed +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/reg_pkg_formats.py +0 -0
- {tableio-0.7 → tableio-0.8}/src/tableio/tableio_types.py +0 -0
- {tableio-0.7 → tableio-0.8}/tableio.egg-info/dependency_links.txt +0 -0
- {tableio-0.7 → tableio-0.8}/tableio.egg-info/requires.txt +0 -0
- {tableio-0.7 → tableio-0.8}/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: 0.8
|
|
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>
|
|
@@ -137,8 +137,9 @@ a better start by reading the examples.
|
|
|
137
137
|
|
|
138
138
|
## Test summary
|
|
139
139
|
|
|
140
|
-
- Test result:
|
|
140
|
+
- Test result: 1311 passed in 19s
|
|
141
141
|
- No flake8 warnings.
|
|
142
142
|
- No mypy errors found.
|
|
143
|
-
-
|
|
143
|
+
- No python layout warnings.
|
|
144
|
+
- Built version(s): 0.8
|
|
144
145
|
- Build and test using Python 3.14.4
|
|
@@ -93,8 +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: 1311 passed in 19s
|
|
97
97
|
- No flake8 warnings.
|
|
98
98
|
- No mypy errors found.
|
|
99
|
-
-
|
|
99
|
+
- No python layout warnings.
|
|
100
|
+
- Built version(s): 0.8
|
|
100
101
|
- Build and test using Python 3.14.4
|
|
@@ -109,8 +109,9 @@ a better start by reading the examples.
|
|
|
109
109
|
|
|
110
110
|
## Test summary
|
|
111
111
|
|
|
112
|
-
- Test result:
|
|
112
|
+
- Test result: 1311 passed in 19s
|
|
113
113
|
- No flake8 warnings.
|
|
114
114
|
- No mypy errors found.
|
|
115
|
-
-
|
|
115
|
+
- No python layout warnings.
|
|
116
|
+
- Built version(s): 0.8
|
|
116
117
|
- Build and test using Python 3.14.4
|
|
@@ -7,7 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
from tableio.capability import CAP_IGNORABLE, CAP_NEEDED, CAP_NOT_USED, \
|
|
9
9
|
Capabilities
|
|
10
|
+
from tableio.access_capability import NO_ERROR_OUTPUT, \
|
|
11
|
+
InsufficientCapabilities, access_capabilities, add_access_capabilities, \
|
|
12
|
+
check_access_capabilities
|
|
10
13
|
from tableio.color import Color
|
|
14
|
+
from tableio.config_data import ConfigData, CsvConfigData, HtmlConfigData, \
|
|
15
|
+
LatexConfigData
|
|
16
|
+
from tableio.config_data_apply import tio_config_create, tio_config_default, \
|
|
17
|
+
tio_config_ignored_names, tio_config_optional_args, tio_config_trim
|
|
18
|
+
from tableio.config_data_describe import ConfigSpec, tio_config_describe, \
|
|
19
|
+
tio_config_descriptions, tio_config_specs
|
|
20
|
+
from tableio.config_data_error import ConfigError, ConfigIssue
|
|
21
|
+
from tableio.config_data_validate import tio_config_validate
|
|
11
22
|
from tableio.factory import create_tableio, filter_args_tableio, \
|
|
12
23
|
list_implementations_tableio, list_registered_tableio
|
|
13
24
|
from tableio.optional_args import CsvDialect, OptionalArgs, OptionalArgsDict
|
|
@@ -40,10 +51,21 @@ __all__ = ['create_tableio',
|
|
|
40
51
|
'CellT',
|
|
41
52
|
'Color',
|
|
42
53
|
'Capabilities',
|
|
54
|
+
'NO_ERROR_OUTPUT',
|
|
55
|
+
'InsufficientCapabilities',
|
|
43
56
|
'CAP_NEEDED',
|
|
44
57
|
'CAP_NOT_USED',
|
|
45
58
|
'CAP_IGNORABLE',
|
|
59
|
+
'access_capabilities',
|
|
60
|
+
'add_access_capabilities',
|
|
61
|
+
'check_access_capabilities',
|
|
46
62
|
'filter_args_tableio',
|
|
47
63
|
'list_registered_tableio',
|
|
48
64
|
'list_implementations_tableio',
|
|
49
|
-
'CsvDialect'
|
|
65
|
+
'CsvDialect',
|
|
66
|
+
'ConfigData', 'ConfigError', 'ConfigIssue', 'ConfigSpec',
|
|
67
|
+
'CsvConfigData', 'HtmlConfigData', 'LatexConfigData',
|
|
68
|
+
'tio_config_create', 'tio_config_default',
|
|
69
|
+
'tio_config_describe', 'tio_config_descriptions',
|
|
70
|
+
'tio_config_ignored_names', 'tio_config_optional_args',
|
|
71
|
+
'tio_config_specs', 'tio_config_trim', 'tio_config_validate']
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
"""Helpers that connect file access modes to capability requests."""
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2026 Tom Björkholm
|
|
5
|
+
# MIT License
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import NoReturn, TextIO
|
|
9
|
+
|
|
10
|
+
from tableio.capability import CAP_NEEDED, Capabilities
|
|
11
|
+
from tableio.tableio_types import FileAccess
|
|
12
|
+
|
|
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."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class InsufficientCapabilities(ValueError):
|
|
20
|
+
"""Raised when requested capabilities contradict requested file access.
|
|
21
|
+
|
|
22
|
+
Error raised when the caller supplies file access and explicit
|
|
23
|
+
Capabilities, but the requested capabilities do not include the capability
|
|
24
|
+
implied by that access mode. For example, READ requires can_read, CREATE
|
|
25
|
+
requires can_write, and UPDATE requires both.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, message: str, capability_names: tuple[str, ...] =
|
|
29
|
+
()) -> None:
|
|
30
|
+
"""Initialize the exception."""
|
|
31
|
+
self.capability_names = capability_names
|
|
32
|
+
super().__init__(message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _raise_error(error_file: TextIO, error: Exception) -> NoReturn:
|
|
36
|
+
"""Write an error message and raise the error."""
|
|
37
|
+
error_file.write(str(error) + '\n')
|
|
38
|
+
raise error
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _check_access_value(file_access: FileAccess, error_file: TextIO) -> None:
|
|
42
|
+
"""Raise if file access is not a supported FileAccess value."""
|
|
43
|
+
if not isinstance(file_access, FileAccess):
|
|
44
|
+
error = TypeError('file_access must be a FileAccess value.')
|
|
45
|
+
_raise_error(error_file, error)
|
|
46
|
+
valid_access = (FileAccess.READ, FileAccess.CREATE, FileAccess.UPDATE)
|
|
47
|
+
if file_access not in valid_access:
|
|
48
|
+
error = ValueError(f'unsupported file access: {file_access!r}.')
|
|
49
|
+
_raise_error(error_file, error)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _check_capabilities_value(capabilities: Capabilities,
|
|
53
|
+
error_file: TextIO) -> None:
|
|
54
|
+
"""Raise if capabilities is not a Capabilities object."""
|
|
55
|
+
if not isinstance(capabilities, Capabilities):
|
|
56
|
+
error = TypeError('capabilities must be a Capabilities object.')
|
|
57
|
+
_raise_error(error_file, error)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def access_capabilities(file_access: FileAccess,
|
|
61
|
+
error_file: TextIO = NO_ERROR_OUTPUT) -> Capabilities:
|
|
62
|
+
"""Return the capabilities implied by a file access mode.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
file_access: File access mode to convert to capability requirements.
|
|
66
|
+
error_file: Text stream receiving error messages before exceptions.
|
|
67
|
+
Raises:
|
|
68
|
+
TypeError: If file_access is not a FileAccess value.
|
|
69
|
+
ValueError: If file_access is an unsupported FileAccess value.
|
|
70
|
+
Returns:
|
|
71
|
+
Capabilities needed for the requested file access.
|
|
72
|
+
"""
|
|
73
|
+
_check_access_value(file_access, error_file)
|
|
74
|
+
if file_access == FileAccess.READ:
|
|
75
|
+
return Capabilities(can_read=CAP_NEEDED)
|
|
76
|
+
if file_access == FileAccess.CREATE:
|
|
77
|
+
return Capabilities(can_write=CAP_NEEDED)
|
|
78
|
+
return Capabilities(can_read=CAP_NEEDED, can_write=CAP_NEEDED)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def add_access_capabilities(file_access: FileAccess,
|
|
82
|
+
capabilities: Capabilities,
|
|
83
|
+
error_file: TextIO = NO_ERROR_OUTPUT) \
|
|
84
|
+
-> Capabilities:
|
|
85
|
+
"""Return capabilities with file access requirements added.
|
|
86
|
+
|
|
87
|
+
The original capabilities object is not mutated.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
file_access: File access mode to add capability requirements for.
|
|
91
|
+
capabilities: Existing capability requirements to extend.
|
|
92
|
+
error_file: Text stream receiving error messages before exceptions.
|
|
93
|
+
Raises:
|
|
94
|
+
TypeError: If file_access or capabilities has an invalid type.
|
|
95
|
+
ValueError: If file_access is an unsupported FileAccess value.
|
|
96
|
+
Returns:
|
|
97
|
+
New Capabilities object including the file access requirements.
|
|
98
|
+
"""
|
|
99
|
+
_check_access_value(file_access, error_file)
|
|
100
|
+
_check_capabilities_value(capabilities, error_file)
|
|
101
|
+
if file_access == FileAccess.READ:
|
|
102
|
+
return capabilities._replace(can_read=CAP_NEEDED)
|
|
103
|
+
if file_access == FileAccess.CREATE:
|
|
104
|
+
return capabilities._replace(can_write=CAP_NEEDED)
|
|
105
|
+
return capabilities._replace(can_read=CAP_NEEDED, can_write=CAP_NEEDED)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _missing_access_caps(file_access: FileAccess,
|
|
109
|
+
capabilities: Capabilities) -> tuple[str, ...]:
|
|
110
|
+
"""Return capability names missing for the requested access mode."""
|
|
111
|
+
if file_access == FileAccess.READ and not capabilities.can_read.supported:
|
|
112
|
+
return ('can_read',)
|
|
113
|
+
if file_access == FileAccess.CREATE and \
|
|
114
|
+
not capabilities.can_write.supported:
|
|
115
|
+
return ('can_write',)
|
|
116
|
+
if file_access == FileAccess.UPDATE:
|
|
117
|
+
missing: list[str] = []
|
|
118
|
+
if not capabilities.can_read.supported:
|
|
119
|
+
missing.append('can_read')
|
|
120
|
+
if not capabilities.can_write.supported:
|
|
121
|
+
missing.append('can_write')
|
|
122
|
+
return tuple(missing)
|
|
123
|
+
return ()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _access_error_message(file_access: FileAccess) -> str:
|
|
127
|
+
"""Return the error message for missing access capabilities."""
|
|
128
|
+
if file_access == FileAccess.READ:
|
|
129
|
+
return 'FileAccess.READ requires can_read capability.'
|
|
130
|
+
if file_access == FileAccess.CREATE:
|
|
131
|
+
return 'FileAccess.CREATE requires can_write capability.'
|
|
132
|
+
return 'FileAccess.UPDATE requires both can_read and can_write ' + \
|
|
133
|
+
'capabilities.'
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def check_access_capabilities(file_access: FileAccess,
|
|
137
|
+
capabilities: Capabilities,
|
|
138
|
+
error_file: TextIO = NO_ERROR_OUTPUT) -> None:
|
|
139
|
+
"""Raise if capabilities are not enough for requested access mode.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
file_access: File access mode to check capability requirements for.
|
|
143
|
+
capabilities: Capability requirements to check.
|
|
144
|
+
error_file: Text stream receiving error messages before exceptions.
|
|
145
|
+
Raises:
|
|
146
|
+
TypeError: If file_access or capabilities has an invalid type.
|
|
147
|
+
ValueError: If file_access is an unsupported FileAccess value.
|
|
148
|
+
InsufficientCapabilities: If capabilities do not support file_access.
|
|
149
|
+
"""
|
|
150
|
+
_check_access_value(file_access, error_file)
|
|
151
|
+
_check_capabilities_value(capabilities, error_file)
|
|
152
|
+
missing_caps = _missing_access_caps(file_access, capabilities)
|
|
153
|
+
if missing_caps:
|
|
154
|
+
message = _access_error_message(file_access)
|
|
155
|
+
error = InsufficientCapabilities(message, missing_caps)
|
|
156
|
+
_raise_error(error_file, error)
|
|
@@ -30,11 +30,8 @@ class CellBorder(NamedTuple):
|
|
|
30
30
|
left: BorderWeight
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
NO_BORDERS = CellBorder(
|
|
34
|
-
|
|
35
|
-
BorderWeight.NONE,
|
|
36
|
-
BorderWeight.NONE,
|
|
37
|
-
BorderWeight.NONE)
|
|
33
|
+
NO_BORDERS = CellBorder(BorderWeight.NONE, BorderWeight.NONE,
|
|
34
|
+
BorderWeight.NONE, BorderWeight.NONE)
|
|
38
35
|
"""The absence of borders on all four cell edges."""
|
|
39
36
|
|
|
40
37
|
|
|
@@ -68,61 +65,40 @@ def _thicker(first: BorderWeight, second: BorderWeight) -> BorderWeight:
|
|
|
68
65
|
|
|
69
66
|
_STYLE_COMPONENTS: dict[TableBorderStyle, _BorderComponents] = {
|
|
70
67
|
TableBorderStyle.NONE: _BorderComponents(
|
|
71
|
-
outer=BorderWeight.NONE,
|
|
72
|
-
|
|
73
|
-
inner_horizontal=BorderWeight.NONE,
|
|
74
|
-
inner_vertical=BorderWeight.NONE),
|
|
68
|
+
outer=BorderWeight.NONE, first_row_separator=BorderWeight.NONE,
|
|
69
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
75
70
|
TableBorderStyle.OUTER_THIN: _BorderComponents(
|
|
76
|
-
outer=BorderWeight.THIN,
|
|
77
|
-
|
|
78
|
-
inner_horizontal=BorderWeight.NONE,
|
|
79
|
-
inner_vertical=BorderWeight.NONE),
|
|
71
|
+
outer=BorderWeight.THIN, first_row_separator=BorderWeight.NONE,
|
|
72
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
80
73
|
TableBorderStyle.OUTER_THICK: _BorderComponents(
|
|
81
|
-
outer=BorderWeight.THICK,
|
|
82
|
-
|
|
83
|
-
inner_horizontal=BorderWeight.NONE,
|
|
84
|
-
inner_vertical=BorderWeight.NONE),
|
|
74
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.NONE,
|
|
75
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
85
76
|
TableBorderStyle.OUTER_FIRST_ROW_THIN: _BorderComponents(
|
|
86
|
-
outer=BorderWeight.THIN,
|
|
87
|
-
|
|
88
|
-
inner_horizontal=BorderWeight.NONE,
|
|
89
|
-
inner_vertical=BorderWeight.NONE),
|
|
77
|
+
outer=BorderWeight.THIN, first_row_separator=BorderWeight.THIN,
|
|
78
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
90
79
|
TableBorderStyle.OUTER_FIRST_ROW_THICK: _BorderComponents(
|
|
91
|
-
outer=BorderWeight.THICK,
|
|
92
|
-
|
|
93
|
-
inner_horizontal=BorderWeight.NONE,
|
|
94
|
-
inner_vertical=BorderWeight.NONE),
|
|
80
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.THICK,
|
|
81
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
95
82
|
TableBorderStyle.OUTER_THICK_FIRST_ROW_THIN: _BorderComponents(
|
|
96
|
-
outer=BorderWeight.THICK,
|
|
97
|
-
|
|
98
|
-
inner_horizontal=BorderWeight.NONE,
|
|
99
|
-
inner_vertical=BorderWeight.NONE),
|
|
83
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.THIN,
|
|
84
|
+
inner_horizontal=BorderWeight.NONE, inner_vertical=BorderWeight.NONE),
|
|
100
85
|
TableBorderStyle.OUTER_FIRST_ROW_THICK_VERTICAL_THIN:
|
|
101
|
-
_BorderComponents(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
inner_vertical=BorderWeight.THIN),
|
|
86
|
+
_BorderComponents(outer=BorderWeight.THICK,
|
|
87
|
+
first_row_separator=BorderWeight.THICK,
|
|
88
|
+
inner_horizontal=BorderWeight.NONE,
|
|
89
|
+
inner_vertical=BorderWeight.THIN),
|
|
106
90
|
TableBorderStyle.OUTER_FIRST_ROW_THICK_INNER_THIN: _BorderComponents(
|
|
107
|
-
outer=BorderWeight.THICK,
|
|
108
|
-
|
|
109
|
-
inner_horizontal=BorderWeight.THIN,
|
|
110
|
-
inner_vertical=BorderWeight.THIN),
|
|
91
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.THICK,
|
|
92
|
+
inner_horizontal=BorderWeight.THIN, inner_vertical=BorderWeight.THIN),
|
|
111
93
|
TableBorderStyle.OUTER_THICK_INNER_THIN: _BorderComponents(
|
|
112
|
-
outer=BorderWeight.THICK,
|
|
113
|
-
|
|
114
|
-
inner_horizontal=BorderWeight.THIN,
|
|
115
|
-
inner_vertical=BorderWeight.THIN),
|
|
94
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.NONE,
|
|
95
|
+
inner_horizontal=BorderWeight.THIN, inner_vertical=BorderWeight.THIN),
|
|
116
96
|
TableBorderStyle.ALL_THIN: _BorderComponents(
|
|
117
|
-
outer=BorderWeight.THIN,
|
|
118
|
-
|
|
119
|
-
inner_horizontal=BorderWeight.THIN,
|
|
120
|
-
inner_vertical=BorderWeight.THIN),
|
|
97
|
+
outer=BorderWeight.THIN, first_row_separator=BorderWeight.NONE,
|
|
98
|
+
inner_horizontal=BorderWeight.THIN, inner_vertical=BorderWeight.THIN),
|
|
121
99
|
TableBorderStyle.ALL_THICK: _BorderComponents(
|
|
122
|
-
outer=BorderWeight.THICK,
|
|
123
|
-
|
|
124
|
-
inner_horizontal=BorderWeight.THICK,
|
|
125
|
-
inner_vertical=BorderWeight.THICK)
|
|
100
|
+
outer=BorderWeight.THICK, first_row_separator=BorderWeight.NONE,
|
|
101
|
+
inner_horizontal=BorderWeight.THICK, inner_vertical=BorderWeight.THICK)
|
|
126
102
|
}
|
|
127
103
|
|
|
128
104
|
|
|
@@ -125,8 +125,7 @@ def single_capability_match(offered: SingleCapability,
|
|
|
125
125
|
return False
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
def capability_match(offered: Capabilities,
|
|
129
|
-
will_use: Capabilities,
|
|
128
|
+
def capability_match(offered: Capabilities, will_use: Capabilities,
|
|
130
129
|
ignore_allowed: bool = False) -> bool:
|
|
131
130
|
"""Check if the offered capabilities match the required capabilities.
|
|
132
131
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""Configuration data for the tableio package."""
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2026 Tom Björkholm
|
|
5
|
+
# MIT License
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from tableio.optional_args import CsvDialect
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class CsvConfigData:
|
|
14
|
+
"""CSV-specific configuration values.
|
|
15
|
+
|
|
16
|
+
This class holds durable user choices that only have meaning for CSV
|
|
17
|
+
output. The values are allowed to be present even when another format is
|
|
18
|
+
selected; helper functions decide whether they are relevant for the
|
|
19
|
+
current TableIO backend. The class has no configuration-framework base
|
|
20
|
+
class, so adapter libraries may combine it with their own base classes.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
dialect: Optional[CsvDialect] = None
|
|
24
|
+
"""The CSV dialect template to use, or ``None`` for backend default."""
|
|
25
|
+
|
|
26
|
+
delimiter: Optional[str] = None
|
|
27
|
+
"""The CSV delimiter to use, or ``None`` for backend default."""
|
|
28
|
+
|
|
29
|
+
quoting: Optional[str] = None
|
|
30
|
+
"""The CSV quoting style to use, or ``None`` for backend default."""
|
|
31
|
+
|
|
32
|
+
quotechar: Optional[str] = None
|
|
33
|
+
"""The CSV quote character to use, or ``None`` for backend default."""
|
|
34
|
+
|
|
35
|
+
lineterminator: Optional[str] = None
|
|
36
|
+
"""The CSV line terminator to use, or ``None`` for backend default."""
|
|
37
|
+
|
|
38
|
+
escapechar: Optional[str] = None
|
|
39
|
+
"""The CSV escape character to use, or ``None`` for backend default."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class HtmlConfigData:
|
|
44
|
+
"""HTML-specific configuration values.
|
|
45
|
+
|
|
46
|
+
The class has no configuration-framework base class, so adapter
|
|
47
|
+
libraries may combine it with their own base classes.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
css_file: Optional[str] = None
|
|
51
|
+
"""The CSS file path or URL to reference, or ``None`` for no CSS file."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class LatexConfigData:
|
|
56
|
+
"""LaTeX-specific configuration values.
|
|
57
|
+
|
|
58
|
+
The class has no configuration-framework base class, so adapter
|
|
59
|
+
libraries may combine it with their own base classes.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
document_class: Optional[str] = None
|
|
63
|
+
"""The LaTeX document class to use, or ``None`` for backend default."""
|
|
64
|
+
|
|
65
|
+
preamble: Optional[str] = None
|
|
66
|
+
"""Extra LaTeX preamble text, or ``None`` for backend default."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ConfigData: # pylint: disable=too-many-instance-attributes
|
|
71
|
+
"""Durable, framework-neutral TableIO configuration choices.
|
|
72
|
+
|
|
73
|
+
This class intentionally excludes runtime intent. File names, file access,
|
|
74
|
+
capabilities and callbacks are supplied to helper functions instead of
|
|
75
|
+
being stored as configuration values. The default format is Excel, and
|
|
76
|
+
format-specific nested sections default to ``None`` until a user or
|
|
77
|
+
application deliberately configures them.
|
|
78
|
+
|
|
79
|
+
The class has no configuration-framework base class, so adapter libraries
|
|
80
|
+
may combine it with their own base classes.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
format_name: str = 'excel'
|
|
84
|
+
"""The TableIO format name. Matching is case-insensitive."""
|
|
85
|
+
|
|
86
|
+
implementation: Optional[str] = None
|
|
87
|
+
"""The optional implementation pin, or ``None`` to choose best match."""
|
|
88
|
+
|
|
89
|
+
character_encoding: Optional[str] = None
|
|
90
|
+
"""The text encoding to use, or ``None`` for backend default."""
|
|
91
|
+
|
|
92
|
+
language: Optional[str] = None
|
|
93
|
+
"""The document language value mapped to backend ``lang`` arguments."""
|
|
94
|
+
|
|
95
|
+
title: Optional[str] = None
|
|
96
|
+
"""The document title, or ``None`` for backend default."""
|
|
97
|
+
|
|
98
|
+
paper_size: Optional[str] = None
|
|
99
|
+
"""The document paper size, or ``None`` for backend default."""
|
|
100
|
+
|
|
101
|
+
line_length: Optional[int] = None
|
|
102
|
+
"""The preferred text line length, or ``None`` for backend default."""
|
|
103
|
+
|
|
104
|
+
table_max_line_length: Optional[int] = None
|
|
105
|
+
"""The preferred text table line length, or ``None`` for default."""
|
|
106
|
+
|
|
107
|
+
table_alignment: Optional[str] = None
|
|
108
|
+
"""The preferred text table alignment, or ``None`` for default."""
|
|
109
|
+
|
|
110
|
+
csv: Optional[CsvConfigData] = None
|
|
111
|
+
"""CSV-specific configuration values, or ``None`` when unset."""
|
|
112
|
+
|
|
113
|
+
html: Optional[HtmlConfigData] = None
|
|
114
|
+
"""HTML-specific configuration values, or ``None`` when unset."""
|
|
115
|
+
|
|
116
|
+
latex: Optional[LatexConfigData] = None
|
|
117
|
+
"""LaTeX-specific configuration values, or ``None`` when unset."""
|