pydantic-forms 2.1.2__tar.gz → 2.2.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.1.2 → pydantic_forms-2.2.0}/.bumpversion.cfg +1 -1
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/PKG-INFO +1 -1
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/__init__.py +1 -1
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/__init__.py +3 -0
- pydantic_forms-2.2.0/pydantic_forms/validators/components/callout.py +76 -0
- pydantic_forms-2.2.0/tests/unit_tests/test_callout.py +97 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.coveragerc +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.github/dependabot.yml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.github/workflows/publish-release.yaml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.github/workflows/run-linting-tests.yml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.github/workflows/run-unit-tests.yml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.gitignore +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.pre-commit-config.yaml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/.stignore +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/LICENSE +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/README.md +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/codecov.yml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/core/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/core/asynchronous.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/core/shared.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/core/sync.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/core/translations.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/exception_handlers/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/exception_handlers/fastapi.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/exceptions.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/py.typed +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/types.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/utils/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/utils/json.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/utils/schema.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/accept.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/choice.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/choice_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/contact_person.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/contact_person_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/display_subscription.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/divider.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/hidden.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/label.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/list_of_one.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/list_of_two.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/long_text.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/migration_summary.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/organisation_id.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/read_only.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/timestamp.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/unique_constrained_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/helpers.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pyproject.toml +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/setup.cfg +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/setup.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/exception_handlers/__init__.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/exception_handlers/test_fastapi.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/helpers.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_accept.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_choice.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_choice_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_constrained_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_contact_person.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_contact_person_list.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_core.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_core_async.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_display_subscription.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_divider.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_hidden.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_label.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_list_of_one.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_list_of_two.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_long_text.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_migration_summary.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_organisation_id.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_read_only_field.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_shared.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_timestamp.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_dict_without_none.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_from_serializable.py +0 -0
- {pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_to_serializable.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from pydantic_forms.validators.components.accept import Accept, AcceptValues
|
|
2
|
+
from pydantic_forms.validators.components.callout import Callout, callout
|
|
2
3
|
from pydantic_forms.validators.components.choice import Choice
|
|
3
4
|
from pydantic_forms.validators.components.choice_list import choice_list
|
|
4
5
|
from pydantic_forms.validators.components.contact_person import ContactPerson, ContactPersonName
|
|
@@ -40,4 +41,6 @@ __all__ = (
|
|
|
40
41
|
"Timestamp",
|
|
41
42
|
"unique_conlist",
|
|
42
43
|
"validate_unique_list",
|
|
44
|
+
"callout",
|
|
45
|
+
"Callout",
|
|
43
46
|
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Copyright 2019-2025 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 CalloutMessageType(str, Enum):
|
|
22
|
+
PRIMARY = "primary"
|
|
23
|
+
SUCCESS = "success"
|
|
24
|
+
WARNING = "warning"
|
|
25
|
+
DANGER = "danger"
|
|
26
|
+
ACCENT = "accent"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CalloutData(TypedDict, total=False):
|
|
30
|
+
header: Union[str, None]
|
|
31
|
+
message: Union[str, None]
|
|
32
|
+
icon_type: Union[str, None]
|
|
33
|
+
message_type: Union[CalloutMessageType, str]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _Callout(BaseModel):
|
|
37
|
+
data: ClassVar[Union[CalloutData, None]] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Callout = Annotated[_Callout, Field(frozen=True, default=None, validate_default=False)]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_callout_schema(data: CalloutData, schema: dict[str, Any]) -> None:
|
|
44
|
+
schema.update(
|
|
45
|
+
{
|
|
46
|
+
"format": "callout",
|
|
47
|
+
"type": "string",
|
|
48
|
+
"default": data,
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def callout(
|
|
54
|
+
*,
|
|
55
|
+
header: Union[str, None] = None,
|
|
56
|
+
message: Union[str, None] = None,
|
|
57
|
+
icon_type: Union[str, None] = "info",
|
|
58
|
+
message_type: Union[CalloutMessageType, str] = CalloutMessageType.PRIMARY,
|
|
59
|
+
) -> type[Callout]:
|
|
60
|
+
|
|
61
|
+
data: CalloutData = {
|
|
62
|
+
"header": header,
|
|
63
|
+
"message": message,
|
|
64
|
+
"icon_type": icon_type,
|
|
65
|
+
"message_type": message_type,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
namespace = {"data": data}
|
|
69
|
+
klass: type[Callout] = new_class("CalloutValue", (_Callout,), {}, lambda ns: ns.update(namespace))
|
|
70
|
+
|
|
71
|
+
json_schema_extra = partial(create_callout_schema, data)
|
|
72
|
+
|
|
73
|
+
return Annotated[
|
|
74
|
+
klass,
|
|
75
|
+
Field(frozen=True, default=None, validate_default=False, json_schema_extra=json_schema_extra),
|
|
76
|
+
] # type: ignore
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from uuid import uuid4
|
|
2
|
+
|
|
3
|
+
from pydantic_forms.core import FormPage
|
|
4
|
+
from pydantic_forms.validators import DisplaySubscription, Label
|
|
5
|
+
from pydantic_forms.validators.components.callout import CalloutMessageType, callout
|
|
6
|
+
from tests.unit_tests.helpers import PYDANTIC_VERSION
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_callout_default():
|
|
10
|
+
"""Ensure Callout default is None and schema defaults are correctly injected."""
|
|
11
|
+
CalloutField = callout(
|
|
12
|
+
header="Test header",
|
|
13
|
+
message="This is a test message",
|
|
14
|
+
icon_type="info",
|
|
15
|
+
message_type=CalloutMessageType.SUCCESS,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
some_sub_id = uuid4()
|
|
19
|
+
|
|
20
|
+
class Form(FormPage):
|
|
21
|
+
display_sub: DisplaySubscription = some_sub_id
|
|
22
|
+
label: Label = "bla"
|
|
23
|
+
callout_field: CalloutField
|
|
24
|
+
|
|
25
|
+
# The field itself should default to None (display-only)
|
|
26
|
+
assert Form().model_dump() == {
|
|
27
|
+
"display_sub": some_sub_id,
|
|
28
|
+
"label": "bla",
|
|
29
|
+
"callout_field": None,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# And the field accepts no input (display only)
|
|
33
|
+
assert Form(display_sub="").model_dump() == {
|
|
34
|
+
"display_sub": some_sub_id,
|
|
35
|
+
"label": "bla",
|
|
36
|
+
"callout_field": None,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_callout_schema():
|
|
41
|
+
"""Ensure the callout field generates a correct JSON schema with default data."""
|
|
42
|
+
CalloutField = callout(
|
|
43
|
+
header="Header",
|
|
44
|
+
message="A message",
|
|
45
|
+
icon_type="bell",
|
|
46
|
+
message_type="danger",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
class Form(FormPage):
|
|
50
|
+
callout_field: CalloutField
|
|
51
|
+
|
|
52
|
+
if PYDANTIC_VERSION == "2.8":
|
|
53
|
+
callout_field_ref = {"allOf": [{"$ref": "#/$defs/CalloutValue"}]}
|
|
54
|
+
else:
|
|
55
|
+
callout_field_ref = {"$ref": "#/$defs/CalloutValue"}
|
|
56
|
+
|
|
57
|
+
expected = {
|
|
58
|
+
"$defs": {"CalloutValue": {"properties": {}, "title": "CalloutValue", "type": "object"}},
|
|
59
|
+
"additionalProperties": False,
|
|
60
|
+
"properties": {
|
|
61
|
+
"callout_field": {
|
|
62
|
+
**callout_field_ref,
|
|
63
|
+
"default": {
|
|
64
|
+
"header": "Header",
|
|
65
|
+
"message": "A message",
|
|
66
|
+
"icon_type": "bell",
|
|
67
|
+
"message_type": "danger",
|
|
68
|
+
},
|
|
69
|
+
"format": "callout",
|
|
70
|
+
"type": "string",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
"title": "unknown",
|
|
74
|
+
"type": "object",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
assert Form.model_json_schema() == expected
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_callout_accepts_custom_message_type():
|
|
81
|
+
"""Ensure custom (non-enum) message_type values are accepted and serialized correctly."""
|
|
82
|
+
CalloutField = callout(
|
|
83
|
+
header="Custom",
|
|
84
|
+
message="Supports custom color values",
|
|
85
|
+
icon_type="iInCircle",
|
|
86
|
+
message_type="neutral", # custom
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
class Form(FormPage):
|
|
90
|
+
callout_field: CalloutField
|
|
91
|
+
|
|
92
|
+
schema = Form.model_json_schema()
|
|
93
|
+
|
|
94
|
+
default_data = schema["properties"]["callout_field"]["default"]
|
|
95
|
+
assert default_data["message_type"] == "neutral"
|
|
96
|
+
assert default_data["header"] == "Custom"
|
|
97
|
+
assert default_data["icon_type"] == "iInCircle"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/__init__.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/accept.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/choice.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/choice_list.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/contact_person.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/divider.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/hidden.py
RENAMED
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/list_of_one.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/list_of_two.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/long_text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/read_only.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/pydantic_forms/validators/components/timestamp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/exception_handlers/__init__.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/exception_handlers/test_fastapi.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_dict_without_none.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_from_serializable.py
RENAMED
|
File without changes
|
{pydantic_forms-2.1.2 → pydantic_forms-2.2.0}/tests/unit_tests/test_utils_to_serializable.py
RENAMED
|
File without changes
|