tableio 1.0__tar.gz → 1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tableio-1.0 → tableio-1.2}/PKG-INFO +56 -15
- {tableio-1.0 → tableio-1.2}/README.md +6 -5
- tableio-1.0/tableio.egg-info/PKG-INFO → tableio-1.2/README_pypi.md +45 -34
- {tableio-1.0 → tableio-1.2}/pyproject.toml +9 -4
- {tableio-1.0 → tableio-1.2}/setup.py +5 -9
- {tableio-1.0 → tableio-1.2}/src/tableio/__init__.py +6 -1
- {tableio-1.0 → tableio-1.2}/src/tableio/config_data.py +8 -1
- {tableio-1.0 → tableio-1.2}/src/tableio/config_data_apply.py +30 -6
- {tableio-1.0 → tableio-1.2}/src/tableio/config_data_describe.py +13 -1
- {tableio-1.0 → tableio-1.2}/src/tableio/config_data_validate.py +5 -1
- {tableio-1.0 → tableio-1.2}/src/tableio/factory.py +28 -12
- tableio-1.2/src/tableio/optional_args.py +143 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio.py +17 -2
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_csv.py +106 -23
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_excel_openpyxl.py +20 -5
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_excel_pylightxl.py +96 -23
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_excel_xlsxwriter.py +18 -13
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_excelbased.py +11 -3
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_mformat.py +73 -27
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_mformatbased.py +30 -6
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_ods_odfdo.py +114 -11
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_spreadsheetbased.py +20 -7
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_textbased.py +15 -8
- tableio-1.2/src/tableio/timedelta_helpers.py +179 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/value_type.py +2 -2
- {tableio-1.0 → tableio-1.2}/src/tableio/valueconversion.py +70 -35
- tableio-1.0/README_pypi.md → tableio-1.2/tableio.egg-info/PKG-INFO +75 -6
- {tableio-1.0 → tableio-1.2}/tableio.egg-info/SOURCES.txt +1 -0
- tableio-1.2/tableio.egg-info/requires.txt +6 -0
- tableio-1.0/src/tableio/optional_args.py +0 -77
- tableio-1.0/tableio.egg-info/requires.txt +0 -10
- {tableio-1.0 → tableio-1.2}/LICENSE.txt +0 -0
- {tableio-1.0 → tableio-1.2}/setup.cfg +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/_archive_rewrite.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/access_capability.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/border_helper.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/capability.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/color.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/config_data_error.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/py.typed +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/reg_pkg_formats.py +0 -0
- {tableio-1.0 → tableio-1.2}/src/tableio/tableio_types.py +0 -0
- {tableio-1.0 → tableio-1.2}/tableio.egg-info/dependency_links.txt +0 -0
- {tableio-1.0 → tableio-1.2}/tableio.egg-info/top_level.txt +0 -0
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tableio
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
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>
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Source code, https://
|
|
8
|
+
Project-URL: Source code, https://github.com/tom-bjorkholm/table-io
|
|
9
|
+
Project-URL: Documentation, https://github.com/tom-bjorkholm/table-io/blob/master/doc/
|
|
9
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
14
|
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Typing :: Typed
|
|
11
17
|
Requires-Python: >=3.12
|
|
12
18
|
Description-Content-Type: text/markdown
|
|
13
19
|
License-File: LICENSE.txt
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist: setuptools>=82.0.1
|
|
16
|
-
Requires-Dist: build>=1.5.0
|
|
17
|
-
Requires-Dist: wheel>=0.47.0
|
|
18
|
-
Requires-Dist: mformat-ext>=0.6
|
|
20
|
+
Requires-Dist: mformat-ext>=0.7
|
|
19
21
|
Requires-Dist: openpyxl>=3.1.5
|
|
20
|
-
Requires-Dist: types-openpyxl>=3.1.5.
|
|
21
|
-
Requires-Dist: odfdo>=3.
|
|
22
|
+
Requires-Dist: types-openpyxl>=3.1.5.20260827
|
|
23
|
+
Requires-Dist: odfdo>=3.26.3
|
|
22
24
|
Requires-Dist: xlsxwriter>=3.2.9
|
|
23
25
|
Requires-Dist: pylightxl>=1.61
|
|
24
26
|
Dynamic: author
|
|
@@ -109,6 +111,7 @@ The main features are:
|
|
|
109
111
|
- int
|
|
110
112
|
- float
|
|
111
113
|
- datetime
|
|
114
|
+
- timedelta (see below)
|
|
112
115
|
- writing a table as a filtered data range
|
|
113
116
|
- writing a table to a specific location in a spreadsheet (specified by a box)
|
|
114
117
|
- reading table data from a specific location in a spreadsheet (specified by a box)
|
|
@@ -117,17 +120,54 @@ The main features are:
|
|
|
117
120
|
at that position or at positions relative to that position.
|
|
118
121
|
- several border styles for tables
|
|
119
122
|
|
|
123
|
+
## Timedelta (duration) values
|
|
124
|
+
|
|
125
|
+
Excel (OpenPyXL, XlsxWriter, pylightxl) and ODS (odfdo) store `timedelta`
|
|
126
|
+
values natively, as duration cells, and read them back as `timedelta`.
|
|
127
|
+
(OpenPyXL reads durations with millisecond precision.)
|
|
128
|
+
The cells are displayed as `[hh]:mm:ss` (hours do not wrap at 24, seconds
|
|
129
|
+
are displayed rounded to whole seconds, the stored value is exact).
|
|
130
|
+
Microsoft Excel has no duration type, it stores a duration as a number of
|
|
131
|
+
days with a time number format. So by design Excel shows the cell as for
|
|
132
|
+
instance `72:00:00`, but shows the value as a date and time (like
|
|
133
|
+
`1/3/1900 12:00:00 AM`) in the formula bar when the cell is selected. Excel
|
|
134
|
+
does the same for durations typed in by hand, and LibreOffice shows
|
|
135
|
+
`72:00:00` in both places for the same file.
|
|
136
|
+
Notice also that Microsoft Excel cannot display negative durations (it
|
|
137
|
+
shows `####`). The stored value is still correct, it is read back
|
|
138
|
+
correctly and LibreOffice displays it as for instance `-00:03:58`. If
|
|
139
|
+
negative durations must be readable in Microsoft Excel, consider storing
|
|
140
|
+
the magnitude and the sign in separate columns.
|
|
141
|
+
|
|
142
|
+
Formats without native duration support (CSV and all write-only document
|
|
143
|
+
formats) write a fallback representation, selected with the optional
|
|
144
|
+
argument `timedelta_fallback` (a `TimeDeltaFallback` value):
|
|
145
|
+
|
|
146
|
+
| TimeDeltaFallback | Two days, three hours and 1.5 seconds |
|
|
147
|
+
|-----------------------|---------------------------------------|
|
|
148
|
+
| `HMS_STRING` (default)| `51:00:01.5` |
|
|
149
|
+
| `DHMS_STRING` | `2 d 03:00:01.5` |
|
|
150
|
+
| `DHMS_STRING_LONG` | `2 days 03:00:01.5` |
|
|
151
|
+
| `WDHMS_STRING` | `2 d 03:00:01.5` (with `W w` if a week or more) |
|
|
152
|
+
| `WDHMS_STRING_LONG` | `2 days 03:00:01.5` (with `W weeks` if a week or more) |
|
|
153
|
+
| `FLOATSECONDS` | `183601.5` |
|
|
154
|
+
|
|
155
|
+
Negative durations get a leading `-`. When reading such a file the values
|
|
156
|
+
are strings (CSV). Convert them with `parse_timedelta()`, that accepts all
|
|
157
|
+
fallback formats (and `timedelta` values, so it works for all formats).
|
|
158
|
+
`format_timedelta()` formats a `timedelta` in any of the fallback formats.
|
|
159
|
+
|
|
120
160
|
## Example programs
|
|
121
161
|
|
|
122
162
|
The best way to learn to use this package is to use the provided
|
|
123
163
|
example programs:
|
|
124
|
-
[https://
|
|
164
|
+
[https://github.com/tom-bjorkholm/table-io/blob/master/example/src/example/README.md](https://github.com/tom-bjorkholm/table-io/blob/master/example/src/example/README.md).
|
|
125
165
|
|
|
126
166
|
## API documentation
|
|
127
167
|
|
|
128
|
-
You can find the public API documentation at [https://
|
|
168
|
+
You can find the public API documentation at [https://github.com/tom-bjorkholm/table-io/blob/master/doc/api.md](https://github.com/tom-bjorkholm/table-io/blob/master/doc/api.md)
|
|
129
169
|
|
|
130
|
-
You can find the protected API documentation at [https://
|
|
170
|
+
You can find the protected API documentation at [https://github.com/tom-bjorkholm/table-io/blob/master/doc/protected_api.md](https://github.com/tom-bjorkholm/table-io/blob/master/doc/protected_api.md)
|
|
131
171
|
The protected API documentation is only for developers that want to
|
|
132
172
|
extend the framework by adding their own classes as registered
|
|
133
173
|
readers/writers to the factory.
|
|
@@ -137,9 +177,10 @@ a better start by reading the examples.
|
|
|
137
177
|
|
|
138
178
|
## Test summary
|
|
139
179
|
|
|
140
|
-
- Test result:
|
|
180
|
+
- Test result: 1567 passed in 9s
|
|
141
181
|
- No flake8 warnings.
|
|
142
182
|
- No mypy errors found.
|
|
183
|
+
- No pylint warnings.
|
|
143
184
|
- No python layout warnings.
|
|
144
|
-
- Built version(s): 1.
|
|
145
|
-
- Build and test using Python 3.14.
|
|
185
|
+
- Built version(s): 1.2
|
|
186
|
+
- Build and test using Python 3.14.7
|
|
@@ -29,7 +29,7 @@ formulas in the received spreadsheet).
|
|
|
29
29
|
The tableio repo uses submodules. To clone it use the command:
|
|
30
30
|
|
|
31
31
|
````sh
|
|
32
|
-
git clone --recurse-submodules git@
|
|
32
|
+
git clone --recurse-submodules git@github.com:tom-bjorkholm/table-io.git
|
|
33
33
|
````
|
|
34
34
|
|
|
35
35
|
If you forgot to include the `--recurse-submodules` in your `git clone` command
|
|
@@ -89,13 +89,14 @@ reports.
|
|
|
89
89
|
|
|
90
90
|
The file `./common_build_tools/README.md` (in git submodule - see above) contains more
|
|
91
91
|
information about the build system. This README can also be viewed at
|
|
92
|
-
[https://
|
|
92
|
+
[https://github.com/tom-bjorkholm/common_build_tools/blob/master/README.md](https://github.com/tom-bjorkholm/common_build_tools/blob/master/README.md)
|
|
93
93
|
|
|
94
94
|
## Test summary
|
|
95
95
|
|
|
96
|
-
- Test result:
|
|
96
|
+
- Test result: 1567 passed in 9s
|
|
97
97
|
- No flake8 warnings.
|
|
98
98
|
- No mypy errors found.
|
|
99
|
+
- No pylint warnings.
|
|
99
100
|
- No python layout warnings.
|
|
100
|
-
- Built version(s): 1.
|
|
101
|
-
- Build and test using Python 3.14.
|
|
101
|
+
- Built version(s): 1.2
|
|
102
|
+
- Build and test using Python 3.14.7
|
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: tableio
|
|
3
|
-
Version: 1.0
|
|
4
|
-
Summary: Uniform way to write table data to and read from different file formats
|
|
5
|
-
Author: Tom Björkholm
|
|
6
|
-
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
Project-URL: Source code, https://bitbucket.org/tom-bjorkholm/table-io
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.12
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE.txt
|
|
14
|
-
Requires-Dist: pip>=26.1.1
|
|
15
|
-
Requires-Dist: setuptools>=82.0.1
|
|
16
|
-
Requires-Dist: build>=1.5.0
|
|
17
|
-
Requires-Dist: wheel>=0.47.0
|
|
18
|
-
Requires-Dist: mformat-ext>=0.6
|
|
19
|
-
Requires-Dist: openpyxl>=3.1.5
|
|
20
|
-
Requires-Dist: types-openpyxl>=3.1.5.20260518
|
|
21
|
-
Requires-Dist: odfdo>=3.22.8
|
|
22
|
-
Requires-Dist: xlsxwriter>=3.2.9
|
|
23
|
-
Requires-Dist: pylightxl>=1.61
|
|
24
|
-
Dynamic: author
|
|
25
|
-
Dynamic: license-file
|
|
26
|
-
Dynamic: requires-dist
|
|
27
|
-
Dynamic: requires-python
|
|
28
|
-
|
|
29
1
|
# tableio
|
|
30
2
|
|
|
31
3
|
The tableio package contains a number of classes providing a uniform way for
|
|
@@ -109,6 +81,7 @@ The main features are:
|
|
|
109
81
|
- int
|
|
110
82
|
- float
|
|
111
83
|
- datetime
|
|
84
|
+
- timedelta (see below)
|
|
112
85
|
- writing a table as a filtered data range
|
|
113
86
|
- writing a table to a specific location in a spreadsheet (specified by a box)
|
|
114
87
|
- reading table data from a specific location in a spreadsheet (specified by a box)
|
|
@@ -117,17 +90,54 @@ The main features are:
|
|
|
117
90
|
at that position or at positions relative to that position.
|
|
118
91
|
- several border styles for tables
|
|
119
92
|
|
|
93
|
+
## Timedelta (duration) values
|
|
94
|
+
|
|
95
|
+
Excel (OpenPyXL, XlsxWriter, pylightxl) and ODS (odfdo) store `timedelta`
|
|
96
|
+
values natively, as duration cells, and read them back as `timedelta`.
|
|
97
|
+
(OpenPyXL reads durations with millisecond precision.)
|
|
98
|
+
The cells are displayed as `[hh]:mm:ss` (hours do not wrap at 24, seconds
|
|
99
|
+
are displayed rounded to whole seconds, the stored value is exact).
|
|
100
|
+
Microsoft Excel has no duration type, it stores a duration as a number of
|
|
101
|
+
days with a time number format. So by design Excel shows the cell as for
|
|
102
|
+
instance `72:00:00`, but shows the value as a date and time (like
|
|
103
|
+
`1/3/1900 12:00:00 AM`) in the formula bar when the cell is selected. Excel
|
|
104
|
+
does the same for durations typed in by hand, and LibreOffice shows
|
|
105
|
+
`72:00:00` in both places for the same file.
|
|
106
|
+
Notice also that Microsoft Excel cannot display negative durations (it
|
|
107
|
+
shows `####`). The stored value is still correct, it is read back
|
|
108
|
+
correctly and LibreOffice displays it as for instance `-00:03:58`. If
|
|
109
|
+
negative durations must be readable in Microsoft Excel, consider storing
|
|
110
|
+
the magnitude and the sign in separate columns.
|
|
111
|
+
|
|
112
|
+
Formats without native duration support (CSV and all write-only document
|
|
113
|
+
formats) write a fallback representation, selected with the optional
|
|
114
|
+
argument `timedelta_fallback` (a `TimeDeltaFallback` value):
|
|
115
|
+
|
|
116
|
+
| TimeDeltaFallback | Two days, three hours and 1.5 seconds |
|
|
117
|
+
|-----------------------|---------------------------------------|
|
|
118
|
+
| `HMS_STRING` (default)| `51:00:01.5` |
|
|
119
|
+
| `DHMS_STRING` | `2 d 03:00:01.5` |
|
|
120
|
+
| `DHMS_STRING_LONG` | `2 days 03:00:01.5` |
|
|
121
|
+
| `WDHMS_STRING` | `2 d 03:00:01.5` (with `W w` if a week or more) |
|
|
122
|
+
| `WDHMS_STRING_LONG` | `2 days 03:00:01.5` (with `W weeks` if a week or more) |
|
|
123
|
+
| `FLOATSECONDS` | `183601.5` |
|
|
124
|
+
|
|
125
|
+
Negative durations get a leading `-`. When reading such a file the values
|
|
126
|
+
are strings (CSV). Convert them with `parse_timedelta()`, that accepts all
|
|
127
|
+
fallback formats (and `timedelta` values, so it works for all formats).
|
|
128
|
+
`format_timedelta()` formats a `timedelta` in any of the fallback formats.
|
|
129
|
+
|
|
120
130
|
## Example programs
|
|
121
131
|
|
|
122
132
|
The best way to learn to use this package is to use the provided
|
|
123
133
|
example programs:
|
|
124
|
-
[https://
|
|
134
|
+
[https://github.com/tom-bjorkholm/table-io/blob/master/example/src/example/README.md](https://github.com/tom-bjorkholm/table-io/blob/master/example/src/example/README.md).
|
|
125
135
|
|
|
126
136
|
## API documentation
|
|
127
137
|
|
|
128
|
-
You can find the public API documentation at [https://
|
|
138
|
+
You can find the public API documentation at [https://github.com/tom-bjorkholm/table-io/blob/master/doc/api.md](https://github.com/tom-bjorkholm/table-io/blob/master/doc/api.md)
|
|
129
139
|
|
|
130
|
-
You can find the protected API documentation at [https://
|
|
140
|
+
You can find the protected API documentation at [https://github.com/tom-bjorkholm/table-io/blob/master/doc/protected_api.md](https://github.com/tom-bjorkholm/table-io/blob/master/doc/protected_api.md)
|
|
131
141
|
The protected API documentation is only for developers that want to
|
|
132
142
|
extend the framework by adding their own classes as registered
|
|
133
143
|
readers/writers to the factory.
|
|
@@ -137,9 +147,10 @@ a better start by reading the examples.
|
|
|
137
147
|
|
|
138
148
|
## Test summary
|
|
139
149
|
|
|
140
|
-
- Test result:
|
|
150
|
+
- Test result: 1567 passed in 9s
|
|
141
151
|
- No flake8 warnings.
|
|
142
152
|
- No mypy errors found.
|
|
153
|
+
- No pylint warnings.
|
|
143
154
|
- No python layout warnings.
|
|
144
|
-
- Built version(s): 1.
|
|
145
|
-
- Build and test using Python 3.14.
|
|
155
|
+
- Built version(s): 1.2
|
|
156
|
+
- Build and test using Python 3.14.7
|
|
@@ -4,7 +4,6 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tableio"
|
|
7
|
-
version = "1.0"
|
|
8
7
|
authors = [
|
|
9
8
|
{ name="Tom Björkholm", email="klausuler_linnet0q@icloud.com" },
|
|
10
9
|
]
|
|
@@ -15,9 +14,15 @@ license = "MIT"
|
|
|
15
14
|
license-files = ["LICENSE.txt"]
|
|
16
15
|
classifiers = [
|
|
17
16
|
"Programming Language :: Python :: 3",
|
|
18
|
-
"
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Programming Language :: Python :: 3.14",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Typing :: Typed"
|
|
19
23
|
]
|
|
20
|
-
dynamic = ["dependencies"]
|
|
24
|
+
dynamic = ["dependencies", "version"]
|
|
21
25
|
|
|
22
26
|
[project.urls]
|
|
23
|
-
"Source code" = "https://
|
|
27
|
+
"Source code" = "https://github.com/tom-bjorkholm/table-io"
|
|
28
|
+
"Documentation" = "https://github.com/tom-bjorkholm/table-io/blob/master/doc/"
|
|
@@ -5,7 +5,7 @@ from setuptools import setup
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='tableio',
|
|
8
|
-
version='1.
|
|
8
|
+
version='1.2',
|
|
9
9
|
description='Uniform way to write table data to and read from different ' \
|
|
10
10
|
'file formats',
|
|
11
11
|
author='Tom Björkholm',
|
|
@@ -14,15 +14,11 @@ setup(
|
|
|
14
14
|
packages=['tableio'],
|
|
15
15
|
package_dir={'tableio': 'src/tableio'},
|
|
16
16
|
package_data={'tableio': ['src/py.typed']},
|
|
17
|
-
install_requires=[
|
|
18
|
-
'
|
|
19
|
-
'setuptools >= 82.0.1',
|
|
20
|
-
'build >= 1.5.0',
|
|
21
|
-
'wheel >= 0.47.0',
|
|
22
|
-
'mformat-ext >= 0.6',
|
|
17
|
+
install_requires=[
|
|
18
|
+
'mformat-ext >= 0.7',
|
|
23
19
|
'openpyxl >= 3.1.5',
|
|
24
|
-
'types-openpyxl >= 3.1.5.
|
|
25
|
-
'odfdo >= 3.
|
|
20
|
+
'types-openpyxl >= 3.1.5.20260827',
|
|
21
|
+
'odfdo >= 3.26.3',
|
|
26
22
|
'xlsxwriter >= 3.2.9',
|
|
27
23
|
'pylightxl >= 1.61'
|
|
28
24
|
]
|
|
@@ -21,9 +21,11 @@ from tableio.config_data_error import ConfigError, ConfigIssue
|
|
|
21
21
|
from tableio.config_data_validate import tio_config_validate
|
|
22
22
|
from tableio.factory import create_tableio, filter_args_tableio, \
|
|
23
23
|
list_implementations_tableio, list_registered_tableio
|
|
24
|
-
from tableio.optional_args import CsvDialect, OptionalArgs,
|
|
24
|
+
from tableio.optional_args import CsvDialect, OptionalArgs, \
|
|
25
|
+
OptionalArgsDict, TimeDeltaFallback
|
|
25
26
|
from tableio.tableio import TableIO
|
|
26
27
|
from tableio.tableio_types import TableBorderStyle, Box, FileAccess, Position
|
|
28
|
+
from tableio.timedelta_helpers import format_timedelta, parse_timedelta
|
|
27
29
|
from tableio.value_type import CellT, DictData, DictDataMap, Fmt, \
|
|
28
30
|
FmtDictData, FmtDictRow, FmtListData, FmtListRow, ListData, \
|
|
29
31
|
ListDataSeq, ReadResult, Value, ValueFmt
|
|
@@ -43,6 +45,8 @@ __all__ = ['create_tableio',
|
|
|
43
45
|
'DictDataMap',
|
|
44
46
|
'ReadResult',
|
|
45
47
|
'Fmt',
|
|
48
|
+
'format_timedelta',
|
|
49
|
+
'parse_timedelta',
|
|
46
50
|
'ValueFmt',
|
|
47
51
|
'FmtListRow',
|
|
48
52
|
'FmtDictRow',
|
|
@@ -63,6 +67,7 @@ __all__ = ['create_tableio',
|
|
|
63
67
|
'list_registered_tableio',
|
|
64
68
|
'list_implementations_tableio',
|
|
65
69
|
'CsvDialect',
|
|
70
|
+
'TimeDeltaFallback',
|
|
66
71
|
'ConfigData', 'ConfigError', 'ConfigIssue', 'ConfigSpec',
|
|
67
72
|
'CsvConfigData', 'HtmlConfigData', 'LatexConfigData',
|
|
68
73
|
'tio_config_create', 'tio_config_default',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from typing import Optional
|
|
9
|
-
from tableio.optional_args import CsvDialect
|
|
9
|
+
from tableio.optional_args import CsvDialect, TimeDeltaFallback
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@dataclass
|
|
@@ -115,3 +115,10 @@ class ConfigData: # pylint: disable=too-many-instance-attributes
|
|
|
115
115
|
|
|
116
116
|
latex: Optional[LatexConfigData] = None
|
|
117
117
|
"""LaTeX-specific configuration values, or ``None`` when unset."""
|
|
118
|
+
|
|
119
|
+
timedelta_fallback: Optional[TimeDeltaFallback] = None
|
|
120
|
+
"""How formats without native timedelta write it, or ``None``.
|
|
121
|
+
|
|
122
|
+
``None`` means backend default (``TimeDeltaFallback.HMS_STRING``).
|
|
123
|
+
Formats with native timedelta support ignore this value.
|
|
124
|
+
"""
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
from typing import Callable, NoReturn, Optional, TypeVar, cast
|
|
8
8
|
|
|
9
9
|
from mformat.mformat import PathLike
|
|
10
|
+
from mformat.paper_size import PaperSize
|
|
11
|
+
from mformat.plain_text_table import TableAlignment
|
|
10
12
|
from tableio.access_capability import add_access_capabilities
|
|
11
13
|
from tableio.capability import Capabilities, capability_match
|
|
12
14
|
from tableio.config_data import ConfigData, CsvConfigData, HtmlConfigData, \
|
|
@@ -15,7 +17,8 @@ from tableio.config_data_error import ConfigError, ConfigIssue
|
|
|
15
17
|
from tableio.config_data_validate import tio_config_validate
|
|
16
18
|
from tableio.factory import create_tableio, filter_args_tableio, \
|
|
17
19
|
list_implementations_tableio, list_registered_tableio, usage_tableio
|
|
18
|
-
from tableio.optional_args import CsvDialect, OptionalArgs,
|
|
20
|
+
from tableio.optional_args import CsvDialect, OptionalArgs, \
|
|
21
|
+
OptionalArgsDict, TimeDeltaFallback
|
|
19
22
|
from tableio.tableio import TableIO
|
|
20
23
|
from tableio.tableio_types import FileAccess
|
|
21
24
|
|
|
@@ -111,6 +114,16 @@ def _best_default_names(capabilities: Capabilities, format_name: Optional[str],
|
|
|
111
114
|
_config_error(name, 'does not match registered TableIO backends.')
|
|
112
115
|
|
|
113
116
|
|
|
117
|
+
def _paper_size(value: Optional[str]) -> Optional[PaperSize]:
|
|
118
|
+
"""Return a validated paper size text as the backend enum value."""
|
|
119
|
+
return None if value is None else PaperSize.from_str(value)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _table_alignment(value: Optional[str]) -> Optional[TableAlignment]:
|
|
123
|
+
"""Return a validated table alignment text as the backend enum value."""
|
|
124
|
+
return None if value is None else TableAlignment[value.upper()]
|
|
125
|
+
|
|
126
|
+
|
|
114
127
|
def _base_arg_items(config: ConfigData) -> list[tuple[str, str, object]]:
|
|
115
128
|
"""Return configured top-level optional argument values."""
|
|
116
129
|
return [
|
|
@@ -118,11 +131,14 @@ def _base_arg_items(config: ConfigData) -> list[tuple[str, str, object]]:
|
|
|
118
131
|
config.character_encoding),
|
|
119
132
|
('language', 'lang', config.language),
|
|
120
133
|
('title', 'title', config.title),
|
|
121
|
-
('paper_size', 'paper_size', config.paper_size),
|
|
134
|
+
('paper_size', 'paper_size', _paper_size(config.paper_size)),
|
|
122
135
|
('line_length', 'line_length', config.line_length),
|
|
123
136
|
('table_max_line_length', 'table_max_line_length',
|
|
124
137
|
config.table_max_line_length),
|
|
125
|
-
('table_alignment', 'table_alignment',
|
|
138
|
+
('table_alignment', 'table_alignment',
|
|
139
|
+
_table_alignment(config.table_alignment)),
|
|
140
|
+
('timedelta_fallback', 'timedelta_fallback',
|
|
141
|
+
config.timedelta_fallback)
|
|
126
142
|
]
|
|
127
143
|
|
|
128
144
|
|
|
@@ -201,7 +217,8 @@ def _all_option_config(format_name: str,
|
|
|
201
217
|
title='HTML file', paper_size='A4', line_length=79,
|
|
202
218
|
table_max_line_length=140,
|
|
203
219
|
table_alignment='CENTER_BUT_DIGITS_RIGHT', csv=csv,
|
|
204
|
-
html=html, latex=latex
|
|
220
|
+
html=html, latex=latex,
|
|
221
|
+
timedelta_fallback=TimeDeltaFallback.HMS_STRING)
|
|
205
222
|
|
|
206
223
|
|
|
207
224
|
def _include_default_impl(implementation: Optional[str],
|
|
@@ -306,6 +323,8 @@ def tio_config_create(
|
|
|
306
323
|
TableIO:
|
|
307
324
|
"""Create a TableIO object from configuration and runtime values.
|
|
308
325
|
|
|
326
|
+
The implementation is selected using the capabilities together with the
|
|
327
|
+
capabilities implied by file_access, as done by the validation.
|
|
309
328
|
Args:
|
|
310
329
|
config: Durable configuration data.
|
|
311
330
|
file_name: Runtime file name to open.
|
|
@@ -317,16 +336,18 @@ def tio_config_create(
|
|
|
317
336
|
"""
|
|
318
337
|
tio_config_validate(config, capabilities=capabilities,
|
|
319
338
|
file_access=file_access)
|
|
339
|
+
match_caps = add_access_capabilities(file_access,
|
|
340
|
+
capabilities or Capabilities())
|
|
320
341
|
extra_args = None
|
|
321
342
|
if file_exists_callback is not None:
|
|
322
343
|
extra_args = cast(OptionalArgsDict, {
|
|
323
344
|
'file_exists_callback': file_exists_callback
|
|
324
345
|
})
|
|
325
|
-
args = _filtered_args(config,
|
|
346
|
+
args = _filtered_args(config, match_caps, extra_args)
|
|
326
347
|
return create_tableio(format_name=config.format_name, file_name=file_name,
|
|
327
348
|
file_access=file_access, args=args,
|
|
328
349
|
implementation=config.implementation,
|
|
329
|
-
capabilities=
|
|
350
|
+
capabilities=match_caps)
|
|
330
351
|
|
|
331
352
|
|
|
332
353
|
def tio_config_ignored_names(config: ConfigData,
|
|
@@ -386,6 +407,9 @@ def tio_config_trim(config: ConfigData,
|
|
|
386
407
|
filtered_names),
|
|
387
408
|
table_alignment=_kept(config.table_alignment,
|
|
388
409
|
'table_alignment', filtered_names),
|
|
410
|
+
timedelta_fallback=_kept(config.timedelta_fallback,
|
|
411
|
+
'timedelta_fallback',
|
|
412
|
+
filtered_names),
|
|
389
413
|
csv=csv, html=html, latex=latex)
|
|
390
414
|
|
|
391
415
|
|
|
@@ -13,7 +13,7 @@ from mformat.plain_text_table import TableAlignment
|
|
|
13
13
|
|
|
14
14
|
from tableio.factory import list_implementations_tableio, \
|
|
15
15
|
list_registered_tableio, usage_tableio
|
|
16
|
-
from tableio.optional_args import CsvDialect
|
|
16
|
+
from tableio.optional_args import CsvDialect, TimeDeltaFallback
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@dataclass
|
|
@@ -58,6 +58,11 @@ def _csv_dialect_choices() -> tuple[str, ...]:
|
|
|
58
58
|
return tuple(member.name for member in CsvDialect)
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def _timedelta_fallback_choices() -> tuple[str, ...]:
|
|
62
|
+
"""Return advertised timedelta fallback choices."""
|
|
63
|
+
return tuple(member.name for member in TimeDeltaFallback)
|
|
64
|
+
|
|
65
|
+
|
|
61
66
|
def _table_alignment_choices() -> tuple[str, ...]:
|
|
62
67
|
"""Return advertised plain text table alignment choices."""
|
|
63
68
|
return tuple(member.name for member in TableAlignment)
|
|
@@ -147,6 +152,13 @@ def tio_config_specs() -> dict[str, ConfigSpec]:
|
|
|
147
152
|
default_text='None means backend default.',
|
|
148
153
|
choices=_table_alignment_choices(),
|
|
149
154
|
optional_arg='table_alignment')),
|
|
155
|
+
_arg_spec(ConfigSpec(name='timedelta_fallback',
|
|
156
|
+
description='How timedelta values are written '
|
|
157
|
+
'by formats without native timedelta support.',
|
|
158
|
+
value_type='Optional[TimeDeltaFallback]',
|
|
159
|
+
default_text='None means backend default.',
|
|
160
|
+
choices=_timedelta_fallback_choices(),
|
|
161
|
+
optional_arg='timedelta_fallback')),
|
|
150
162
|
_arg_spec(ConfigSpec(name='csv.dialect',
|
|
151
163
|
description='The CSV dialect template.',
|
|
152
164
|
value_type='Optional[CsvDialect]',
|
|
@@ -17,7 +17,7 @@ from tableio.config_data_error import ConfigError, ConfigIssue
|
|
|
17
17
|
from tableio.factory import TableIOFactoryNoCapabilityMatch, \
|
|
18
18
|
TableIOFactoryNoSuchError, filter_args_tableio, \
|
|
19
19
|
list_implementations_tableio
|
|
20
|
-
from tableio.optional_args import CsvDialect
|
|
20
|
+
from tableio.optional_args import CsvDialect, TimeDeltaFallback
|
|
21
21
|
from tableio.tableio_types import FileAccess
|
|
22
22
|
|
|
23
23
|
|
|
@@ -156,6 +156,10 @@ def _validate_top_values(config: ConfigData, specs: dict[str, ConfigSpec],
|
|
|
156
156
|
_validate_int_min(issues, 'table_max_line_length',
|
|
157
157
|
config.table_max_line_length, 10, True)
|
|
158
158
|
_valid_choice(config.table_alignment, specs['table_alignment'], issues)
|
|
159
|
+
if config.timedelta_fallback is not None and \
|
|
160
|
+
not isinstance(config.timedelta_fallback, TimeDeltaFallback):
|
|
161
|
+
_add_issue(issues, 'timedelta_fallback',
|
|
162
|
+
'must be a TimeDeltaFallback value or None.')
|
|
159
163
|
|
|
160
164
|
|
|
161
165
|
def _validate_csv(config: ConfigData, specs: dict[str, ConfigSpec],
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
from typing import Optional, NamedTuple
|
|
9
|
-
from functools import total_ordering
|
|
10
9
|
import warnings
|
|
11
10
|
from mformat.mformat import PathLike
|
|
12
11
|
from tableio.access_capability import check_access_capabilities
|
|
@@ -53,7 +52,6 @@ class TableIOFactoryNoCapabilityMatch(ValueError):
|
|
|
53
52
|
"""
|
|
54
53
|
|
|
55
54
|
|
|
56
|
-
@total_ordering
|
|
57
55
|
class ImplPrio(NamedTuple):
|
|
58
56
|
"""Priority of an implementation."""
|
|
59
57
|
|
|
@@ -66,25 +64,43 @@ class ImplPrio(NamedTuple):
|
|
|
66
64
|
priority: int
|
|
67
65
|
"""The priority of the implementation."""
|
|
68
66
|
|
|
67
|
+
def sort_key(self) -> tuple[int, str, str]:
|
|
68
|
+
"""Return the ordering key: priority, format name, implementation.
|
|
69
|
+
|
|
70
|
+
All comparison operators are defined from this key, as the
|
|
71
|
+
inherited tuple comparisons use the field order instead.
|
|
72
|
+
"""
|
|
73
|
+
return (self.priority, self.format_name, self.implementation)
|
|
74
|
+
|
|
69
75
|
def __lt__(self, other: object) -> bool:
|
|
70
76
|
"""Compare two implementation priorities."""
|
|
71
77
|
if not isinstance(other, ImplPrio):
|
|
72
78
|
return NotImplemented
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
return self.sort_key() < other.sort_key()
|
|
80
|
+
|
|
81
|
+
def __le__(self, other: object) -> bool:
|
|
82
|
+
"""Compare two implementation priorities."""
|
|
83
|
+
if not isinstance(other, ImplPrio):
|
|
84
|
+
return NotImplemented
|
|
85
|
+
return self.sort_key() <= other.sort_key()
|
|
86
|
+
|
|
87
|
+
def __gt__(self, other: object) -> bool:
|
|
88
|
+
"""Compare two implementation priorities."""
|
|
89
|
+
if not isinstance(other, ImplPrio):
|
|
90
|
+
return NotImplemented
|
|
91
|
+
return self.sort_key() > other.sort_key()
|
|
92
|
+
|
|
93
|
+
def __ge__(self, other: object) -> bool:
|
|
94
|
+
"""Compare two implementation priorities."""
|
|
95
|
+
if not isinstance(other, ImplPrio):
|
|
96
|
+
return NotImplemented
|
|
97
|
+
return self.sort_key() >= other.sort_key()
|
|
82
98
|
|
|
83
99
|
def __eq__(self, other: object) -> bool:
|
|
84
100
|
"""Compare two implementation priorities."""
|
|
85
101
|
if not isinstance(other, ImplPrio):
|
|
86
102
|
return NotImplemented
|
|
87
|
-
return
|
|
103
|
+
return self.sort_key() == other.sort_key()
|
|
88
104
|
|
|
89
105
|
|
|
90
106
|
class BestMatch(NamedTuple):
|