fh-pydantic-form 0.3.6__tar.gz → 0.3.8__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 (23) hide show
  1. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/.github/workflows/build.yaml +1 -0
  2. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/PKG-INFO +114 -4
  3. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/README.md +111 -1
  4. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/RELEASE_NOTES.md +61 -0
  5. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/pyproject.toml +3 -3
  6. fh_pydantic_form-0.3.8/src/fh_pydantic_form/comparison_form.py +1626 -0
  7. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/field_renderers.py +242 -55
  8. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/form_parser.py +82 -12
  9. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/form_renderer.py +99 -43
  10. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/type_helpers.py +22 -2
  11. fh_pydantic_form-0.3.6/src/fh_pydantic_form/comparison_form.py +0 -637
  12. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/.github/workflows/publish.yaml +0 -0
  13. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/.gitignore +0 -0
  14. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/.pre-commit-config.yaml +0 -0
  15. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/LICENSE +0 -0
  16. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/__init__.py +0 -0
  17. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/color_utils.py +0 -0
  18. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/constants.py +0 -0
  19. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/defaults.py +0 -0
  20. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/list_path.py +0 -0
  21. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/py.typed +0 -0
  22. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/registry.py +0 -0
  23. {fh_pydantic_form-0.3.6 → fh_pydantic_form-0.3.8}/src/fh_pydantic_form/ui_style.py +0 -0
@@ -27,6 +27,7 @@ jobs:
27
27
  uses: astral-sh/setup-uv@v5
28
28
  with:
29
29
  enable-cache: true
30
+ cache-dependency-glob: "pyproject.toml"
30
31
 
31
32
  - name: "Set up Python"
32
33
  uses: actions/setup-python@v5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fh-pydantic-form
3
- Version: 0.3.6
3
+ Version: 0.3.8
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
@@ -18,9 +18,9 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System
19
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.10
21
- Requires-Dist: monsterui>=1.0.19
21
+ Requires-Dist: monsterui>=1.0.29
22
22
  Requires-Dist: pydantic>=2.0
23
- Requires-Dist: python-fasthtml>=0.12.12
23
+ Requires-Dist: python-fasthtml>=0.12.29
24
24
  Description-Content-Type: text/markdown
25
25
 
26
26
  # fh-pydantic-form
@@ -480,6 +480,83 @@ form_renderer = PydanticForm(
480
480
 
481
481
  This automatic default injection means you can safely exclude fields that shouldn't be user-editable while maintaining data integrity.
482
482
 
483
+ ### SkipJsonSchema Fields
484
+
485
+ `fh-pydantic-form` provides advanced handling for `SkipJsonSchema` fields with selective visibility control. By default, fields marked with `SkipJsonSchema` are hidden from forms, but you can selectively show specific ones using the `keep_skip_json_fields` parameter.
486
+
487
+ ```python
488
+ from pydantic.json_schema import SkipJsonSchema
489
+
490
+ class DocumentModel(BaseModel):
491
+ title: str
492
+ content: str
493
+
494
+ # Hidden by default - system fields
495
+ document_id: SkipJsonSchema[str] = Field(
496
+ default_factory=lambda: f"doc_{uuid4().hex[:12]}",
497
+ description="Internal document ID"
498
+ )
499
+ created_at: SkipJsonSchema[datetime.datetime] = Field(
500
+ default_factory=datetime.datetime.now,
501
+ description="Creation timestamp"
502
+ )
503
+ version: SkipJsonSchema[int] = Field(
504
+ default=1,
505
+ description="Document version"
506
+ )
507
+
508
+ # Normal form - all SkipJsonSchema fields hidden
509
+ form_normal = PydanticForm("doc_form", DocumentModel)
510
+
511
+ # Admin form - selectively show some SkipJsonSchema fields
512
+ form_admin = PydanticForm(
513
+ "admin_form",
514
+ DocumentModel,
515
+ keep_skip_json_fields=[
516
+ "document_id", # Show document ID
517
+ "version", # Show version number
518
+ # created_at remains hidden
519
+ ]
520
+ )
521
+ ```
522
+
523
+ #### Nested SkipJsonSchema Fields
524
+
525
+ The feature supports dot notation for nested objects and list items:
526
+
527
+ ```python
528
+ class Address(BaseModel):
529
+ street: str
530
+ city: str
531
+ # Hidden system field
532
+ internal_id: SkipJsonSchema[str] = Field(default_factory=lambda: f"addr_{uuid4().hex[:8]}")
533
+
534
+ class UserModel(BaseModel):
535
+ name: str
536
+ main_address: Address
537
+ other_addresses: List[Address]
538
+
539
+ # Show specific nested SkipJsonSchema fields
540
+ form = PydanticForm(
541
+ "user_form",
542
+ UserModel,
543
+ keep_skip_json_fields=[
544
+ "main_address.internal_id", # Show main address ID
545
+ "other_addresses.internal_id", # Show all address IDs in the list
546
+ ]
547
+ )
548
+ ```
549
+
550
+ **Key Features:**
551
+ - **Hidden by default:** SkipJsonSchema fields are automatically excluded from forms
552
+ - **Selective visibility:** Use `keep_skip_json_fields` to show specific fields
553
+ - **Nested support:** Access nested fields with dot notation (`"main_address.internal_id"`)
554
+ - **List support:** Show fields in all list items (`"addresses.internal_id"`)
555
+ - **Smart defaults:** Non-kept fields use model defaults, kept fields retain initial values
556
+ - **Admin interfaces:** Perfect for admin panels or debugging where you need to see system fields
557
+
558
+ See `examples/complex_example.py` for a comprehensive demonstration of SkipJsonSchema field handling.
559
+
483
560
  ## Refreshing & Resetting
484
561
 
485
562
  Forms support dynamic refresh and reset functionality:
@@ -824,6 +901,38 @@ comparison_form.register_routes(app)
824
901
  - **Metrics Integration**: Right side typically shows LLM output quality scores
825
902
  - **Flexible Layout**: Responsive design works on desktop and mobile
826
903
  - **Form Validation**: Standard validation works with either form
904
+ - **Intelligent List Copying**: Copy lists between forms with automatic length adjustment
905
+
906
+ ### Copying Between Forms
907
+
908
+ The ComparisonForm provides granular copy functionality at multiple levels. When you enable copy buttons (via `copy_left=True` or `copy_right=True`), each field, nested model, and list item gets its own copy button for maximum flexibility:
909
+
910
+ ```python
911
+ # Enable copy buttons (copy FROM right TO left)
912
+ comparison_form = ComparisonForm(
913
+ name="extraction_evaluation",
914
+ left_form=left_form,
915
+ right_form=right_form,
916
+ left_label="Ground Truth",
917
+ right_label="LLM Output",
918
+ copy_left=True, # Show copy buttons on right form to copy TO left
919
+ )
920
+ ```
921
+
922
+ **Copy Granularity Levels:**
923
+
924
+ The copy feature works at five different levels of granularity:
925
+
926
+ 1. **Individual Fields** - Copy a single field value (e.g., `name`, `price`, `status`)
927
+
928
+ 2. **Nested BaseModel (Entire Object)** - Copy all fields within a nested model at once
929
+
930
+ 3. **Individual Fields in Nested Models** - Copy a specific field within a nested object
931
+
932
+ 4. **Full List Fields** - Copy entire lists with automatic length adjustment
933
+
934
+ 5. **Individual List Items** - Add a single item from one list to another
935
+
827
936
 
828
937
  ### Common Patterns
829
938
 
@@ -1007,6 +1116,7 @@ form_renderer = PydanticForm(
1007
1116
  | `disabled_fields` | `Optional[List[str]]` | `None` | List of specific field names to disable |
1008
1117
  | `label_colors` | `Optional[Dict[str, str]]` | `None` | Mapping of field names to CSS colors or Tailwind classes |
1009
1118
  | `exclude_fields` | `Optional[List[str]]` | `None` | List of field names to exclude from rendering (auto-injected on submission) |
1119
+ | `keep_skip_json_fields` | `Optional[List[str]]` | `None` | List of SkipJsonSchema field paths to selectively show (supports dot notation for nested fields) |
1010
1120
  | `spacing` | `SpacingValue` | `"normal"` | Spacing theme: `"normal"`, `"compact"`, or `SpacingTheme` enum |
1011
1121
  | `metrics_dict` | `Optional[Dict[str, Dict]]` | `None` | Field metrics for highlighting and tooltips |
1012
1122
 
@@ -1025,7 +1135,7 @@ form_renderer = PydanticForm(
1025
1135
  | Method | Purpose |
1026
1136
  |--------|---------|
1027
1137
  | `render_inputs()` | Generate the HTML form inputs (without `<form>` wrapper) |
1028
- | `with_initial_values(initial_values)` | Create a new form instance with same configuration but different initial values |
1138
+ | `with_initial_values(initial_values, metrics_dict=None)` | Create a new form instance with same configuration but different initial values |
1029
1139
  | `refresh_button(text=None, **kwargs)` | Create a refresh button component |
1030
1140
  | `reset_button(text=None, **kwargs)` | Create a reset button component |
1031
1141
  | `register_routes(app)` | Register HTMX endpoints for list manipulation |
@@ -455,6 +455,83 @@ form_renderer = PydanticForm(
455
455
 
456
456
  This automatic default injection means you can safely exclude fields that shouldn't be user-editable while maintaining data integrity.
457
457
 
458
+ ### SkipJsonSchema Fields
459
+
460
+ `fh-pydantic-form` provides advanced handling for `SkipJsonSchema` fields with selective visibility control. By default, fields marked with `SkipJsonSchema` are hidden from forms, but you can selectively show specific ones using the `keep_skip_json_fields` parameter.
461
+
462
+ ```python
463
+ from pydantic.json_schema import SkipJsonSchema
464
+
465
+ class DocumentModel(BaseModel):
466
+ title: str
467
+ content: str
468
+
469
+ # Hidden by default - system fields
470
+ document_id: SkipJsonSchema[str] = Field(
471
+ default_factory=lambda: f"doc_{uuid4().hex[:12]}",
472
+ description="Internal document ID"
473
+ )
474
+ created_at: SkipJsonSchema[datetime.datetime] = Field(
475
+ default_factory=datetime.datetime.now,
476
+ description="Creation timestamp"
477
+ )
478
+ version: SkipJsonSchema[int] = Field(
479
+ default=1,
480
+ description="Document version"
481
+ )
482
+
483
+ # Normal form - all SkipJsonSchema fields hidden
484
+ form_normal = PydanticForm("doc_form", DocumentModel)
485
+
486
+ # Admin form - selectively show some SkipJsonSchema fields
487
+ form_admin = PydanticForm(
488
+ "admin_form",
489
+ DocumentModel,
490
+ keep_skip_json_fields=[
491
+ "document_id", # Show document ID
492
+ "version", # Show version number
493
+ # created_at remains hidden
494
+ ]
495
+ )
496
+ ```
497
+
498
+ #### Nested SkipJsonSchema Fields
499
+
500
+ The feature supports dot notation for nested objects and list items:
501
+
502
+ ```python
503
+ class Address(BaseModel):
504
+ street: str
505
+ city: str
506
+ # Hidden system field
507
+ internal_id: SkipJsonSchema[str] = Field(default_factory=lambda: f"addr_{uuid4().hex[:8]}")
508
+
509
+ class UserModel(BaseModel):
510
+ name: str
511
+ main_address: Address
512
+ other_addresses: List[Address]
513
+
514
+ # Show specific nested SkipJsonSchema fields
515
+ form = PydanticForm(
516
+ "user_form",
517
+ UserModel,
518
+ keep_skip_json_fields=[
519
+ "main_address.internal_id", # Show main address ID
520
+ "other_addresses.internal_id", # Show all address IDs in the list
521
+ ]
522
+ )
523
+ ```
524
+
525
+ **Key Features:**
526
+ - **Hidden by default:** SkipJsonSchema fields are automatically excluded from forms
527
+ - **Selective visibility:** Use `keep_skip_json_fields` to show specific fields
528
+ - **Nested support:** Access nested fields with dot notation (`"main_address.internal_id"`)
529
+ - **List support:** Show fields in all list items (`"addresses.internal_id"`)
530
+ - **Smart defaults:** Non-kept fields use model defaults, kept fields retain initial values
531
+ - **Admin interfaces:** Perfect for admin panels or debugging where you need to see system fields
532
+
533
+ See `examples/complex_example.py` for a comprehensive demonstration of SkipJsonSchema field handling.
534
+
458
535
  ## Refreshing & Resetting
459
536
 
460
537
  Forms support dynamic refresh and reset functionality:
@@ -799,6 +876,38 @@ comparison_form.register_routes(app)
799
876
  - **Metrics Integration**: Right side typically shows LLM output quality scores
800
877
  - **Flexible Layout**: Responsive design works on desktop and mobile
801
878
  - **Form Validation**: Standard validation works with either form
879
+ - **Intelligent List Copying**: Copy lists between forms with automatic length adjustment
880
+
881
+ ### Copying Between Forms
882
+
883
+ The ComparisonForm provides granular copy functionality at multiple levels. When you enable copy buttons (via `copy_left=True` or `copy_right=True`), each field, nested model, and list item gets its own copy button for maximum flexibility:
884
+
885
+ ```python
886
+ # Enable copy buttons (copy FROM right TO left)
887
+ comparison_form = ComparisonForm(
888
+ name="extraction_evaluation",
889
+ left_form=left_form,
890
+ right_form=right_form,
891
+ left_label="Ground Truth",
892
+ right_label="LLM Output",
893
+ copy_left=True, # Show copy buttons on right form to copy TO left
894
+ )
895
+ ```
896
+
897
+ **Copy Granularity Levels:**
898
+
899
+ The copy feature works at five different levels of granularity:
900
+
901
+ 1. **Individual Fields** - Copy a single field value (e.g., `name`, `price`, `status`)
902
+
903
+ 2. **Nested BaseModel (Entire Object)** - Copy all fields within a nested model at once
904
+
905
+ 3. **Individual Fields in Nested Models** - Copy a specific field within a nested object
906
+
907
+ 4. **Full List Fields** - Copy entire lists with automatic length adjustment
908
+
909
+ 5. **Individual List Items** - Add a single item from one list to another
910
+
802
911
 
803
912
  ### Common Patterns
804
913
 
@@ -982,6 +1091,7 @@ form_renderer = PydanticForm(
982
1091
  | `disabled_fields` | `Optional[List[str]]` | `None` | List of specific field names to disable |
983
1092
  | `label_colors` | `Optional[Dict[str, str]]` | `None` | Mapping of field names to CSS colors or Tailwind classes |
984
1093
  | `exclude_fields` | `Optional[List[str]]` | `None` | List of field names to exclude from rendering (auto-injected on submission) |
1094
+ | `keep_skip_json_fields` | `Optional[List[str]]` | `None` | List of SkipJsonSchema field paths to selectively show (supports dot notation for nested fields) |
985
1095
  | `spacing` | `SpacingValue` | `"normal"` | Spacing theme: `"normal"`, `"compact"`, or `SpacingTheme` enum |
986
1096
  | `metrics_dict` | `Optional[Dict[str, Dict]]` | `None` | Field metrics for highlighting and tooltips |
987
1097
 
@@ -1000,7 +1110,7 @@ form_renderer = PydanticForm(
1000
1110
  | Method | Purpose |
1001
1111
  |--------|---------|
1002
1112
  | `render_inputs()` | Generate the HTML form inputs (without `<form>` wrapper) |
1003
- | `with_initial_values(initial_values)` | Create a new form instance with same configuration but different initial values |
1113
+ | `with_initial_values(initial_values, metrics_dict=None)` | Create a new form instance with same configuration but different initial values |
1004
1114
  | `refresh_button(text=None, **kwargs)` | Create a refresh button component |
1005
1115
  | `reset_button(text=None, **kwargs)` | Create a reset button component |
1006
1116
  | `register_routes(app)` | Register HTMX endpoints for list manipulation |
@@ -1,5 +1,66 @@
1
1
  # Release Notes
2
2
 
3
+ ## Version 0.3.8 (2025-10-02)
4
+
5
+ ### 🎉 New Features
6
+
7
+ #### Intelligent Copying in ComparisonForm
8
+ - **NEW**: Comprehensive copy functionality at multiple granularity levels
9
+ - Individual field copying
10
+ - Full nested BaseModel object copying
11
+ - Individual fields within nested models
12
+ - **Full list field copying with automatic length alignment**
13
+ - Individual list item copying with smart insertion
14
+
15
+
16
+ ### 🔧 Bug Fixes & Improvements
17
+
18
+ #### SkipJsonSchema Field Handling
19
+ - **FIXED**: SkipJsonSchema fields now properly preserve initial values when provided
20
+ - **IMPROVED**: Better handling of skip fields with default values
21
+ - **ENHANCED**: More robust field introspection for SkipJsonSchema annotation
22
+
23
+
24
+ ## Version 0.3.7 (2025-09-19)
25
+
26
+ ### 🎉 New Features
27
+
28
+ #### SkipJsonSchema Field Support with Selective Override
29
+ - **NEW**: Added comprehensive support for fields marked with `SkipJsonSchema` annotation
30
+ - **NEW**: `keep_skip_json_fields` parameter allows selective inclusion of specific SkipJsonSchema fields
31
+ - Supports dot-notation paths for nested fields (e.g., `"addresses.internal_id"`)
32
+ - Enables fine-grained control over which internal fields are exposed in forms
33
+ - Works with complex nested structures and list fields
34
+ - **ENHANCED**: SkipJsonSchema fields are automatically excluded from form rendering by default
35
+ - **IMPROVED**: Better field introspection for complex type scenarios including optional skip fields
36
+
37
+ ### 🔧 Bug Fixes & Improvements
38
+
39
+ #### Default Values Handling
40
+ - **FIXED**: Default values for simple fields now work correctly without initial values
41
+ - **IMPROVED**: Better handling of field defaults when no initial values are provided
42
+ - **ENHANCED**: More robust form rendering for fields with default values
43
+
44
+ #### Documentation & Examples
45
+ - **UPDATED**: README.md with SkipJsonSchema handling documentation
46
+ - **ENHANCED**: Complex example updated to demonstrate SkipJsonSchema usage patterns
47
+ - **IMPROVED**: Better code documentation and examples
48
+
49
+ ### 🧪 Testing
50
+ - **NEW**: Comprehensive test coverage for SkipJsonSchema field handling
51
+ - **NEW**: Tests for default values behavior without initial values
52
+ - **IMPROVED**: Enhanced test coverage for edge cases and type introspection
53
+
54
+ ### 📊 Statistics
55
+ - **7 commits** since v0.3.6
56
+ - Focus on optional field handling and default value improvements
57
+ - Enhanced SkipJsonSchema support with comprehensive testing
58
+
59
+ **Key Highlights:**
60
+ This release significantly improves handling of optional fields, particularly those marked with `SkipJsonSchema`, and fixes important issues with default value handling when no initial values are provided.
61
+
62
+ ---
63
+
3
64
  ## Version 0.3.6 (2025-07-21)
4
65
 
5
66
  - **NEW**: can now pass new metrics_dict to `.with_initial_values()` helper method.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fh-pydantic-form"
3
- version = "0.3.6"
3
+ version = "0.3.8"
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"
@@ -29,9 +29,9 @@ classifiers = [
29
29
  "Operating System :: OS Independent",
30
30
  ]
31
31
  dependencies = [
32
- "monsterui>=1.0.19",
32
+ "monsterui>=1.0.29",
33
33
  "pydantic>=2.0",
34
- "python-fasthtml>=0.12.12",
34
+ "python-fasthtml>=0.12.29",
35
35
  ]
36
36
 
37
37
  [project.urls]