fh-pydantic-form 0.3.4__tar.gz → 0.3.5__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.

Files changed (22) hide show
  1. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/PKG-INFO +1 -1
  2. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/RELEASE_NOTES.md +8 -0
  3. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/pyproject.toml +1 -1
  4. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/__init__.py +3 -0
  5. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/defaults.py +6 -0
  6. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/field_renderers.py +62 -1
  7. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/.github/workflows/build.yaml +0 -0
  8. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/.github/workflows/publish.yaml +0 -0
  9. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/.gitignore +0 -0
  10. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/.pre-commit-config.yaml +0 -0
  11. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/LICENSE +0 -0
  12. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/README.md +0 -0
  13. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/color_utils.py +0 -0
  14. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/comparison_form.py +0 -0
  15. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/constants.py +0 -0
  16. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/form_parser.py +0 -0
  17. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/form_renderer.py +0 -0
  18. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/list_path.py +0 -0
  19. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/py.typed +0 -0
  20. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/registry.py +0 -0
  21. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/src/fh_pydantic_form/type_helpers.py +0 -0
  22. {fh_pydantic_form-0.3.4 → fh_pydantic_form-0.3.5}/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.4
3
+ Version: 0.3.5
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
@@ -1,5 +1,13 @@
1
1
  # Release Notes
2
2
 
3
+ ## Version 0.3.5 (2025-07-17)
4
+
5
+ - **NEW**: Added support for `decimal.Decimal` fields with dedicated field renderer
6
+ - **FIXED**: Scientific notation display issues in decimal values
7
+ - **IMPROVED**: MyPy type checking compliance
8
+
9
+ ---
10
+
3
11
  ## Version 0.3.4 (2025-07-15)
4
12
 
5
13
  - **NEW**: Added support for Optional[List[..]] types in form fields
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fh-pydantic-form"
3
- version = "0.3.4"
3
+ version = "0.3.5"
4
4
  description = "a library to turn any pydantic BaseModel object into a fasthtml/monsterui input form"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,4 +1,5 @@
1
1
  import datetime
2
+ import decimal
2
3
  from enum import Enum
3
4
  from typing import Literal, get_origin
4
5
 
@@ -17,6 +18,7 @@ from fh_pydantic_form.field_renderers import (
17
18
  BaseModelFieldRenderer,
18
19
  BooleanFieldRenderer,
19
20
  DateFieldRenderer,
21
+ DecimalFieldRenderer,
20
22
  EnumFieldRenderer,
21
23
  ListFieldRenderer,
22
24
  LiteralFieldRenderer,
@@ -55,6 +57,7 @@ def register_default_renderers() -> None:
55
57
  FieldRendererRegistry.register_type_renderer(bool, BooleanFieldRenderer)
56
58
  FieldRendererRegistry.register_type_renderer(int, NumberFieldRenderer)
57
59
  FieldRendererRegistry.register_type_renderer(float, NumberFieldRenderer)
60
+ FieldRendererRegistry.register_type_renderer(decimal.Decimal, DecimalFieldRenderer)
58
61
  FieldRendererRegistry.register_type_renderer(datetime.date, DateFieldRenderer)
59
62
  FieldRendererRegistry.register_type_renderer(datetime.time, TimeFieldRenderer)
60
63
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime as _dt
4
+ import decimal
4
5
  from enum import Enum
5
6
  from typing import Any, Literal, get_args, get_origin
6
7
 
@@ -25,6 +26,7 @@ _SIMPLE_DEFAULTS = {
25
26
  int: 0,
26
27
  float: 0.0,
27
28
  bool: False,
29
+ decimal.Decimal: decimal.Decimal("0"),
28
30
  _dt.date: lambda: _today(), # callable - gets current date (late-bound)
29
31
  _dt.time: lambda: _dt.time(0, 0), # callable - midnight
30
32
  }
@@ -52,6 +54,10 @@ def default_for_annotation(annotation: Any) -> Any:
52
54
  if _is_optional_type(annotation):
53
55
  return None
54
56
 
57
+ # List[T] → []
58
+ if origin is list:
59
+ return []
60
+
55
61
  # Literal[...] → first literal value
56
62
  if origin is Literal:
57
63
  return _first_literal_choice(annotation)
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import re
3
3
  from datetime import date, time
4
+ from decimal import Decimal
4
5
  from enum import Enum
5
6
  from typing import (
6
7
  Any,
@@ -510,7 +511,7 @@ class BaseFieldRenderer(MetricsRendererMixin):
510
511
  label_text_span = fh.Span(label_text, **span_attrs)
511
512
 
512
513
  # Prepare label attributes
513
- label_attrs = {"For": self.field_name}
514
+ label_attrs = {"for": self.field_name}
514
515
 
515
516
  # Build label classes with tokenized gap
516
517
  label_gap_class = spacing("label_gap", self.spacing)
@@ -705,6 +706,66 @@ class NumberFieldRenderer(BaseFieldRenderer):
705
706
  return mui.Input(**input_attrs)
706
707
 
707
708
 
709
+ class DecimalFieldRenderer(BaseFieldRenderer):
710
+ """Renderer for decimal.Decimal fields"""
711
+
712
+ def __init__(self, *args, **kwargs):
713
+ """Initialize decimal field renderer, passing all arguments to parent"""
714
+ super().__init__(*args, **kwargs)
715
+
716
+ def render_input(self) -> FT:
717
+ """
718
+ Render input element for decimal fields
719
+
720
+ Returns:
721
+ A NumberInput component appropriate for decimal values
722
+ """
723
+ # Determine if field is required
724
+ has_default = get_default(self.field_info) is not _UNSET
725
+ is_field_required = not self.is_optional and not has_default
726
+
727
+ placeholder_text = f"Enter {self.original_field_name.replace('_', ' ')}"
728
+ if self.is_optional:
729
+ placeholder_text += " (Optional)"
730
+
731
+ input_cls_parts = ["w-full"]
732
+ input_spacing_cls = spacing_many(
733
+ ["input_size", "input_padding", "input_line_height", "input_font_size"],
734
+ self.spacing,
735
+ )
736
+ if input_spacing_cls:
737
+ input_cls_parts.append(input_spacing_cls)
738
+
739
+ # Convert Decimal value to string for display
740
+ if isinstance(self.value, Decimal):
741
+ # Use format to avoid scientific notation
742
+ display_value = format(self.value, "f")
743
+ # Normalize zero values to display as "0"
744
+ if self.value == 0:
745
+ display_value = "0"
746
+ elif self.value is not None:
747
+ display_value = str(self.value)
748
+ else:
749
+ display_value = ""
750
+
751
+ input_attrs = {
752
+ "value": display_value,
753
+ "id": self.field_name,
754
+ "name": self.field_name,
755
+ "type": "number",
756
+ "placeholder": placeholder_text,
757
+ "required": is_field_required,
758
+ "cls": " ".join(input_cls_parts),
759
+ "step": "any", # Allow arbitrary decimal precision
760
+ }
761
+
762
+ # Only add the disabled attribute if the field should actually be disabled
763
+ if self.disabled:
764
+ input_attrs["disabled"] = True
765
+
766
+ return mui.Input(**input_attrs)
767
+
768
+
708
769
  class BooleanFieldRenderer(BaseFieldRenderer):
709
770
  """Renderer for boolean fields"""
710
771