fh-pydantic-form 0.3.3__tar.gz → 0.3.4__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.
Potentially problematic release.
This version of fh-pydantic-form might be problematic. Click here for more details.
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/PKG-INFO +1 -1
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/RELEASE_NOTES.md +4 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/pyproject.toml +1 -1
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/__init__.py +9 -6
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/field_renderers.py +18 -5
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/form_parser.py +9 -5
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/form_renderer.py +1 -1
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/.github/workflows/build.yaml +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/.github/workflows/publish.yaml +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/.gitignore +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/.pre-commit-config.yaml +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/LICENSE +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/README.md +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/color_utils.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/comparison_form.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/constants.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/defaults.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/list_path.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/py.typed +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/registry.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/type_helpers.py +0 -0
- {fh_pydantic_form-0.3.3 → fh_pydantic_form-0.3.4}/src/fh_pydantic_form/ui_style.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fh-pydantic-form
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: a library to turn any pydantic BaseModel object into a fasthtml/monsterui input form
|
|
5
5
|
Project-URL: Homepage, https://github.com/Marcura/fh-pydantic-form
|
|
6
6
|
Project-URL: Repository, https://github.com/Marcura/fh-pydantic-form
|
|
@@ -87,13 +87,16 @@ def register_default_renderers() -> None:
|
|
|
87
87
|
|
|
88
88
|
# Register list renderer for List[*] types
|
|
89
89
|
def is_list_field(field_info):
|
|
90
|
-
"""Check if field is a list type"""
|
|
90
|
+
"""Check if field is a list type, including Optional[List[...]]"""
|
|
91
91
|
annotation = getattr(field_info, "annotation", None)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
92
|
+
if annotation is None:
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
# Handle Optional[List[...]] by unwrapping the Optional
|
|
96
|
+
underlying_type = _get_underlying_type_if_optional(annotation)
|
|
97
|
+
|
|
98
|
+
# Check if the underlying type is a list
|
|
99
|
+
return get_origin(underlying_type) is list
|
|
97
100
|
|
|
98
101
|
FieldRendererRegistry.register_type_renderer_with_predicate(
|
|
99
102
|
is_list_field, ListFieldRenderer
|
|
@@ -1523,12 +1523,15 @@ class ListFieldRenderer(BaseFieldRenderer):
|
|
|
1523
1523
|
annotation = getattr(self.field_info, "annotation", None)
|
|
1524
1524
|
item_type = None # Initialize here to avoid UnboundLocalError
|
|
1525
1525
|
|
|
1526
|
+
# Handle Optional[List[...]] by unwrapping the Optional first
|
|
1527
|
+
base_annotation = _get_underlying_type_if_optional(annotation)
|
|
1528
|
+
|
|
1526
1529
|
if (
|
|
1527
|
-
|
|
1528
|
-
and hasattr(
|
|
1529
|
-
and
|
|
1530
|
+
base_annotation is not None
|
|
1531
|
+
and hasattr(base_annotation, "__origin__")
|
|
1532
|
+
and base_annotation.__origin__ is list
|
|
1530
1533
|
):
|
|
1531
|
-
item_type =
|
|
1534
|
+
item_type = base_annotation.__args__[0]
|
|
1532
1535
|
|
|
1533
1536
|
if not item_type:
|
|
1534
1537
|
logger.error(f"Cannot determine item type for list field {self.field_name}")
|
|
@@ -1600,10 +1603,20 @@ class ListFieldRenderer(BaseFieldRenderer):
|
|
|
1600
1603
|
if self.disabled:
|
|
1601
1604
|
add_button_attrs["disabled"] = "true"
|
|
1602
1605
|
|
|
1606
|
+
# Differentiate message for Optional[List] vs required List
|
|
1607
|
+
if self.is_optional:
|
|
1608
|
+
empty_message = (
|
|
1609
|
+
"No items in this optional list. Click 'Add Item' if needed."
|
|
1610
|
+
)
|
|
1611
|
+
else:
|
|
1612
|
+
empty_message = (
|
|
1613
|
+
"No items in this required list. Click 'Add Item' to create one."
|
|
1614
|
+
)
|
|
1615
|
+
|
|
1603
1616
|
empty_state = mui.Alert(
|
|
1604
1617
|
fh.Div(
|
|
1605
1618
|
mui.UkIcon("info", cls="mr-2"),
|
|
1606
|
-
|
|
1619
|
+
empty_message,
|
|
1607
1620
|
mui.Button("Add Item", **add_button_attrs),
|
|
1608
1621
|
cls="flex flex-col items-start",
|
|
1609
1622
|
),
|
|
@@ -7,8 +7,8 @@ from typing import (
|
|
|
7
7
|
Optional,
|
|
8
8
|
Tuple,
|
|
9
9
|
Union,
|
|
10
|
-
get_origin,
|
|
11
10
|
get_args,
|
|
11
|
+
get_origin,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from fh_pydantic_form.type_helpers import (
|
|
@@ -438,7 +438,7 @@ def _parse_list_fields(
|
|
|
438
438
|
list_field_defs: Dict[str, Dict[str, Any]],
|
|
439
439
|
base_prefix: str = "",
|
|
440
440
|
exclude_fields: Optional[List[str]] = None,
|
|
441
|
-
) -> Dict[str, List[Any]]:
|
|
441
|
+
) -> Dict[str, Optional[List[Any]]]:
|
|
442
442
|
"""
|
|
443
443
|
Parse list fields from form data by analyzing keys and reconstructing ordered lists.
|
|
444
444
|
|
|
@@ -490,7 +490,7 @@ def _parse_list_fields(
|
|
|
490
490
|
list_items_temp[field_name][idx_str][subfield] = value
|
|
491
491
|
|
|
492
492
|
# Build final lists based on tracked order
|
|
493
|
-
final_lists = {}
|
|
493
|
+
final_lists: Dict[str, Optional[List[Any]]] = {}
|
|
494
494
|
for field_name, ordered_indices in list_item_indices_ordered.items():
|
|
495
495
|
field_def = list_field_defs[field_name]
|
|
496
496
|
item_type = field_def["item_type"]
|
|
@@ -544,8 +544,12 @@ def _parse_list_fields(
|
|
|
544
544
|
if field_name in final_lists:
|
|
545
545
|
continue
|
|
546
546
|
|
|
547
|
-
# User submitted form with zero items → honour intent with
|
|
548
|
-
|
|
547
|
+
# User submitted form with zero items → honour intent with None for Optional[List]
|
|
548
|
+
field_info = field_def["field_info"]
|
|
549
|
+
if _is_optional_type(field_info.annotation):
|
|
550
|
+
final_lists[field_name] = None # Use None for empty Optional[List]
|
|
551
|
+
else:
|
|
552
|
+
final_lists[field_name] = [] # Regular empty list for required fields
|
|
549
553
|
|
|
550
554
|
return final_lists
|
|
551
555
|
|
|
@@ -435,7 +435,7 @@ class PydanticForm(Generic[ModelType]):
|
|
|
435
435
|
try:
|
|
436
436
|
default_factory = field_info.default_factory
|
|
437
437
|
if callable(default_factory):
|
|
438
|
-
initial_value = default_factory()
|
|
438
|
+
initial_value = default_factory() # type: ignore[call-arg]
|
|
439
439
|
else:
|
|
440
440
|
initial_value = None
|
|
441
441
|
logger.warning(
|
|
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
|