pydantic-forms 2.4.0__tar.gz → 2.6.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.
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/PKG-INFO +39 -95
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/README.md +17 -42
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/__init__.py +3 -1
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/core/asynchronous.py +5 -2
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/core/shared.py +32 -1
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/core/sync.py +5 -2
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/exceptions.py +20 -4
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/__init__.py +3 -0
- pydantic_forms-2.6.0/pydantic_forms/validators/components/markdown.py +70 -0
- pydantic_forms-2.6.0/pyproject.toml +238 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_core.py +106 -1
- pydantic_forms-2.6.0/tests/unit_tests/test_default_factory.py +57 -0
- pydantic_forms-2.6.0/tests/unit_tests/test_markdown.py +73 -0
- pydantic_forms-2.4.0/.bumpversion.cfg +0 -12
- pydantic_forms-2.4.0/.coveragerc +0 -3
- pydantic_forms-2.4.0/.github/dependabot.yml +0 -11
- pydantic_forms-2.4.0/.github/workflows/publish-release.yaml +0 -25
- pydantic_forms-2.4.0/.github/workflows/run-linting-tests.yml +0 -37
- pydantic_forms-2.4.0/.github/workflows/run-unit-tests.yml +0 -73
- pydantic_forms-2.4.0/.gitignore +0 -130
- pydantic_forms-2.4.0/.pre-commit-config.yaml +0 -53
- pydantic_forms-2.4.0/.stignore +0 -117
- pydantic_forms-2.4.0/codecov.yml +0 -2
- pydantic_forms-2.4.0/pyproject.toml +0 -171
- pydantic_forms-2.4.0/setup.cfg +0 -61
- pydantic_forms-2.4.0/setup.py +0 -20
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/LICENSE +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/core/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/core/translations.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/exception_handlers/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/exception_handlers/fastapi.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/py.typed +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/types.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/utils/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/utils/json.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/utils/schema.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/accept.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/callout.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/choice.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/choice_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/contact_person.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/contact_person_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/display_subscription.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/divider.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/hidden.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/label.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/list_of_one.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/list_of_two.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/long_text.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/migration_summary.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/organisation_id.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/read_only.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/timestamp.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/components/unique_constrained_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/constants.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/pydantic_forms/validators/helpers.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/exception_handlers/__init__.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/exception_handlers/test_fastapi.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/helpers.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_accept.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_callout.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_choice.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_choice_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_constrained_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_contact_person.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_contact_person_list.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_core_async.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_display_subscription.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_divider.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_hidden.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_label.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_list_of_one.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_list_of_two.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_long_text.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_migration_summary.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_organisation_id.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_read_only_field.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_shared.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_timestamp.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_utils_dict_without_none.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_utils_from_serializable.py +0 -0
- {pydantic_forms-2.4.0 → pydantic_forms-2.6.0}/tests/unit_tests/test_utils_to_serializable.py +0 -0
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-forms
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Pydantic-forms engine.
|
|
5
|
-
Home-page: https://github.com/workfloworchestrator/pydantic-forms
|
|
6
5
|
Author: SURF
|
|
7
|
-
Author-email: automation-beheer@surf.nl
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Classifier: Intended Audience :: Information Technology
|
|
11
|
-
Classifier: Intended Audience :: System Administrators
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Topic :: Internet
|
|
16
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Classifier: Topic :: Software Development
|
|
20
|
-
Classifier: Typing :: Typed
|
|
6
|
+
Author-email: SURF <automation-beheer@surf.nl>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
21
9
|
Classifier: Development Status :: 4 - Beta
|
|
22
10
|
Classifier: Environment :: Web Environment
|
|
23
11
|
Classifier: Framework :: AsyncIO
|
|
@@ -25,56 +13,38 @@ Classifier: Framework :: FastAPI
|
|
|
25
13
|
Classifier: Framework :: Flask
|
|
26
14
|
Classifier: Framework :: Pydantic :: 2
|
|
27
15
|
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
28
18
|
Classifier: Intended Audience :: Telecommunications Industry
|
|
29
|
-
Classifier:
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
30
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
31
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
32
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
33
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
34
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
35
26
|
Classifier: Programming Language :: Python :: 3.14
|
|
27
|
+
Classifier: Programming Language :: Python
|
|
36
28
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
37
29
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
Classifier: Topic :: Internet
|
|
31
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
32
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
33
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
34
|
+
Classifier: Topic :: Software Development
|
|
35
|
+
Classifier: Typing :: Typed
|
|
36
|
+
Requires-Dist: more-itertools>=10.0.0
|
|
40
37
|
Requires-Dist: pydantic[email]>=2.9.0
|
|
41
38
|
Requires-Dist: pydantic-i18n==0.4.5
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist:
|
|
44
|
-
Requires-
|
|
45
|
-
Requires-Dist: pre-commit ; extra == "dev"
|
|
46
|
-
Requires-Dist: pydocstyle ; extra == "dev"
|
|
47
|
-
Requires-Dist: python-dotenv ; extra == "dev"
|
|
48
|
-
Requires-Dist: watchdog ; extra == "dev"
|
|
49
|
-
Requires-Dist: mkdocs ; extra == "doc"
|
|
50
|
-
Requires-Dist: mkdocs-material ; extra == "doc"
|
|
51
|
-
Requires-Dist: mkdocs-render-swagger-plugin ; extra == "doc"
|
|
52
|
-
Requires-Dist: mkdocs-include-markdown-plugin ; extra == "doc"
|
|
53
|
-
Requires-Dist: fastapi >=0.80.0 ; extra == "fastapi"
|
|
54
|
-
Requires-Dist: orjson >=3.6.0,<4.0.0 ; extra == "orjson"
|
|
55
|
-
Requires-Dist: apache-license-check ; extra == "test"
|
|
56
|
-
Requires-Dist: black ; extra == "test"
|
|
57
|
-
Requires-Dist: structlog ; extra == "test"
|
|
58
|
-
Requires-Dist: jsonref ; extra == "test"
|
|
59
|
-
Requires-Dist: mypy==1.18.2 ; extra == "test"
|
|
60
|
-
Requires-Dist: pytest ; extra == "test"
|
|
61
|
-
Requires-Dist: pytest-cov ; extra == "test"
|
|
62
|
-
Requires-Dist: pytest-asyncio ; extra == "test"
|
|
63
|
-
Requires-Dist: pytest-xdist ; extra == "test"
|
|
64
|
-
Requires-Dist: ruff ; extra == "test"
|
|
65
|
-
Requires-Dist: types-Deprecated ; extra == "test"
|
|
66
|
-
Requires-Dist: types-certifi ; extra == "test"
|
|
67
|
-
Requires-Dist: types-python-dateutil ; extra == "test"
|
|
68
|
-
Requires-Dist: types-pytz ; extra == "test"
|
|
69
|
-
Requires-Dist: types-toml ; extra == "test"
|
|
70
|
-
Requires-Dist: types-ujson ; extra == "test"
|
|
71
|
-
Requires-Dist: types-orjson ; extra == "test"
|
|
39
|
+
Requires-Dist: fastapi>=0.103.2 ; extra == 'fastapi'
|
|
40
|
+
Requires-Dist: orjson>=3.10.7,<4.0.0 ; extra == 'orjson'
|
|
41
|
+
Requires-Python: >=3.10, <3.15
|
|
72
42
|
Project-URL: Documentation, https://github.com/workfloworchestrator/pydantic-forms/blob/main/README.md
|
|
73
|
-
|
|
74
|
-
|
|
43
|
+
Project-URL: Homepage, https://github.com/workfloworchestrator/pydantic-forms
|
|
44
|
+
Project-URL: Source, https://github.com/workfloworchestrator/pydantic-forms
|
|
75
45
|
Provides-Extra: fastapi
|
|
76
46
|
Provides-Extra: orjson
|
|
77
|
-
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
78
48
|
|
|
79
49
|
# Pydantic forms
|
|
80
50
|
|
|
@@ -95,71 +65,46 @@ Documentation regarding the usage of Forms can be found
|
|
|
95
65
|
[here](https://github.com/workfloworchestrator/orchestrator-core/blob/main/docs/architecture/application/forms-frontend.md)
|
|
96
66
|
|
|
97
67
|
### Installation (Development standalone)
|
|
98
|
-
Install the project and its dependencies to develop on the code.
|
|
99
|
-
|
|
100
|
-
#### Step 1 - install flit:
|
|
101
68
|
|
|
102
|
-
|
|
103
|
-
python3 -m venv venv
|
|
104
|
-
source venv/bin/activate
|
|
105
|
-
pip install flit
|
|
106
|
-
```
|
|
69
|
+
This project uses [uv](https://docs.astral.sh/uv/). Install the project and its dependencies to develop on the code:
|
|
107
70
|
|
|
108
|
-
#### Step 2 - install the development code:
|
|
109
71
|
```shell
|
|
110
|
-
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
!!! danger
|
|
114
|
-
Make sure to use the flit binary that is installed in your environment. You can check the correct
|
|
115
|
-
path by running
|
|
116
|
-
```shell
|
|
117
|
-
which flit
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
To be sure that the packages will be installed against the correct venv you can also prepend the python interpreter
|
|
121
|
-
that you want to use:
|
|
122
|
-
|
|
123
|
-
```shell
|
|
124
|
-
flit install --deps develop --symlink --python venv/bin/python
|
|
72
|
+
uv sync --all-extras
|
|
125
73
|
```
|
|
126
74
|
|
|
75
|
+
This creates a `.venv`, installs `pydantic-forms` in editable mode, and installs the `dev` dependency group
|
|
76
|
+
(which chains in `test` and `doc`) plus the `fastapi` and `orjson` extras. There is no need to create or
|
|
77
|
+
activate a virtualenv yourself -- prefix commands with `uv run` and uv will use the right interpreter.
|
|
127
78
|
|
|
128
79
|
### Running tests
|
|
129
80
|
Run the unit-test suite to verify a correct setup.
|
|
130
81
|
|
|
131
|
-
#### Step 2 - Run tests
|
|
132
82
|
```shell
|
|
133
|
-
pytest tests/unit_tests
|
|
83
|
+
uv run pytest tests/unit_tests
|
|
134
84
|
```
|
|
135
85
|
|
|
136
86
|
or with xdist:
|
|
137
87
|
|
|
138
88
|
```shell
|
|
139
|
-
pytest -n auto tests/unit_tests
|
|
89
|
+
uv run pytest -n auto tests/unit_tests
|
|
140
90
|
```
|
|
141
91
|
|
|
142
92
|
If you do not encounter any failures in the test, you should be able to develop features in the pydantic-forms.
|
|
143
93
|
|
|
144
|
-
### Installation (Development
|
|
94
|
+
### Installation (Development version used by a project that depends on pydantic-forms)
|
|
145
95
|
|
|
146
|
-
If you are working on a project that already uses
|
|
147
|
-
against it,
|
|
148
|
-
automatically replace the pypi dep with a symlink to the development version
|
|
149
|
-
of the core and update/downgrade all required packages in your own project.
|
|
150
|
-
|
|
151
|
-
#### Step 1 - install flit:
|
|
96
|
+
If you are working on a project that already uses `pydantic-forms` and you want to test your new form features
|
|
97
|
+
against it, point that project at your local checkout. Run this **from the other project**:
|
|
152
98
|
|
|
153
99
|
```shell
|
|
154
|
-
|
|
155
|
-
source venv/bin/activate
|
|
156
|
-
pip install flit
|
|
100
|
+
uv add --editable /path/to/pydantic-forms
|
|
157
101
|
```
|
|
158
102
|
|
|
159
|
-
|
|
103
|
+
This replaces the PyPI dependency with an editable install of your working copy and records it under
|
|
104
|
+
`[tool.uv.sources]` in that project's `pyproject.toml`. Undo it with:
|
|
160
105
|
|
|
161
106
|
```shell
|
|
162
|
-
|
|
107
|
+
uv remove pydantic-forms && uv add pydantic-forms
|
|
163
108
|
```
|
|
164
109
|
|
|
165
110
|
# Increasing the version number for a (pre) release.
|
|
@@ -169,11 +114,11 @@ When your PR is accepted you will get a version number.
|
|
|
169
114
|
You can do the necessary change with a clean, e.g. every change committed, branch:
|
|
170
115
|
|
|
171
116
|
```shell
|
|
172
|
-
|
|
117
|
+
uv version 0.0.1
|
|
173
118
|
```
|
|
174
119
|
|
|
175
|
-
Note: specifying
|
|
176
|
-
set a "RC1" version if needed.
|
|
120
|
+
Note: specifying the version explicitly, instead of relying on `uv version --bump patch` to increase it, allows
|
|
121
|
+
you to set a "RC1" version if needed -- e.g. `uv version 0.0.1rc1`.
|
|
177
122
|
|
|
178
123
|
# Debugging Form behaviour
|
|
179
124
|
|
|
@@ -185,4 +130,3 @@ LOG_LEVEL_PYDANTIC_FORMS=DEBUG
|
|
|
185
130
|
|
|
186
131
|
This will add the traceback to the `JSONResponse`. If the loglevel is set to DEBUG the library will also add the
|
|
187
132
|
traceback to the logger.
|
|
188
|
-
|
|
@@ -17,71 +17,46 @@ Documentation regarding the usage of Forms can be found
|
|
|
17
17
|
[here](https://github.com/workfloworchestrator/orchestrator-core/blob/main/docs/architecture/application/forms-frontend.md)
|
|
18
18
|
|
|
19
19
|
### Installation (Development standalone)
|
|
20
|
-
Install the project and its dependencies to develop on the code.
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
This project uses [uv](https://docs.astral.sh/uv/). Install the project and its dependencies to develop on the code:
|
|
23
22
|
|
|
24
23
|
```shell
|
|
25
|
-
|
|
26
|
-
source venv/bin/activate
|
|
27
|
-
pip install flit
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
#### Step 2 - install the development code:
|
|
31
|
-
```shell
|
|
32
|
-
flit install --deps develop --symlink --python venv/bin/python
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
!!! danger
|
|
36
|
-
Make sure to use the flit binary that is installed in your environment. You can check the correct
|
|
37
|
-
path by running
|
|
38
|
-
```shell
|
|
39
|
-
which flit
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
To be sure that the packages will be installed against the correct venv you can also prepend the python interpreter
|
|
43
|
-
that you want to use:
|
|
44
|
-
|
|
45
|
-
```shell
|
|
46
|
-
flit install --deps develop --symlink --python venv/bin/python
|
|
24
|
+
uv sync --all-extras
|
|
47
25
|
```
|
|
48
26
|
|
|
27
|
+
This creates a `.venv`, installs `pydantic-forms` in editable mode, and installs the `dev` dependency group
|
|
28
|
+
(which chains in `test` and `doc`) plus the `fastapi` and `orjson` extras. There is no need to create or
|
|
29
|
+
activate a virtualenv yourself -- prefix commands with `uv run` and uv will use the right interpreter.
|
|
49
30
|
|
|
50
31
|
### Running tests
|
|
51
32
|
Run the unit-test suite to verify a correct setup.
|
|
52
33
|
|
|
53
|
-
#### Step 2 - Run tests
|
|
54
34
|
```shell
|
|
55
|
-
pytest tests/unit_tests
|
|
35
|
+
uv run pytest tests/unit_tests
|
|
56
36
|
```
|
|
57
37
|
|
|
58
38
|
or with xdist:
|
|
59
39
|
|
|
60
40
|
```shell
|
|
61
|
-
pytest -n auto tests/unit_tests
|
|
41
|
+
uv run pytest -n auto tests/unit_tests
|
|
62
42
|
```
|
|
63
43
|
|
|
64
44
|
If you do not encounter any failures in the test, you should be able to develop features in the pydantic-forms.
|
|
65
45
|
|
|
66
|
-
### Installation (Development
|
|
67
|
-
|
|
68
|
-
If you are working on a project that already uses the `pydantic-forms` and you want to test your new form features
|
|
69
|
-
against it, you can use some `flit` magic to symlink the dev version of the forms to your project. It will
|
|
70
|
-
automatically replace the pypi dep with a symlink to the development version
|
|
71
|
-
of the core and update/downgrade all required packages in your own project.
|
|
46
|
+
### Installation (Development version used by a project that depends on pydantic-forms)
|
|
72
47
|
|
|
73
|
-
|
|
48
|
+
If you are working on a project that already uses `pydantic-forms` and you want to test your new form features
|
|
49
|
+
against it, point that project at your local checkout. Run this **from the other project**:
|
|
74
50
|
|
|
75
51
|
```shell
|
|
76
|
-
|
|
77
|
-
source venv/bin/activate
|
|
78
|
-
pip install flit
|
|
52
|
+
uv add --editable /path/to/pydantic-forms
|
|
79
53
|
```
|
|
80
54
|
|
|
81
|
-
|
|
55
|
+
This replaces the PyPI dependency with an editable install of your working copy and records it under
|
|
56
|
+
`[tool.uv.sources]` in that project's `pyproject.toml`. Undo it with:
|
|
82
57
|
|
|
83
58
|
```shell
|
|
84
|
-
|
|
59
|
+
uv remove pydantic-forms && uv add pydantic-forms
|
|
85
60
|
```
|
|
86
61
|
|
|
87
62
|
# Increasing the version number for a (pre) release.
|
|
@@ -91,11 +66,11 @@ When your PR is accepted you will get a version number.
|
|
|
91
66
|
You can do the necessary change with a clean, e.g. every change committed, branch:
|
|
92
67
|
|
|
93
68
|
```shell
|
|
94
|
-
|
|
69
|
+
uv version 0.0.1
|
|
95
70
|
```
|
|
96
71
|
|
|
97
|
-
Note: specifying
|
|
98
|
-
set a "RC1" version if needed.
|
|
72
|
+
Note: specifying the version explicitly, instead of relying on `uv version --bump patch` to increase it, allows
|
|
73
|
+
you to set a "RC1" version if needed -- e.g. `uv version 0.0.1rc1`.
|
|
99
74
|
|
|
100
75
|
# Debugging Form behaviour
|
|
101
76
|
|
|
@@ -18,7 +18,7 @@ import structlog
|
|
|
18
18
|
from pydantic import ValidationError
|
|
19
19
|
from pydantic_i18n import PydanticI18n
|
|
20
20
|
|
|
21
|
-
from pydantic_forms.core.shared import FORMS
|
|
21
|
+
from pydantic_forms.core.shared import FORMS, GenerateFormJsonSchema
|
|
22
22
|
from pydantic_forms.core.translations import translations
|
|
23
23
|
from pydantic_forms.exceptions import (
|
|
24
24
|
FormException,
|
|
@@ -102,7 +102,10 @@ async def post_form(
|
|
|
102
102
|
return generated_form
|
|
103
103
|
|
|
104
104
|
# Form is not completely filled raise next form
|
|
105
|
-
raise FormNotCompleteError(
|
|
105
|
+
raise FormNotCompleteError(
|
|
106
|
+
generated_form.model_json_schema(schema_generator=GenerateFormJsonSchema),
|
|
107
|
+
meta=getattr(generated_form, "meta__", None),
|
|
108
|
+
)
|
|
106
109
|
|
|
107
110
|
|
|
108
111
|
def _get_form(key: str) -> StateInputFormGeneratorAsync:
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
# See the License for the specific language governing permissions and
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
from inspect import isasyncgenfunction, isgeneratorfunction
|
|
14
|
-
from typing import Any, Callable
|
|
14
|
+
from typing import Any, Callable, cast
|
|
15
15
|
|
|
16
16
|
import structlog
|
|
17
17
|
from pydantic import BaseModel, ConfigDict, PydanticUndefinedAnnotation, version
|
|
18
|
+
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
|
|
19
|
+
from pydantic_core import core_schema
|
|
18
20
|
|
|
19
21
|
logger = structlog.get_logger(__name__)
|
|
20
22
|
|
|
@@ -22,6 +24,35 @@ logger = structlog.get_logger(__name__)
|
|
|
22
24
|
PYDANTIC_VERSION = version.version_short()
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
class GenerateFormJsonSchema(GenerateJsonSchema):
|
|
28
|
+
"""JSON schema generator that also emits ``default`` values produced by ``default_factory``.
|
|
29
|
+
|
|
30
|
+
Pydantic intentionally leaves ``default_factory`` results out of the generated JSON schema. For
|
|
31
|
+
forms this is a problem: ``default_factory`` is the recommended way to declare mutable defaults
|
|
32
|
+
(``Field(default_factory=list)`` and friends), and frontends pre-fill fields from the schema
|
|
33
|
+
``default``. Without it such fields render uninitialised, and dynamic array/object widgets reject
|
|
34
|
+
submission until the user performs a dummy add/remove interaction to materialise an empty value.
|
|
35
|
+
|
|
36
|
+
This generator evaluates argument-less factories and adds their result as the schema ``default``.
|
|
37
|
+
Factories that need the already-validated data (``default_factory_takes_data``) cannot be
|
|
38
|
+
evaluated at schema-generation time and are skipped, as is any factory that raises.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue:
|
|
42
|
+
json_schema = super().default_schema(schema)
|
|
43
|
+
if (
|
|
44
|
+
"default" not in json_schema
|
|
45
|
+
and "default_factory" in schema
|
|
46
|
+
and not schema.get("default_factory_takes_data", False)
|
|
47
|
+
):
|
|
48
|
+
factory = cast(Callable[[], Any], schema["default_factory"])
|
|
49
|
+
try:
|
|
50
|
+
json_schema["default"] = self.encode_default(factory())
|
|
51
|
+
except Exception: # never let a factory break schema generation
|
|
52
|
+
logger.debug("Could not evaluate default_factory for form schema", exc_info=True)
|
|
53
|
+
return json_schema
|
|
54
|
+
|
|
55
|
+
|
|
25
56
|
class DisplayOnlyFieldType:
|
|
26
57
|
pass
|
|
27
58
|
|
|
@@ -18,7 +18,7 @@ import structlog
|
|
|
18
18
|
from pydantic import ValidationError
|
|
19
19
|
from pydantic_i18n import PydanticI18n
|
|
20
20
|
|
|
21
|
-
from pydantic_forms.core.shared import FORMS
|
|
21
|
+
from pydantic_forms.core.shared import FORMS, GenerateFormJsonSchema
|
|
22
22
|
from pydantic_forms.core.translations import translations
|
|
23
23
|
from pydantic_forms.exceptions import FormException, FormNotCompleteError, FormOverflowError, FormValidationError
|
|
24
24
|
from pydantic_forms.types import InputForm, State, StateInputFormGenerator
|
|
@@ -88,7 +88,10 @@ def post_form(
|
|
|
88
88
|
generated_form = generator.send(form_validated_data)
|
|
89
89
|
|
|
90
90
|
# Form is not completely filled; raise next form
|
|
91
|
-
raise FormNotCompleteError(
|
|
91
|
+
raise FormNotCompleteError(
|
|
92
|
+
generated_form.model_json_schema(schema_generator=GenerateFormJsonSchema),
|
|
93
|
+
meta=getattr(generated_form, "meta__", None),
|
|
94
|
+
)
|
|
92
95
|
except StopIteration as e:
|
|
93
96
|
if user_inputs:
|
|
94
97
|
raise FormOverflowError(f"Did not process all user_inputs ({len(user_inputs)} remaining)")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import traceback
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
from typing import Any, Iterable, Optional, TypedDict, Union, cast
|
|
4
5
|
|
|
5
6
|
import structlog
|
|
@@ -36,9 +37,22 @@ class FormOverflowError(FormException):
|
|
|
36
37
|
"""Raised when more inputs are provided than the form can process."""
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
Loc = tuple[Union[int, str], ...]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _error_is_nested_object_validator(error: ErrorDetails) -> bool:
|
|
44
|
+
"""Return True for custom errors raised for an object payload instead of a leaf field."""
|
|
45
|
+
exc = error.get("ctx", {}).get("error")
|
|
46
|
+
return (
|
|
47
|
+
bool(error["loc"]) and isinstance(exc, (ValueError, AssertionError)) and isinstance(error.get("input"), Mapping)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
39
51
|
# def convert_errors(validation_error: ValidationError) -> Iterable[ErrorDetails]:
|
|
40
52
|
def convert_errors(
|
|
41
|
-
validation_error: ValidationError,
|
|
53
|
+
validation_error: ValidationError,
|
|
54
|
+
tr: PydanticI18n,
|
|
55
|
+
locale: str = "en_US",
|
|
42
56
|
) -> Iterable[ErrorDetails]:
|
|
43
57
|
"""Convert Pydantic's error messages to our needs.
|
|
44
58
|
|
|
@@ -55,6 +69,11 @@ def convert_errors(
|
|
|
55
69
|
# Reinstate the "__root__" location for validation errors raised by Pydantic v1's root_validator.
|
|
56
70
|
# With Pydantic v2's model_validator the location is an empty string, which breaks frontend clients.
|
|
57
71
|
error["loc"] = ("__root__",)
|
|
72
|
+
elif _error_is_nested_object_validator(error):
|
|
73
|
+
# Errors raised by a model_validator on a nested model point at the nested model itself, which
|
|
74
|
+
# frontend clients cannot map to an input field. Mark them as model-level errors so that clients
|
|
75
|
+
# display the custom message, like they do for the form's own "__root__" errors.
|
|
76
|
+
error["loc"] = (*error["loc"], "__root__")
|
|
58
77
|
return
|
|
59
78
|
|
|
60
79
|
if LOG_LEVEL_PYDANTIC_FORMS == "DEBUG":
|
|
@@ -90,9 +109,6 @@ class FormValidationError(FormException):
|
|
|
90
109
|
)
|
|
91
110
|
|
|
92
111
|
|
|
93
|
-
Loc = tuple[Union[int, str], ...]
|
|
94
|
-
|
|
95
|
-
|
|
96
112
|
class _ErrorDictRequired(TypedDict):
|
|
97
113
|
loc: Loc
|
|
98
114
|
msg: str
|
|
@@ -11,6 +11,7 @@ from pydantic_forms.validators.components.label import Label
|
|
|
11
11
|
from pydantic_forms.validators.components.list_of_one import ListOfOne
|
|
12
12
|
from pydantic_forms.validators.components.list_of_two import ListOfTwo
|
|
13
13
|
from pydantic_forms.validators.components.long_text import LongText
|
|
14
|
+
from pydantic_forms.validators.components.markdown import Markdown, markdown
|
|
14
15
|
from pydantic_forms.validators.components.migration_summary import MigrationSummary, migration_summary
|
|
15
16
|
from pydantic_forms.validators.components.organisation_id import OrganisationId
|
|
16
17
|
from pydantic_forms.validators.components.read_only import read_only_field, read_only_list
|
|
@@ -43,4 +44,6 @@ __all__ = (
|
|
|
43
44
|
"validate_unique_list",
|
|
44
45
|
"callout",
|
|
45
46
|
"Callout",
|
|
47
|
+
"markdown",
|
|
48
|
+
"Markdown",
|
|
46
49
|
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright 2019-2026 SURF.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from functools import partial
|
|
15
|
+
from types import new_class
|
|
16
|
+
from typing import Annotated, Any, ClassVar, TypedDict, Union
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MarkdownColor(str, Enum):
|
|
22
|
+
PRIMARY = "primary"
|
|
23
|
+
SUCCESS = "success"
|
|
24
|
+
WARNING = "warning"
|
|
25
|
+
DANGER = "danger"
|
|
26
|
+
ACCENT = "accent"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MarkdownData(TypedDict, total=False):
|
|
30
|
+
content: Union[str, None]
|
|
31
|
+
color: Union[MarkdownColor, str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _Markdown(BaseModel):
|
|
35
|
+
data: ClassVar[Union[MarkdownData, None]] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Markdown = Annotated[_Markdown, Field(frozen=True, default=None, validate_default=False)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_markdown_schema(data: MarkdownData, schema: dict[str, Any]) -> None:
|
|
42
|
+
schema.update(
|
|
43
|
+
{
|
|
44
|
+
"format": "markdown",
|
|
45
|
+
"type": "string",
|
|
46
|
+
"default": data,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def markdown(
|
|
52
|
+
*,
|
|
53
|
+
content: Union[str, None] = None,
|
|
54
|
+
color: Union[MarkdownColor, str] = MarkdownColor.PRIMARY,
|
|
55
|
+
) -> type[Markdown]:
|
|
56
|
+
|
|
57
|
+
data: MarkdownData = {
|
|
58
|
+
"content": content,
|
|
59
|
+
"color": color,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
namespace = {"data": data}
|
|
63
|
+
klass: type[Markdown] = new_class("MarkdownValue", (_Markdown,), {}, lambda ns: ns.update(namespace))
|
|
64
|
+
|
|
65
|
+
json_schema_extra = partial(create_markdown_schema, data)
|
|
66
|
+
|
|
67
|
+
return Annotated[
|
|
68
|
+
klass,
|
|
69
|
+
Field(frozen=True, default=None, validate_default=False, json_schema_extra=json_schema_extra),
|
|
70
|
+
] # type: ignore
|