pydantic-wizard 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gianluca Pagliara
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydantic-wizard
3
+ Version: 0.1.0
4
+ Summary: Interactive wizard-style CLI for configuring Pydantic v2 models, with YAML serialization.
5
+ Author-email: Gianluca Pagliara <pagliara.gianluca@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3.13
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Typing :: Typed
9
+ Requires-Python: <3.14,>=3.13
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pydantic>=2.0
13
+ Requires-Dist: typer>=0.15
14
+ Requires-Dist: rich>=13.0
15
+ Requires-Dist: questionary>=2.0
16
+ Requires-Dist: PyYAML>=6.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: pytest; extra == "dev"
19
+ Requires-Dist: ruff; extra == "dev"
20
+ Requires-Dist: mypy; extra == "dev"
21
+ Requires-Dist: types-PyYAML; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # pydantic-wizard
25
+
26
+ Interactive wizard-style CLI for configuring [Pydantic v2](https://docs.pydantic.dev/) models, with YAML serialization.
27
+
28
+ Point it at any `BaseModel` subclass and it will walk you through every field — with type-aware prompts, constraint validation, and nested model support — then save the result as a clean YAML file.
29
+
30
+ ## Features
31
+
32
+ - **Zero boilerplate** — works with any Pydantic v2 `BaseModel`, no registration needed
33
+ - **Type-aware prompts** — booleans get yes/no, enums get dropdowns, nested models recurse automatically
34
+ - **Constraint enforcement** — respects `ge`, `le`, `gt`, `lt` and other Pydantic field validators
35
+ - **15+ built-in type handlers** — scalars, `Decimal`, `Enum`, `Literal`, `datetime`, `Optional`, `list`, `set`, `dict`, `Union`, nested models
36
+ - **YAML round-trip** — serialize with metadata, load back, edit, and re-save
37
+ - **Interactive validation** — on error, re-prompts only the failing fields
38
+ - **Extensible** — register custom `TypeHandler` implementations for domain-specific types
39
+ - **Rich terminal output** — field panels, summary tables, and colored messages via [Rich](https://github.com/Textualize/rich)
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install pydantic-wizard
45
+ ```
46
+
47
+ Or with [uv](https://docs.astral.sh/uv/):
48
+
49
+ ```bash
50
+ uv add pydantic-wizard
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### Define a model
56
+
57
+ ```python
58
+ # myapp/config.py
59
+ from decimal import Decimal
60
+ from pydantic import BaseModel, Field
61
+
62
+ class DatabaseConfig(BaseModel):
63
+ host: str = Field(description="Database hostname")
64
+ port: int = Field(default=5432, ge=1, le=65535)
65
+ name: str = Field(description="Database name")
66
+ pool_size: int = Field(default=10, ge=1)
67
+ timeout: Decimal = Field(default=Decimal("30.0"), ge=0)
68
+ ssl_enabled: bool = Field(default=True)
69
+ ```
70
+
71
+ ### Run the wizard from the CLI
72
+
73
+ ```bash
74
+ pydantic-wizard new myapp.config.DatabaseConfig -o db.yaml
75
+ ```
76
+
77
+ The wizard will prompt you for each field with type-appropriate inputs, validate the result, and save to `db.yaml`.
78
+
79
+ ### Or use it programmatically
80
+
81
+ ```python
82
+ from pydantic_wizard import prompt_model, serialize_to_yaml, validate_and_fix
83
+ from myapp.config import DatabaseConfig
84
+ from pathlib import Path
85
+
86
+ # Interactive prompt for all fields
87
+ data = prompt_model(DatabaseConfig)
88
+
89
+ # Validate (re-prompts on errors)
90
+ instance = validate_and_fix(DatabaseConfig, data)
91
+
92
+ # Save to YAML
93
+ serialize_to_yaml(data, DatabaseConfig, Path("db.yaml"), model_name="database")
94
+ ```
95
+
96
+ ## CLI Commands
97
+
98
+ ### `new` — Create a configuration
99
+
100
+ ```bash
101
+ pydantic-wizard new <MODEL_FQN> [--output, -o PATH]
102
+ ```
103
+
104
+ Walks through every field interactively, validates, and saves to YAML.
105
+
106
+ ### `edit` — Edit an existing configuration
107
+
108
+ ```bash
109
+ pydantic-wizard edit <CONFIG_FILE> [--output, -o PATH]
110
+ ```
111
+
112
+ Loads an existing YAML file and re-runs the wizard with current values as defaults.
113
+
114
+ ### `validate` — Validate a configuration
115
+
116
+ ```bash
117
+ pydantic-wizard validate <CONFIG_FILE> [--model, -m FQN]
118
+ ```
119
+
120
+ Validates a YAML file against its Pydantic model. The model class is resolved from YAML metadata or the `--model` flag.
121
+
122
+ ### `show-schema` — Show model schema
123
+
124
+ ```bash
125
+ pydantic-wizard show-schema <MODEL_FQN>
126
+ ```
127
+
128
+ Displays a formatted table of all fields, types, defaults, and descriptions.
129
+
130
+ ## Supported Types
131
+
132
+ | Category | Types |
133
+ |----------|-------|
134
+ | **Scalars** | `str`, `int`, `float`, `bool`, `Decimal` |
135
+ | **Enums & Literals** | `Enum` subclasses, `Literal["a", "b", "c"]` |
136
+ | **Date & Time** | `datetime`, `time`, `timedelta` |
137
+ | **Optional** | `T \| None`, `Optional[T]` |
138
+ | **Collections** | `list[T]`, `set[T]`, `dict[K, V]` |
139
+ | **Unions** | `A \| B \| C` (type selection prompt) |
140
+ | **Nested Models** | Any `BaseModel` subclass (recursive prompting) |
141
+
142
+ ## Programmatic API
143
+
144
+ ### Introspection
145
+
146
+ ```python
147
+ from pydantic_wizard import introspect_model, FieldSpec
148
+
149
+ specs: list[FieldSpec] = introspect_model(DatabaseConfig)
150
+ for spec in specs:
151
+ print(f"{spec.name}: {spec.inner_type}, required={spec.is_required}")
152
+ ```
153
+
154
+ ### Custom Type Handlers
155
+
156
+ Register custom handlers for domain-specific types:
157
+
158
+ ```python
159
+ from pydantic_wizard import TypeHandlerRegistry, prompt_model
160
+ from pydantic_wizard.type_handlers import TypeHandler
161
+
162
+ class MoneyHandler:
163
+ def can_handle(self, spec):
164
+ return spec.inner_type is Money
165
+
166
+ def prompt(self, spec, default=None):
167
+ raw = questionary.text(f" {spec.name} (e.g. 100.00 USD):").ask()
168
+ return Money.parse(raw)
169
+
170
+ def serialize(self, value):
171
+ return str(value)
172
+
173
+ def deserialize(self, raw, spec):
174
+ return Money.parse(raw)
175
+
176
+ # Use the custom registry
177
+ registry = TypeHandlerRegistry()
178
+ registry.register(MoneyHandler()) # inserted at front, takes priority
179
+ data = prompt_model(MyConfig, registry=registry)
180
+ ```
181
+
182
+ ## YAML Output Format
183
+
184
+ Generated YAML files include metadata for round-trip support:
185
+
186
+ ```yaml
187
+ _metadata:
188
+ model_type: DatabaseConfig
189
+ configuration_class: myapp.config.DatabaseConfig
190
+ version: 0.1.0
191
+ configuration:
192
+ host: localhost
193
+ port: 5432
194
+ name: mydb
195
+ pool_size: 10
196
+ timeout: "30.0"
197
+ ssl_enabled: true
198
+ ```
199
+
200
+ - **Decimals** are serialized as quoted strings to preserve precision
201
+ - **Enums** are serialized as their `.value`
202
+ - **Sets** are serialized as sorted lists
203
+ - **Datetimes** use ISO 8601 format
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,184 @@
1
+ # pydantic-wizard
2
+
3
+ Interactive wizard-style CLI for configuring [Pydantic v2](https://docs.pydantic.dev/) models, with YAML serialization.
4
+
5
+ Point it at any `BaseModel` subclass and it will walk you through every field — with type-aware prompts, constraint validation, and nested model support — then save the result as a clean YAML file.
6
+
7
+ ## Features
8
+
9
+ - **Zero boilerplate** — works with any Pydantic v2 `BaseModel`, no registration needed
10
+ - **Type-aware prompts** — booleans get yes/no, enums get dropdowns, nested models recurse automatically
11
+ - **Constraint enforcement** — respects `ge`, `le`, `gt`, `lt` and other Pydantic field validators
12
+ - **15+ built-in type handlers** — scalars, `Decimal`, `Enum`, `Literal`, `datetime`, `Optional`, `list`, `set`, `dict`, `Union`, nested models
13
+ - **YAML round-trip** — serialize with metadata, load back, edit, and re-save
14
+ - **Interactive validation** — on error, re-prompts only the failing fields
15
+ - **Extensible** — register custom `TypeHandler` implementations for domain-specific types
16
+ - **Rich terminal output** — field panels, summary tables, and colored messages via [Rich](https://github.com/Textualize/rich)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install pydantic-wizard
22
+ ```
23
+
24
+ Or with [uv](https://docs.astral.sh/uv/):
25
+
26
+ ```bash
27
+ uv add pydantic-wizard
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Define a model
33
+
34
+ ```python
35
+ # myapp/config.py
36
+ from decimal import Decimal
37
+ from pydantic import BaseModel, Field
38
+
39
+ class DatabaseConfig(BaseModel):
40
+ host: str = Field(description="Database hostname")
41
+ port: int = Field(default=5432, ge=1, le=65535)
42
+ name: str = Field(description="Database name")
43
+ pool_size: int = Field(default=10, ge=1)
44
+ timeout: Decimal = Field(default=Decimal("30.0"), ge=0)
45
+ ssl_enabled: bool = Field(default=True)
46
+ ```
47
+
48
+ ### Run the wizard from the CLI
49
+
50
+ ```bash
51
+ pydantic-wizard new myapp.config.DatabaseConfig -o db.yaml
52
+ ```
53
+
54
+ The wizard will prompt you for each field with type-appropriate inputs, validate the result, and save to `db.yaml`.
55
+
56
+ ### Or use it programmatically
57
+
58
+ ```python
59
+ from pydantic_wizard import prompt_model, serialize_to_yaml, validate_and_fix
60
+ from myapp.config import DatabaseConfig
61
+ from pathlib import Path
62
+
63
+ # Interactive prompt for all fields
64
+ data = prompt_model(DatabaseConfig)
65
+
66
+ # Validate (re-prompts on errors)
67
+ instance = validate_and_fix(DatabaseConfig, data)
68
+
69
+ # Save to YAML
70
+ serialize_to_yaml(data, DatabaseConfig, Path("db.yaml"), model_name="database")
71
+ ```
72
+
73
+ ## CLI Commands
74
+
75
+ ### `new` — Create a configuration
76
+
77
+ ```bash
78
+ pydantic-wizard new <MODEL_FQN> [--output, -o PATH]
79
+ ```
80
+
81
+ Walks through every field interactively, validates, and saves to YAML.
82
+
83
+ ### `edit` — Edit an existing configuration
84
+
85
+ ```bash
86
+ pydantic-wizard edit <CONFIG_FILE> [--output, -o PATH]
87
+ ```
88
+
89
+ Loads an existing YAML file and re-runs the wizard with current values as defaults.
90
+
91
+ ### `validate` — Validate a configuration
92
+
93
+ ```bash
94
+ pydantic-wizard validate <CONFIG_FILE> [--model, -m FQN]
95
+ ```
96
+
97
+ Validates a YAML file against its Pydantic model. The model class is resolved from YAML metadata or the `--model` flag.
98
+
99
+ ### `show-schema` — Show model schema
100
+
101
+ ```bash
102
+ pydantic-wizard show-schema <MODEL_FQN>
103
+ ```
104
+
105
+ Displays a formatted table of all fields, types, defaults, and descriptions.
106
+
107
+ ## Supported Types
108
+
109
+ | Category | Types |
110
+ |----------|-------|
111
+ | **Scalars** | `str`, `int`, `float`, `bool`, `Decimal` |
112
+ | **Enums & Literals** | `Enum` subclasses, `Literal["a", "b", "c"]` |
113
+ | **Date & Time** | `datetime`, `time`, `timedelta` |
114
+ | **Optional** | `T \| None`, `Optional[T]` |
115
+ | **Collections** | `list[T]`, `set[T]`, `dict[K, V]` |
116
+ | **Unions** | `A \| B \| C` (type selection prompt) |
117
+ | **Nested Models** | Any `BaseModel` subclass (recursive prompting) |
118
+
119
+ ## Programmatic API
120
+
121
+ ### Introspection
122
+
123
+ ```python
124
+ from pydantic_wizard import introspect_model, FieldSpec
125
+
126
+ specs: list[FieldSpec] = introspect_model(DatabaseConfig)
127
+ for spec in specs:
128
+ print(f"{spec.name}: {spec.inner_type}, required={spec.is_required}")
129
+ ```
130
+
131
+ ### Custom Type Handlers
132
+
133
+ Register custom handlers for domain-specific types:
134
+
135
+ ```python
136
+ from pydantic_wizard import TypeHandlerRegistry, prompt_model
137
+ from pydantic_wizard.type_handlers import TypeHandler
138
+
139
+ class MoneyHandler:
140
+ def can_handle(self, spec):
141
+ return spec.inner_type is Money
142
+
143
+ def prompt(self, spec, default=None):
144
+ raw = questionary.text(f" {spec.name} (e.g. 100.00 USD):").ask()
145
+ return Money.parse(raw)
146
+
147
+ def serialize(self, value):
148
+ return str(value)
149
+
150
+ def deserialize(self, raw, spec):
151
+ return Money.parse(raw)
152
+
153
+ # Use the custom registry
154
+ registry = TypeHandlerRegistry()
155
+ registry.register(MoneyHandler()) # inserted at front, takes priority
156
+ data = prompt_model(MyConfig, registry=registry)
157
+ ```
158
+
159
+ ## YAML Output Format
160
+
161
+ Generated YAML files include metadata for round-trip support:
162
+
163
+ ```yaml
164
+ _metadata:
165
+ model_type: DatabaseConfig
166
+ configuration_class: myapp.config.DatabaseConfig
167
+ version: 0.1.0
168
+ configuration:
169
+ host: localhost
170
+ port: 5432
171
+ name: mydb
172
+ pool_size: 10
173
+ timeout: "30.0"
174
+ ssl_enabled: true
175
+ ```
176
+
177
+ - **Decimals** are serialized as quoted strings to preserve precision
178
+ - **Enums** are serialized as their `.value`
179
+ - **Sets** are serialized as sorted lists
180
+ - **Datetimes** use ISO 8601 format
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,85 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pydantic-wizard"
7
+ version = "0.1.0"
8
+ description = "Interactive wizard-style CLI for configuring Pydantic v2 models, with YAML serialization."
9
+ readme = "README.md"
10
+ authors = [{ name = "Gianluca Pagliara", email = "pagliara.gianluca@gmail.com" }]
11
+ requires-python = ">=3.13,<3.14"
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3.13",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Typing :: Typed",
16
+ ]
17
+ dependencies = [
18
+ "pydantic>=2.0",
19
+ "typer>=0.15",
20
+ "rich>=13.0",
21
+ "questionary>=2.0",
22
+ "PyYAML>=6.0",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = [
27
+ "pytest",
28
+ "ruff",
29
+ "mypy",
30
+ "types-PyYAML",
31
+ ]
32
+
33
+ [project.scripts]
34
+ pydantic-wizard = "pydantic_wizard.app:app"
35
+
36
+ [tool.setuptools]
37
+ include-package-data = true
38
+
39
+ [tool.setuptools.package-dir]
40
+ "" = "src"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+ include = ["pydantic_wizard*"]
45
+
46
+ [tool.pytest.ini_options]
47
+ pythonpath = ["src"]
48
+ testpaths = ["tests"]
49
+
50
+ [tool.ruff]
51
+ line-length = 88
52
+ target-version = "py313"
53
+
54
+ [tool.ruff.lint]
55
+ select = [
56
+ "E",
57
+ "W",
58
+ "F",
59
+ "I",
60
+ "C",
61
+ "B",
62
+ "UP",
63
+ ]
64
+ ignore = [
65
+ "C901",
66
+ "E203",
67
+ "E501",
68
+ ]
69
+
70
+ [tool.ruff.lint.per-file-ignores]
71
+ "__init__.py" = ["F401"]
72
+
73
+ [tool.mypy]
74
+ python_version = "3.13"
75
+ warn_return_any = true
76
+ warn_unused_configs = true
77
+ disallow_untyped_defs = true
78
+ check_untyped_defs = true
79
+ strict = true
80
+ ignore_missing_imports = true
81
+ disable_error_code = ["misc"]
82
+ exclude = ["tests/.*"]
83
+ namespace_packages = true
84
+ explicit_package_bases = true
85
+ mypy_path = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,45 @@
1
+ """pydantic-wizard: Interactive wizard-style CLI for configuring Pydantic v2 models."""
2
+
3
+ from pydantic_wizard.display import (
4
+ display_model_list,
5
+ display_schema,
6
+ display_summary_table,
7
+ display_validation_errors,
8
+ )
9
+ from pydantic_wizard.introspection import (
10
+ FieldSpec,
11
+ get_type_display_name,
12
+ introspect_model,
13
+ )
14
+ from pydantic_wizard.prompts import prompt_model
15
+ from pydantic_wizard.serialization import (
16
+ load_from_yaml,
17
+ resolve_config_class,
18
+ serialize_to_yaml,
19
+ )
20
+ from pydantic_wizard.type_handlers import TypeHandler, TypeHandlerRegistry
21
+ from pydantic_wizard.validation import validate_and_fix, validate_config
22
+
23
+ __all__ = [
24
+ # Introspection
25
+ "FieldSpec",
26
+ "introspect_model",
27
+ "get_type_display_name",
28
+ # Prompting
29
+ "prompt_model",
30
+ # Serialization
31
+ "serialize_to_yaml",
32
+ "load_from_yaml",
33
+ "resolve_config_class",
34
+ # Validation
35
+ "validate_config",
36
+ "validate_and_fix",
37
+ # Type handlers
38
+ "TypeHandler",
39
+ "TypeHandlerRegistry",
40
+ # Display
41
+ "display_model_list",
42
+ "display_schema",
43
+ "display_summary_table",
44
+ "display_validation_errors",
45
+ ]