ExcelAlchemy 2.0.0rc1__tar.gz → 2.2.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.
- excelalchemy-2.2.2/PKG-INFO +131 -0
- excelalchemy-2.2.2/README-pypi.md +91 -0
- excelalchemy-2.2.2/pyproject.toml +155 -0
- excelalchemy-2.2.2/src/excelalchemy/__init__.py +126 -0
- excelalchemy-2.2.2/src/excelalchemy/_primitives/__init__.py +1 -0
- excelalchemy-2.0.0rc1/src/excelalchemy/const.py → excelalchemy-2.2.2/src/excelalchemy/_primitives/constants.py +11 -11
- excelalchemy-2.2.2/src/excelalchemy/_primitives/deprecation.py +22 -0
- excelalchemy-2.2.2/src/excelalchemy/_primitives/header_models.py +27 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types → excelalchemy-2.2.2/src/excelalchemy/_primitives}/identity.py +15 -11
- excelalchemy-2.2.2/src/excelalchemy/_primitives/payloads.py +17 -0
- excelalchemy-2.2.2/src/excelalchemy/artifacts.py +38 -0
- excelalchemy-2.2.2/src/excelalchemy/codecs/__init__.py +14 -0
- excelalchemy-2.2.2/src/excelalchemy/codecs/base.py +126 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/boolean.py +13 -10
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/date.py +26 -13
- excelalchemy-2.2.2/src/excelalchemy/codecs/date_range.py +189 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/email.py +8 -5
- excelalchemy-2.2.2/src/excelalchemy/codecs/money.py +29 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/multi_checkbox.py +31 -22
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/number.py +36 -26
- excelalchemy-2.2.2/src/excelalchemy/codecs/number_range.py +141 -0
- excelalchemy-2.2.2/src/excelalchemy/codecs/organization.py +83 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/phone_number.py +6 -3
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/radio.py +17 -12
- excelalchemy-2.2.2/src/excelalchemy/codecs/staff.py +88 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/string.py +13 -15
- excelalchemy-2.2.2/src/excelalchemy/codecs/tree.py +64 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types/value → excelalchemy-2.2.2/src/excelalchemy/codecs}/url.py +6 -3
- excelalchemy-2.2.2/src/excelalchemy/config.py +409 -0
- excelalchemy-2.2.2/src/excelalchemy/const.py +3 -0
- excelalchemy-2.2.2/src/excelalchemy/core/abstract.py +57 -0
- excelalchemy-2.2.2/src/excelalchemy/core/alchemy.py +372 -0
- excelalchemy-2.2.2/src/excelalchemy/core/executor.py +124 -0
- excelalchemy-2.2.2/src/excelalchemy/core/headers.py +149 -0
- excelalchemy-2.2.2/src/excelalchemy/core/import_session.py +307 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/rendering.py +14 -12
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/rows.py +48 -38
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/schema.py +15 -12
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/storage.py +16 -7
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/storage_minio.py +22 -16
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/storage_protocol.py +1 -1
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/table.py +64 -29
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/writer.py +67 -60
- excelalchemy-2.2.2/src/excelalchemy/exc.py +7 -0
- excelalchemy-2.0.0rc1/src/excelalchemy/exc.py → excelalchemy-2.2.2/src/excelalchemy/exceptions.py +9 -7
- excelalchemy-2.2.2/src/excelalchemy/header_models.py +10 -0
- excelalchemy-2.2.2/src/excelalchemy/helper/pydantic.py +244 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/i18n/messages.py +12 -20
- excelalchemy-2.2.2/src/excelalchemy/identity.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/metadata.py +899 -0
- excelalchemy-2.2.2/src/excelalchemy/results.py +91 -0
- excelalchemy-2.2.2/src/excelalchemy/types/__init__.py +15 -0
- excelalchemy-2.2.2/src/excelalchemy/types/abstract.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/alchemy.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/field.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/header.py +10 -0
- excelalchemy-2.2.2/src/excelalchemy/types/identity.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/result.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/__init__.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/boolean.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/date.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/date_range.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/email.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/money.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/multi_checkbox.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/number.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/number_range.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/organization.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/phone_number.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/radio.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/staff.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/string.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/tree.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/types/value/url.py +7 -0
- excelalchemy-2.2.2/src/excelalchemy/util/converter.py +50 -0
- excelalchemy-2.2.2/src/excelalchemy/util/convertor.py +8 -0
- excelalchemy-2.2.2/src/excelalchemy/util/file.py +52 -0
- excelalchemy-2.0.0rc1/PKG-INFO +0 -325
- excelalchemy-2.0.0rc1/README.md +0 -285
- excelalchemy-2.0.0rc1/pyproject.toml +0 -118
- excelalchemy-2.0.0rc1/src/excelalchemy/__init__.py +0 -75
- excelalchemy-2.0.0rc1/src/excelalchemy/core/abstract.py +0 -36
- excelalchemy-2.0.0rc1/src/excelalchemy/core/alchemy.py +0 -403
- excelalchemy-2.0.0rc1/src/excelalchemy/core/executor.py +0 -114
- excelalchemy-2.0.0rc1/src/excelalchemy/core/headers.py +0 -111
- excelalchemy-2.0.0rc1/src/excelalchemy/helper/pydantic.py +0 -191
- excelalchemy-2.0.0rc1/src/excelalchemy/types/abstract.py +0 -118
- excelalchemy-2.0.0rc1/src/excelalchemy/types/alchemy.py +0 -126
- excelalchemy-2.0.0rc1/src/excelalchemy/types/field.py +0 -434
- excelalchemy-2.0.0rc1/src/excelalchemy/types/header.py +0 -25
- excelalchemy-2.0.0rc1/src/excelalchemy/types/result.py +0 -77
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/__init__.py +0 -10
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/date_range.py +0 -165
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/money.py +0 -11
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/number_range.py +0 -99
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/organization.py +0 -70
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/staff.py +0 -73
- excelalchemy-2.0.0rc1/src/excelalchemy/types/value/tree.py +0 -54
- excelalchemy-2.0.0rc1/src/excelalchemy/util/__init__.py +0 -0
- excelalchemy-2.0.0rc1/src/excelalchemy/util/convertor.py +0 -47
- excelalchemy-2.0.0rc1/src/excelalchemy/util/file.py +0 -47
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/LICENSE +0 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/core/__init__.py +0 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/helper/__init__.py +0 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/i18n/__init__.py +0 -0
- {excelalchemy-2.0.0rc1 → excelalchemy-2.2.2}/src/excelalchemy/py.typed +0 -0
- {excelalchemy-2.0.0rc1/src/excelalchemy/types → excelalchemy-2.2.2/src/excelalchemy/util}/__init__.py +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ExcelAlchemy
|
|
3
|
+
Version: 2.2.2
|
|
4
|
+
Summary: Schema-driven Python library for typed Excel import/export workflows with Pydantic and locale-aware workbooks.
|
|
5
|
+
Keywords: excel,openpyxl,pydantic,minio,schema
|
|
6
|
+
Author: Ray
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pydantic[email] >=2.12, <3
|
|
23
|
+
Requires-Dist: openpyxl >=3.1.5, <4
|
|
24
|
+
Requires-Dist: pendulum >=3.2.0, <4
|
|
25
|
+
Requires-Dist: minio >=7.2.20, <8 ; extra == "development"
|
|
26
|
+
Requires-Dist: pre-commit ; extra == "development"
|
|
27
|
+
Requires-Dist: pyright==1.1.408 ; extra == "development"
|
|
28
|
+
Requires-Dist: pytest ; extra == "development"
|
|
29
|
+
Requires-Dist: coverage ; extra == "development"
|
|
30
|
+
Requires-Dist: pytest-cov ; extra == "development"
|
|
31
|
+
Requires-Dist: ruff ; extra == "development"
|
|
32
|
+
Requires-Dist: minio >=7.2.20, <8 ; extra == "minio"
|
|
33
|
+
Project-URL: Documentation, https://github.com/RayCarterLab/ExcelAlchemy#readme
|
|
34
|
+
Project-URL: Home, https://github.com/RayCarterLab/ExcelAlchemy
|
|
35
|
+
Project-URL: Issues, https://github.com/RayCarterLab/ExcelAlchemy/issues
|
|
36
|
+
Project-URL: Repository, https://github.com/RayCarterLab/ExcelAlchemy
|
|
37
|
+
Provides-Extra: development
|
|
38
|
+
Provides-Extra: minio
|
|
39
|
+
|
|
40
|
+
# ExcelAlchemy
|
|
41
|
+
|
|
42
|
+
Schema-driven Python library for typed Excel import/export workflows with Pydantic and locale-aware workbooks.
|
|
43
|
+
|
|
44
|
+
ExcelAlchemy turns Pydantic models into typed workbook contracts:
|
|
45
|
+
|
|
46
|
+
- generate Excel templates from code
|
|
47
|
+
- validate uploaded workbooks
|
|
48
|
+
- map failures back to rows and cells
|
|
49
|
+
- render workbook-facing output in `zh-CN` or `en`
|
|
50
|
+
- keep storage pluggable through `ExcelStorage`
|
|
51
|
+
|
|
52
|
+
[GitHub Repository](https://github.com/RayCarterLab/ExcelAlchemy) · [Full README](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/README.md) · [Architecture](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/architecture.md) · [Migration Notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/MIGRATIONS.md)
|
|
53
|
+
|
|
54
|
+
## Screenshots
|
|
55
|
+
|
|
56
|
+
### Template
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
### Import Result
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install ExcelAlchemy
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Optional Minio support:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install "ExcelAlchemy[minio]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Minimal Example
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from pydantic import BaseModel
|
|
80
|
+
|
|
81
|
+
from excelalchemy import ExcelAlchemy, FieldMeta, ImporterConfig, Number, String
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Importer(BaseModel):
|
|
85
|
+
age: Number = FieldMeta(label='Age', order=1)
|
|
86
|
+
name: String = FieldMeta(label='Name', order=2)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
alchemy = ExcelAlchemy(ImporterConfig(Importer, locale='en'))
|
|
90
|
+
template = alchemy.download_template_artifact(filename='people-template.xlsx')
|
|
91
|
+
|
|
92
|
+
excel_bytes = template.as_bytes()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Modern Annotated Example
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from typing import Annotated
|
|
99
|
+
|
|
100
|
+
from pydantic import BaseModel, Field
|
|
101
|
+
|
|
102
|
+
from excelalchemy import Email, ExcelAlchemy, ExcelMeta, ImporterConfig
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Importer(BaseModel):
|
|
106
|
+
email: Annotated[
|
|
107
|
+
Email,
|
|
108
|
+
Field(min_length=10),
|
|
109
|
+
ExcelMeta(label='Email', order=1, hint='Use your work email'),
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
alchemy = ExcelAlchemy(ImporterConfig(Importer, locale='en'))
|
|
114
|
+
template = alchemy.download_template_artifact(filename='people-template.xlsx')
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Why ExcelAlchemy
|
|
118
|
+
|
|
119
|
+
- Pydantic v2-based schema extraction and validation
|
|
120
|
+
- locale-aware workbook comments and result workbooks
|
|
121
|
+
- pluggable storage instead of a hard-coded backend
|
|
122
|
+
- `openpyxl`-based runtime path without pandas
|
|
123
|
+
- contract tests, Ruff, and Pyright in the development workflow
|
|
124
|
+
|
|
125
|
+
## Learn More
|
|
126
|
+
|
|
127
|
+
- [Full project README](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/README.md)
|
|
128
|
+
- [Architecture notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/architecture.md)
|
|
129
|
+
- [Locale policy](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/locale.md)
|
|
130
|
+
- [Migration notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/MIGRATIONS.md)
|
|
131
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# ExcelAlchemy
|
|
2
|
+
|
|
3
|
+
Schema-driven Python library for typed Excel import/export workflows with Pydantic and locale-aware workbooks.
|
|
4
|
+
|
|
5
|
+
ExcelAlchemy turns Pydantic models into typed workbook contracts:
|
|
6
|
+
|
|
7
|
+
- generate Excel templates from code
|
|
8
|
+
- validate uploaded workbooks
|
|
9
|
+
- map failures back to rows and cells
|
|
10
|
+
- render workbook-facing output in `zh-CN` or `en`
|
|
11
|
+
- keep storage pluggable through `ExcelStorage`
|
|
12
|
+
|
|
13
|
+
[GitHub Repository](https://github.com/RayCarterLab/ExcelAlchemy) · [Full README](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/README.md) · [Architecture](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/architecture.md) · [Migration Notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/MIGRATIONS.md)
|
|
14
|
+
|
|
15
|
+
## Screenshots
|
|
16
|
+
|
|
17
|
+
### Template
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
### Import Result
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install ExcelAlchemy
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Optional Minio support:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install "ExcelAlchemy[minio]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Minimal Example
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from pydantic import BaseModel
|
|
41
|
+
|
|
42
|
+
from excelalchemy import ExcelAlchemy, FieldMeta, ImporterConfig, Number, String
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Importer(BaseModel):
|
|
46
|
+
age: Number = FieldMeta(label='Age', order=1)
|
|
47
|
+
name: String = FieldMeta(label='Name', order=2)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
alchemy = ExcelAlchemy(ImporterConfig(Importer, locale='en'))
|
|
51
|
+
template = alchemy.download_template_artifact(filename='people-template.xlsx')
|
|
52
|
+
|
|
53
|
+
excel_bytes = template.as_bytes()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Modern Annotated Example
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from typing import Annotated
|
|
60
|
+
|
|
61
|
+
from pydantic import BaseModel, Field
|
|
62
|
+
|
|
63
|
+
from excelalchemy import Email, ExcelAlchemy, ExcelMeta, ImporterConfig
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Importer(BaseModel):
|
|
67
|
+
email: Annotated[
|
|
68
|
+
Email,
|
|
69
|
+
Field(min_length=10),
|
|
70
|
+
ExcelMeta(label='Email', order=1, hint='Use your work email'),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
alchemy = ExcelAlchemy(ImporterConfig(Importer, locale='en'))
|
|
75
|
+
template = alchemy.download_template_artifact(filename='people-template.xlsx')
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Why ExcelAlchemy
|
|
79
|
+
|
|
80
|
+
- Pydantic v2-based schema extraction and validation
|
|
81
|
+
- locale-aware workbook comments and result workbooks
|
|
82
|
+
- pluggable storage instead of a hard-coded backend
|
|
83
|
+
- `openpyxl`-based runtime path without pandas
|
|
84
|
+
- contract tests, Ruff, and Pyright in the development workflow
|
|
85
|
+
|
|
86
|
+
## Learn More
|
|
87
|
+
|
|
88
|
+
- [Full project README](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/README.md)
|
|
89
|
+
- [Architecture notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/architecture.md)
|
|
90
|
+
- [Locale policy](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/docs/locale.md)
|
|
91
|
+
- [Migration notes](https://github.com/RayCarterLab/ExcelAlchemy/blob/main/MIGRATIONS.md)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ['flit_core >=3.12,<4']
|
|
3
|
+
build-backend = 'flit_core.buildapi'
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = 'ExcelAlchemy'
|
|
7
|
+
description = 'Schema-driven Python library for typed Excel import/export workflows with Pydantic and locale-aware workbooks.'
|
|
8
|
+
authors = [{ name = 'Ray' }]
|
|
9
|
+
readme = { file = 'README-pypi.md', content-type = 'text/markdown' }
|
|
10
|
+
license = { file = 'LICENSE' }
|
|
11
|
+
keywords = ['excel', 'openpyxl', 'pydantic', 'minio', 'schema']
|
|
12
|
+
classifiers = [
|
|
13
|
+
'Development Status :: 5 - Production/Stable',
|
|
14
|
+
'Intended Audience :: Developers',
|
|
15
|
+
'License :: OSI Approved :: MIT License',
|
|
16
|
+
'Operating System :: OS Independent',
|
|
17
|
+
'Programming Language :: Python',
|
|
18
|
+
'Programming Language :: Python :: 3',
|
|
19
|
+
'Programming Language :: Python :: 3 :: Only',
|
|
20
|
+
'Programming Language :: Python :: 3.12',
|
|
21
|
+
'Programming Language :: Python :: 3.13',
|
|
22
|
+
'Programming Language :: Python :: 3.14',
|
|
23
|
+
'Topic :: Office/Business :: Financial :: Spreadsheet',
|
|
24
|
+
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
25
|
+
]
|
|
26
|
+
dynamic = ['version']
|
|
27
|
+
requires-python = '>=3.12'
|
|
28
|
+
dependencies = [
|
|
29
|
+
'pydantic[email] >=2.12, <3',
|
|
30
|
+
'openpyxl >=3.1.5, <4',
|
|
31
|
+
'pendulum >=3.2.0, <4',
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.flit.module]
|
|
35
|
+
name = 'excelalchemy'
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Home = 'https://github.com/RayCarterLab/ExcelAlchemy'
|
|
39
|
+
Repository = 'https://github.com/RayCarterLab/ExcelAlchemy'
|
|
40
|
+
Documentation = 'https://github.com/RayCarterLab/ExcelAlchemy#readme'
|
|
41
|
+
Issues = 'https://github.com/RayCarterLab/ExcelAlchemy/issues'
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
minio = [
|
|
45
|
+
'minio >=7.2.20, <8',
|
|
46
|
+
]
|
|
47
|
+
development = [
|
|
48
|
+
'minio >=7.2.20, <8',
|
|
49
|
+
'pre-commit',
|
|
50
|
+
'pyright==1.1.408',
|
|
51
|
+
'pytest',
|
|
52
|
+
'coverage',
|
|
53
|
+
'pytest-cov',
|
|
54
|
+
'ruff',
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.pyright]
|
|
58
|
+
include = ['src/excelalchemy', 'tests']
|
|
59
|
+
exclude = [
|
|
60
|
+
'.venv',
|
|
61
|
+
'venv',
|
|
62
|
+
'.git',
|
|
63
|
+
'**/.mypy_cache',
|
|
64
|
+
'**/__pycache__',
|
|
65
|
+
'**/.pytest_cache',
|
|
66
|
+
'src/excelalchemy/types/field.py',
|
|
67
|
+
]
|
|
68
|
+
enableTypeIgnoreComments = false
|
|
69
|
+
reportAbstractUsage = false
|
|
70
|
+
reportAttributeAccessIssue = false
|
|
71
|
+
reportCallIssue = false
|
|
72
|
+
reportPrivateImportUsage = false
|
|
73
|
+
reportRedeclaration = false
|
|
74
|
+
strict = [
|
|
75
|
+
'src/excelalchemy/_primitives/constants.py',
|
|
76
|
+
'src/excelalchemy/_primitives/deprecation.py',
|
|
77
|
+
'src/excelalchemy/_primitives/header_models.py',
|
|
78
|
+
'src/excelalchemy/_primitives/identity.py',
|
|
79
|
+
'src/excelalchemy/_primitives/payloads.py',
|
|
80
|
+
'src/excelalchemy/artifacts.py',
|
|
81
|
+
'src/excelalchemy/codecs/base.py',
|
|
82
|
+
'src/excelalchemy/codecs/boolean.py',
|
|
83
|
+
'src/excelalchemy/codecs/date.py',
|
|
84
|
+
'src/excelalchemy/codecs/date_range.py',
|
|
85
|
+
'src/excelalchemy/codecs/email.py',
|
|
86
|
+
'src/excelalchemy/codecs/money.py',
|
|
87
|
+
'src/excelalchemy/codecs/multi_checkbox.py',
|
|
88
|
+
'src/excelalchemy/codecs/number.py',
|
|
89
|
+
'src/excelalchemy/codecs/number_range.py',
|
|
90
|
+
'src/excelalchemy/codecs/organization.py',
|
|
91
|
+
'src/excelalchemy/codecs/phone_number.py',
|
|
92
|
+
'src/excelalchemy/codecs/radio.py',
|
|
93
|
+
'src/excelalchemy/codecs/staff.py',
|
|
94
|
+
'src/excelalchemy/codecs/string.py',
|
|
95
|
+
'src/excelalchemy/codecs/tree.py',
|
|
96
|
+
'src/excelalchemy/codecs/url.py',
|
|
97
|
+
'src/excelalchemy/config.py',
|
|
98
|
+
'src/excelalchemy/core/alchemy.py',
|
|
99
|
+
'src/excelalchemy/core/abstract.py',
|
|
100
|
+
'src/excelalchemy/core/executor.py',
|
|
101
|
+
'src/excelalchemy/core/rendering.py',
|
|
102
|
+
'src/excelalchemy/core/schema.py',
|
|
103
|
+
'src/excelalchemy/core/storage.py',
|
|
104
|
+
'src/excelalchemy/core/storage_minio.py',
|
|
105
|
+
'src/excelalchemy/core/storage_protocol.py',
|
|
106
|
+
'src/excelalchemy/core/table.py',
|
|
107
|
+
'src/excelalchemy/core/writer.py',
|
|
108
|
+
'src/excelalchemy/exceptions.py',
|
|
109
|
+
'src/excelalchemy/core/headers.py',
|
|
110
|
+
'src/excelalchemy/core/rows.py',
|
|
111
|
+
'src/excelalchemy/helper/pydantic.py',
|
|
112
|
+
'src/excelalchemy/i18n/messages.py',
|
|
113
|
+
'src/excelalchemy/metadata.py',
|
|
114
|
+
'src/excelalchemy/results.py',
|
|
115
|
+
'src/excelalchemy/util/convertor.py',
|
|
116
|
+
'src/excelalchemy/util/file.py',
|
|
117
|
+
]
|
|
118
|
+
typeCheckingMode = 'basic'
|
|
119
|
+
|
|
120
|
+
[tool.ruff]
|
|
121
|
+
line-length = 120
|
|
122
|
+
target-version = 'py312'
|
|
123
|
+
src = ['src', 'tests']
|
|
124
|
+
extend-exclude = ['files']
|
|
125
|
+
|
|
126
|
+
[tool.ruff.lint]
|
|
127
|
+
select = ['E', 'F', 'I', 'UP', 'B', 'SIM', 'C4', 'RUF', 'PERF']
|
|
128
|
+
ignore = ['E501', 'RUF001', 'RUF002', 'RUF003']
|
|
129
|
+
|
|
130
|
+
[tool.ruff.lint.per-file-ignores]
|
|
131
|
+
'**/__init__.py' = ['F401']
|
|
132
|
+
'src/excelalchemy/const.py' = ['RUF100']
|
|
133
|
+
'src/excelalchemy/exc.py' = ['E402', 'RUF100']
|
|
134
|
+
'src/excelalchemy/header_models.py' = ['E402', 'RUF100']
|
|
135
|
+
'src/excelalchemy/identity.py' = ['E402', 'RUF100']
|
|
136
|
+
'src/excelalchemy/types/*.py' = ['E402', 'RUF100']
|
|
137
|
+
'src/excelalchemy/types/**/*.py' = ['E402', 'RUF100']
|
|
138
|
+
|
|
139
|
+
[tool.ruff.format]
|
|
140
|
+
quote-style = 'preserve'
|
|
141
|
+
indent-style = 'space'
|
|
142
|
+
line-ending = 'auto'
|
|
143
|
+
|
|
144
|
+
[tool.pytest.ini_options]
|
|
145
|
+
addopts = ['--import-mode=importlib']
|
|
146
|
+
testpaths = ['tests']
|
|
147
|
+
|
|
148
|
+
[tool.coverage.run]
|
|
149
|
+
branch = true
|
|
150
|
+
source = ['excelalchemy']
|
|
151
|
+
|
|
152
|
+
[tool.coverage.report]
|
|
153
|
+
fail_under = 85
|
|
154
|
+
skip_covered = true
|
|
155
|
+
show_missing = true
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""A Python Library for Reading and Writing Excel Files"""
|
|
2
|
+
|
|
3
|
+
__version__ = '2.2.2'
|
|
4
|
+
from excelalchemy._primitives.constants import CharacterSet, DataRangeOption, DateFormat, Option
|
|
5
|
+
from excelalchemy._primitives.deprecation import ExcelAlchemyDeprecationWarning
|
|
6
|
+
from excelalchemy._primitives.identity import (
|
|
7
|
+
Base64Str,
|
|
8
|
+
ColumnIndex,
|
|
9
|
+
DataUrlStr,
|
|
10
|
+
Key,
|
|
11
|
+
Label,
|
|
12
|
+
OptionId,
|
|
13
|
+
RowIndex,
|
|
14
|
+
UniqueKey,
|
|
15
|
+
UniqueLabel,
|
|
16
|
+
UrlStr,
|
|
17
|
+
)
|
|
18
|
+
from excelalchemy.artifacts import ExcelArtifact
|
|
19
|
+
from excelalchemy.codecs.base import CompositeExcelFieldCodec, ExcelFieldCodec
|
|
20
|
+
from excelalchemy.codecs.boolean import Boolean, BooleanCodec
|
|
21
|
+
from excelalchemy.codecs.date import Date, DateCodec
|
|
22
|
+
from excelalchemy.codecs.date_range import DateRange, DateRangeCodec
|
|
23
|
+
from excelalchemy.codecs.email import Email, EmailCodec
|
|
24
|
+
from excelalchemy.codecs.money import Money, MoneyCodec
|
|
25
|
+
from excelalchemy.codecs.multi_checkbox import MultiCheckbox, MultiChoiceCodec
|
|
26
|
+
from excelalchemy.codecs.number import Number, NumberCodec
|
|
27
|
+
from excelalchemy.codecs.number_range import NumberRange, NumberRangeCodec
|
|
28
|
+
from excelalchemy.codecs.organization import (
|
|
29
|
+
MultiOrganization,
|
|
30
|
+
MultiOrganizationCodec,
|
|
31
|
+
SingleOrganization,
|
|
32
|
+
SingleOrganizationCodec,
|
|
33
|
+
)
|
|
34
|
+
from excelalchemy.codecs.phone_number import PhoneNumber, PhoneNumberCodec
|
|
35
|
+
from excelalchemy.codecs.radio import Radio, SingleChoiceCodec
|
|
36
|
+
from excelalchemy.codecs.staff import MultiStaff, MultiStaffCodec, SingleStaff, SingleStaffCodec
|
|
37
|
+
from excelalchemy.codecs.string import String, StringCodec
|
|
38
|
+
from excelalchemy.codecs.tree import (
|
|
39
|
+
MultiTreeNode,
|
|
40
|
+
MultiTreeNodeCodec,
|
|
41
|
+
SingleTreeNode,
|
|
42
|
+
SingleTreeNodeCodec,
|
|
43
|
+
)
|
|
44
|
+
from excelalchemy.codecs.url import Url, UrlCodec
|
|
45
|
+
from excelalchemy.config import ExporterConfig, ImporterConfig, ImportMode
|
|
46
|
+
from excelalchemy.core.alchemy import ExcelAlchemy
|
|
47
|
+
from excelalchemy.core.storage_protocol import ExcelStorage
|
|
48
|
+
from excelalchemy.exceptions import ConfigError, ExcelCellError, ExcelRowError, ProgrammaticError
|
|
49
|
+
from excelalchemy.helper.pydantic import extract_pydantic_model
|
|
50
|
+
from excelalchemy.metadata import ExcelMeta, FieldMeta, PatchFieldMeta
|
|
51
|
+
from excelalchemy.results import ImportResult, ValidateHeaderResult, ValidateResult, ValidateRowResult
|
|
52
|
+
from excelalchemy.util.file import flatten
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
'Base64Str',
|
|
56
|
+
'Boolean',
|
|
57
|
+
'BooleanCodec',
|
|
58
|
+
'ColumnIndex',
|
|
59
|
+
'CompositeExcelFieldCodec',
|
|
60
|
+
'ConfigError',
|
|
61
|
+
'DataRangeOption',
|
|
62
|
+
'DataUrlStr',
|
|
63
|
+
'Date',
|
|
64
|
+
'DateCodec',
|
|
65
|
+
'DateFormat',
|
|
66
|
+
'DateRange',
|
|
67
|
+
'DateRangeCodec',
|
|
68
|
+
'Email',
|
|
69
|
+
'EmailCodec',
|
|
70
|
+
'ExcelAlchemy',
|
|
71
|
+
'ExcelAlchemyDeprecationWarning',
|
|
72
|
+
'ExcelArtifact',
|
|
73
|
+
'ExcelCellError',
|
|
74
|
+
'ExcelFieldCodec',
|
|
75
|
+
'ExcelMeta',
|
|
76
|
+
'ExcelRowError',
|
|
77
|
+
'ExcelStorage',
|
|
78
|
+
'ExporterConfig',
|
|
79
|
+
'FieldMeta',
|
|
80
|
+
'ImportMode',
|
|
81
|
+
'ImportResult',
|
|
82
|
+
'ImporterConfig',
|
|
83
|
+
'Key',
|
|
84
|
+
'Label',
|
|
85
|
+
'Money',
|
|
86
|
+
'MoneyCodec',
|
|
87
|
+
'MultiCheckbox',
|
|
88
|
+
'MultiChoiceCodec',
|
|
89
|
+
'MultiOrganization',
|
|
90
|
+
'MultiOrganizationCodec',
|
|
91
|
+
'MultiStaff',
|
|
92
|
+
'MultiStaffCodec',
|
|
93
|
+
'MultiTreeNode',
|
|
94
|
+
'MultiTreeNodeCodec',
|
|
95
|
+
'Number',
|
|
96
|
+
'NumberCodec',
|
|
97
|
+
'NumberRange',
|
|
98
|
+
'NumberRangeCodec',
|
|
99
|
+
'Option',
|
|
100
|
+
'OptionId',
|
|
101
|
+
'PatchFieldMeta',
|
|
102
|
+
'PhoneNumber',
|
|
103
|
+
'PhoneNumberCodec',
|
|
104
|
+
'ProgrammaticError',
|
|
105
|
+
'Radio',
|
|
106
|
+
'RowIndex',
|
|
107
|
+
'SingleChoiceCodec',
|
|
108
|
+
'SingleOrganization',
|
|
109
|
+
'SingleOrganizationCodec',
|
|
110
|
+
'SingleStaff',
|
|
111
|
+
'SingleStaffCodec',
|
|
112
|
+
'SingleTreeNode',
|
|
113
|
+
'SingleTreeNodeCodec',
|
|
114
|
+
'String',
|
|
115
|
+
'StringCodec',
|
|
116
|
+
'UniqueKey',
|
|
117
|
+
'UniqueLabel',
|
|
118
|
+
'Url',
|
|
119
|
+
'UrlCodec',
|
|
120
|
+
'UrlStr',
|
|
121
|
+
'ValidateHeaderResult',
|
|
122
|
+
'ValidateResult',
|
|
123
|
+
'ValidateRowResult',
|
|
124
|
+
'extract_pydantic_model',
|
|
125
|
+
'flatten',
|
|
126
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Private primitive building blocks used by ExcelAlchemy internals."""
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from enum import
|
|
2
|
+
from enum import StrEnum
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from excelalchemy._primitives.identity import Key, Label, OptionId
|
|
5
6
|
from excelalchemy.i18n.messages import MessageKey
|
|
6
7
|
from excelalchemy.i18n.messages import display_message as dmsg
|
|
7
|
-
from excelalchemy.types.identity import Key, Label, OptionId
|
|
8
8
|
|
|
9
9
|
HEADER_HINT = dmsg(MessageKey.HEADER_HINT, locale='zh-CN')
|
|
10
10
|
|
|
11
11
|
EXCEL_COMMENT_FORMAT = {'height': 100, 'width': 300, 'font_size': 7}
|
|
12
12
|
CHARACTER_WIDTH = 1.3
|
|
13
13
|
DEFAULT_SHEET_NAME = 'Sheet1'
|
|
14
|
-
#
|
|
14
|
+
# Connector used when flattening merged workbook headers.
|
|
15
15
|
UNIQUE_HEADER_CONNECTOR: str = '·'
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# Result workbook status column.
|
|
18
18
|
RESULT_COLUMN_LABEL: Label = Label(dmsg(MessageKey.RESULT_COLUMN_LABEL, locale='zh-CN'))
|
|
19
19
|
RESULT_COLUMN_KEY: Key = Key('__result__')
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Result workbook reason column.
|
|
22
22
|
REASON_COLUMN_LABEL: Label = Label(dmsg(MessageKey.REASON_COLUMN_LABEL, locale='zh-CN'))
|
|
23
23
|
REASON_COLUMN_KEY: Key = Key('__reason__')
|
|
24
24
|
|
|
@@ -26,15 +26,15 @@ BACKGROUND_REQUIRED_COLOR = 'FDAFB5'
|
|
|
26
26
|
BACKGROUND_ERROR_COLOR = 'FEC100'
|
|
27
27
|
FONT_READ_COLOR = 'FF0000'
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
# Display separator used for multi-choice workbook cells.
|
|
30
30
|
MULTI_CHECKBOX_SEPARATOR = ','
|
|
31
31
|
|
|
32
32
|
FIELD_DATA_KEY = Key('fieldData')
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# Millisecond to second conversion factor.
|
|
35
35
|
MILLISECOND_TO_SECOND = 1000
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Soft option-count limit used for warning logs.
|
|
38
38
|
MAX_OPTIONS_COUNT = 100
|
|
39
39
|
|
|
40
40
|
DEFAULT_FIELD_META_ORDER = -1
|
|
@@ -45,7 +45,7 @@ type ListStr = list[str]
|
|
|
45
45
|
type IntStr = int | str
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
class CharacterSet(
|
|
48
|
+
class CharacterSet(StrEnum):
|
|
49
49
|
CHINESE = 'CHINESE'
|
|
50
50
|
NUMBER = 'NUMBER'
|
|
51
51
|
LOWERCASE_LETTERS = 'LOWERCASE_LETTERS'
|
|
@@ -53,14 +53,14 @@ class CharacterSet(str, Enum):
|
|
|
53
53
|
SPECIAL_SYMBOLS = 'SPECIAL_SYMBOLS'
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
class DateFormat(
|
|
56
|
+
class DateFormat(StrEnum):
|
|
57
57
|
YEAR = 'YEAR'
|
|
58
58
|
MONTH = 'MONTH'
|
|
59
59
|
DAY = 'DAY'
|
|
60
60
|
MINUTE = 'MINUTE'
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
class DataRangeOption(
|
|
63
|
+
class DataRangeOption(StrEnum):
|
|
64
64
|
NONE = 'NONE'
|
|
65
65
|
PRE = 'PRE'
|
|
66
66
|
NEXT = 'NEXT'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Deprecation helpers for public compatibility layers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
DEPRECATION_REMOVAL_VERSION = '3.0'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExcelAlchemyDeprecationWarning(FutureWarning):
|
|
11
|
+
"""Warning emitted for deprecated public APIs that still have a compatibility shim."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def warn_compat_import(import_path: str, replacement: str) -> None:
|
|
15
|
+
warnings.warn(
|
|
16
|
+
(
|
|
17
|
+
f'`{import_path}` is deprecated and will be removed in ExcelAlchemy '
|
|
18
|
+
f'{DEPRECATION_REMOVAL_VERSION}. Import from `{replacement}` instead.'
|
|
19
|
+
),
|
|
20
|
+
category=ExcelAlchemyDeprecationWarning,
|
|
21
|
+
stacklevel=2,
|
|
22
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Internal workbook header models."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from pydantic.fields import Field
|
|
5
|
+
|
|
6
|
+
from excelalchemy._primitives.constants import UNIQUE_HEADER_CONNECTOR
|
|
7
|
+
from excelalchemy._primitives.identity import Label, UniqueLabel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExcelHeader(BaseModel):
|
|
11
|
+
"""Normalized workbook header extracted from user input."""
|
|
12
|
+
|
|
13
|
+
label: Label = Field(description='Workbook header label.')
|
|
14
|
+
parent_label: Label = Field(
|
|
15
|
+
description='Parent workbook header label. Falls back to the label itself for flat headers.'
|
|
16
|
+
)
|
|
17
|
+
offset: int = Field(default=0, description='Child-column offset under a merged parent header.')
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def unique_label(self) -> UniqueLabel:
|
|
21
|
+
"""Return the fully qualified workbook header label."""
|
|
22
|
+
label = (
|
|
23
|
+
f'{self.parent_label}{UNIQUE_HEADER_CONNECTOR}{self.label}'
|
|
24
|
+
if self.parent_label != self.label
|
|
25
|
+
else self.label
|
|
26
|
+
)
|
|
27
|
+
return UniqueLabel(label)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Internal typed primitives used across the ExcelAlchemy core layer."""
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
@@ -27,36 +27,40 @@ class _IntegerIdentity(int):
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class Label(_StringIdentity):
|
|
30
|
-
"""
|
|
30
|
+
"""Workbook header label."""
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class UniqueLabel(Label):
|
|
34
|
-
"""
|
|
34
|
+
"""Fully qualified workbook header label."""
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class Key(_StringIdentity):
|
|
38
|
-
"""Python
|
|
38
|
+
"""Schema key used by the Python model."""
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class UniqueKey(Key):
|
|
42
|
-
"""
|
|
42
|
+
"""Fully qualified schema key."""
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class RowIndex(_IntegerIdentity):
|
|
46
|
-
"""
|
|
46
|
+
"""Zero-based workbook row index."""
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class ColumnIndex(_IntegerIdentity):
|
|
50
|
-
"""
|
|
50
|
+
"""Zero-based workbook column index."""
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
class OptionId(_StringIdentity):
|
|
54
|
-
"""
|
|
54
|
+
"""Selection option identifier."""
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
class
|
|
58
|
-
"""
|
|
57
|
+
class DataUrlStr(_StringIdentity):
|
|
58
|
+
"""Data URL string."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Base64Str(DataUrlStr):
|
|
62
|
+
"""Deprecated compatibility alias for the legacy data URL string return type."""
|
|
59
63
|
|
|
60
64
|
|
|
61
65
|
class UrlStr(_StringIdentity):
|
|
62
|
-
"""URL
|
|
66
|
+
"""Generic URL string."""
|