pydantic-fixturegen 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
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 pydantic-fixturegen might be problematic. Click here for more details.
- pydantic_fixturegen/api/__init__.py +137 -0
- pydantic_fixturegen/api/_runtime.py +726 -0
- pydantic_fixturegen/api/models.py +73 -0
- pydantic_fixturegen/cli/__init__.py +32 -1
- pydantic_fixturegen/cli/check.py +230 -0
- pydantic_fixturegen/cli/diff.py +992 -0
- pydantic_fixturegen/cli/doctor.py +188 -35
- pydantic_fixturegen/cli/gen/_common.py +134 -7
- pydantic_fixturegen/cli/gen/explain.py +597 -40
- pydantic_fixturegen/cli/gen/fixtures.py +244 -112
- pydantic_fixturegen/cli/gen/json.py +229 -138
- pydantic_fixturegen/cli/gen/schema.py +170 -85
- pydantic_fixturegen/cli/init.py +333 -0
- pydantic_fixturegen/cli/schema.py +45 -0
- pydantic_fixturegen/cli/watch.py +126 -0
- pydantic_fixturegen/core/config.py +137 -3
- pydantic_fixturegen/core/config_schema.py +178 -0
- pydantic_fixturegen/core/constraint_report.py +305 -0
- pydantic_fixturegen/core/errors.py +42 -0
- pydantic_fixturegen/core/field_policies.py +100 -0
- pydantic_fixturegen/core/generate.py +241 -37
- pydantic_fixturegen/core/io_utils.py +10 -2
- pydantic_fixturegen/core/path_template.py +197 -0
- pydantic_fixturegen/core/presets.py +73 -0
- pydantic_fixturegen/core/providers/temporal.py +10 -0
- pydantic_fixturegen/core/safe_import.py +146 -12
- pydantic_fixturegen/core/seed_freeze.py +176 -0
- pydantic_fixturegen/emitters/json_out.py +65 -16
- pydantic_fixturegen/emitters/pytest_codegen.py +68 -13
- pydantic_fixturegen/emitters/schema_out.py +27 -3
- pydantic_fixturegen/logging.py +114 -0
- pydantic_fixturegen/schemas/config.schema.json +244 -0
- pydantic_fixturegen-1.1.0.dist-info/METADATA +173 -0
- pydantic_fixturegen-1.1.0.dist-info/RECORD +57 -0
- pydantic_fixturegen-1.0.0.dist-info/METADATA +0 -280
- pydantic_fixturegen-1.0.0.dist-info/RECORD +0 -41
- {pydantic_fixturegen-1.0.0.dist-info → pydantic_fixturegen-1.1.0.dist-info}/WHEEL +0 -0
- {pydantic_fixturegen-1.0.0.dist-info → pydantic_fixturegen-1.1.0.dist-info}/entry_points.txt +0 -0
- {pydantic_fixturegen-1.0.0.dist-info → pydantic_fixturegen-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import datetime
|
|
5
6
|
import json
|
|
6
7
|
import re
|
|
7
8
|
import shutil
|
|
8
9
|
import subprocess
|
|
9
|
-
from collections.abc import Iterable, Sequence
|
|
10
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from pprint import pformat
|
|
@@ -14,8 +15,11 @@ from typing import Any, Literal, cast
|
|
|
14
15
|
|
|
15
16
|
from pydantic import BaseModel
|
|
16
17
|
|
|
18
|
+
from pydantic_fixturegen.core.constraint_report import ConstraintReporter
|
|
19
|
+
from pydantic_fixturegen.core.field_policies import FieldPolicy
|
|
17
20
|
from pydantic_fixturegen.core.generate import GenerationConfig, InstanceGenerator
|
|
18
21
|
from pydantic_fixturegen.core.io_utils import WriteResult, write_atomic_text
|
|
22
|
+
from pydantic_fixturegen.core.path_template import OutputTemplate, OutputTemplateContext
|
|
19
23
|
from pydantic_fixturegen.core.version import build_artifact_header
|
|
20
24
|
|
|
21
25
|
DEFAULT_SCOPE = "function"
|
|
@@ -36,6 +40,9 @@ class PytestEmitConfig:
|
|
|
36
40
|
optional_p_none: float | None = None
|
|
37
41
|
model_digest: str | None = None
|
|
38
42
|
hash_compare: bool = True
|
|
43
|
+
per_model_seeds: Mapping[str, int] | None = None
|
|
44
|
+
field_policies: tuple[FieldPolicy, ...] = ()
|
|
45
|
+
time_anchor: datetime.datetime | None = None
|
|
39
46
|
|
|
40
47
|
|
|
41
48
|
def emit_pytest_fixtures(
|
|
@@ -43,6 +50,8 @@ def emit_pytest_fixtures(
|
|
|
43
50
|
*,
|
|
44
51
|
output_path: str | Path,
|
|
45
52
|
config: PytestEmitConfig | None = None,
|
|
53
|
+
template: OutputTemplate | None = None,
|
|
54
|
+
template_context: OutputTemplateContext | None = None,
|
|
46
55
|
) -> WriteResult:
|
|
47
56
|
"""Generate pytest fixture code for ``models`` and write it atomically."""
|
|
48
57
|
|
|
@@ -59,21 +68,42 @@ def emit_pytest_fixtures(
|
|
|
59
68
|
if cfg.return_type not in {"model", "dict"}:
|
|
60
69
|
raise ValueError(f"Unsupported return_type: {cfg.return_type!r}")
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
def _build_generator(seed_value: int | None) -> InstanceGenerator:
|
|
72
|
+
generation_config = GenerationConfig(
|
|
73
|
+
seed=seed_value,
|
|
74
|
+
time_anchor=cfg.time_anchor,
|
|
75
|
+
field_policies=cfg.field_policies,
|
|
76
|
+
)
|
|
77
|
+
if cfg.optional_p_none is not None:
|
|
78
|
+
generation_config.optional_p_none = cfg.optional_p_none
|
|
79
|
+
return InstanceGenerator(config=generation_config)
|
|
80
|
+
|
|
81
|
+
shared_generator: InstanceGenerator | None = None
|
|
66
82
|
|
|
67
83
|
model_entries: list[_ModelEntry] = []
|
|
68
84
|
fixture_names: dict[str, int] = {}
|
|
69
85
|
helper_names: dict[str, int] = {}
|
|
70
86
|
|
|
87
|
+
per_model_seeds = cfg.per_model_seeds or {}
|
|
88
|
+
constraint_reporters: list[ConstraintReporter] = []
|
|
89
|
+
|
|
71
90
|
for model in models:
|
|
91
|
+
model_id = f"{model.__module__}.{model.__qualname__}"
|
|
92
|
+
if per_model_seeds:
|
|
93
|
+
seed_value = per_model_seeds.get(model_id, cfg.seed)
|
|
94
|
+
generator = _build_generator(seed_value)
|
|
95
|
+
else:
|
|
96
|
+
if shared_generator is None:
|
|
97
|
+
shared_generator = _build_generator(cfg.seed)
|
|
98
|
+
generator = shared_generator
|
|
99
|
+
|
|
72
100
|
instances = generator.generate(model, count=cfg.cases)
|
|
73
101
|
if len(instances) < cfg.cases:
|
|
74
102
|
raise RuntimeError(
|
|
75
103
|
f"Failed to generate {cfg.cases} instance(s) for {model.__qualname__}."
|
|
76
104
|
)
|
|
105
|
+
if per_model_seeds:
|
|
106
|
+
constraint_reporters.append(generator.constraint_report)
|
|
77
107
|
data = [_model_to_literal(instance) for instance in instances]
|
|
78
108
|
base_name = model.__name__
|
|
79
109
|
if cfg.style in {"factory", "class"}:
|
|
@@ -96,11 +126,32 @@ def emit_pytest_fixtures(
|
|
|
96
126
|
entries=model_entries,
|
|
97
127
|
config=cfg,
|
|
98
128
|
)
|
|
129
|
+
template_obj = template or OutputTemplate(output_path)
|
|
130
|
+
context = template_context or OutputTemplateContext()
|
|
131
|
+
resolved_path = template_obj.render(
|
|
132
|
+
context=context,
|
|
133
|
+
case_index=1 if template_obj.uses_case_index() else None,
|
|
134
|
+
)
|
|
99
135
|
result = write_atomic_text(
|
|
100
|
-
|
|
136
|
+
resolved_path,
|
|
101
137
|
rendered,
|
|
102
138
|
hash_compare=cfg.hash_compare,
|
|
103
139
|
)
|
|
140
|
+
aggregate_report = ConstraintReporter()
|
|
141
|
+
if shared_generator is not None and not per_model_seeds:
|
|
142
|
+
aggregate_report.merge_from(shared_generator.constraint_report)
|
|
143
|
+
else:
|
|
144
|
+
for reporter in constraint_reporters:
|
|
145
|
+
aggregate_report.merge_from(reporter)
|
|
146
|
+
|
|
147
|
+
if cfg.time_anchor is not None:
|
|
148
|
+
result.metadata = result.metadata or {}
|
|
149
|
+
result.metadata["time_anchor"] = cfg.time_anchor.isoformat()
|
|
150
|
+
|
|
151
|
+
summary = aggregate_report.summary()
|
|
152
|
+
if summary.get("models"):
|
|
153
|
+
result.metadata = result.metadata or {}
|
|
154
|
+
result.metadata["constraints"] = summary
|
|
104
155
|
return result
|
|
105
156
|
|
|
106
157
|
|
|
@@ -118,16 +169,20 @@ def _render_module(*, entries: Iterable[_ModelEntry], config: PytestEmitConfig)
|
|
|
118
169
|
models_metadata = ", ".join(
|
|
119
170
|
f"{entry.model.__module__}.{entry.model.__name__}" for entry in entries_list
|
|
120
171
|
)
|
|
172
|
+
header_extras = {
|
|
173
|
+
"style": config.style,
|
|
174
|
+
"scope": config.scope,
|
|
175
|
+
"return": config.return_type,
|
|
176
|
+
"cases": config.cases,
|
|
177
|
+
"models": models_metadata,
|
|
178
|
+
}
|
|
179
|
+
if config.time_anchor is not None:
|
|
180
|
+
header_extras["time_anchor"] = config.time_anchor.isoformat()
|
|
181
|
+
|
|
121
182
|
header = build_artifact_header(
|
|
122
183
|
seed=config.seed,
|
|
123
184
|
model_digest=config.model_digest,
|
|
124
|
-
extras=
|
|
125
|
-
"style": config.style,
|
|
126
|
-
"scope": config.scope,
|
|
127
|
-
"return": config.return_type,
|
|
128
|
-
"cases": config.cases,
|
|
129
|
-
"models": models_metadata,
|
|
130
|
-
},
|
|
185
|
+
extras=header_extras,
|
|
131
186
|
)
|
|
132
187
|
|
|
133
188
|
needs_any = config.return_type == "dict" or config.style in {"factory", "class"}
|
|
@@ -10,6 +10,8 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel
|
|
12
12
|
|
|
13
|
+
from pydantic_fixturegen.core.path_template import OutputTemplate, OutputTemplateContext
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
@dataclass(slots=True)
|
|
15
17
|
class SchemaEmitConfig:
|
|
@@ -24,11 +26,20 @@ def emit_model_schema(
|
|
|
24
26
|
output_path: str | Path,
|
|
25
27
|
indent: int | None = 2,
|
|
26
28
|
ensure_ascii: bool = False,
|
|
29
|
+
template: OutputTemplate | None = None,
|
|
30
|
+
template_context: OutputTemplateContext | None = None,
|
|
27
31
|
) -> Path:
|
|
28
32
|
"""Write the model JSON schema to ``output_path``."""
|
|
29
33
|
|
|
34
|
+
template_obj = template or OutputTemplate(output_path)
|
|
35
|
+
context = template_context or OutputTemplateContext()
|
|
36
|
+
resolved_path = template_obj.render(
|
|
37
|
+
context=context,
|
|
38
|
+
case_index=1 if template_obj.uses_case_index() else None,
|
|
39
|
+
)
|
|
40
|
+
|
|
30
41
|
config = SchemaEmitConfig(
|
|
31
|
-
output_path=
|
|
42
|
+
output_path=resolved_path,
|
|
32
43
|
indent=_normalise_indent(indent),
|
|
33
44
|
ensure_ascii=ensure_ascii,
|
|
34
45
|
)
|
|
@@ -40,6 +51,8 @@ def emit_model_schema(
|
|
|
40
51
|
sort_keys=True,
|
|
41
52
|
)
|
|
42
53
|
config.output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
if payload and not payload.endswith("\n"):
|
|
55
|
+
payload += "\n"
|
|
43
56
|
config.output_path.write_text(payload, encoding="utf-8")
|
|
44
57
|
return config.output_path
|
|
45
58
|
|
|
@@ -50,16 +63,25 @@ def emit_models_schema(
|
|
|
50
63
|
output_path: str | Path,
|
|
51
64
|
indent: int | None = 2,
|
|
52
65
|
ensure_ascii: bool = False,
|
|
66
|
+
template: OutputTemplate | None = None,
|
|
67
|
+
template_context: OutputTemplateContext | None = None,
|
|
53
68
|
) -> Path:
|
|
54
69
|
"""Emit a combined schema referencing each model by its qualified name."""
|
|
55
70
|
|
|
71
|
+
template_obj = template or OutputTemplate(output_path)
|
|
72
|
+
context = template_context or OutputTemplateContext()
|
|
73
|
+
resolved_path = template_obj.render(
|
|
74
|
+
context=context,
|
|
75
|
+
case_index=1 if template_obj.uses_case_index() else None,
|
|
76
|
+
)
|
|
77
|
+
|
|
56
78
|
config = SchemaEmitConfig(
|
|
57
|
-
output_path=
|
|
79
|
+
output_path=resolved_path,
|
|
58
80
|
indent=_normalise_indent(indent),
|
|
59
81
|
ensure_ascii=ensure_ascii,
|
|
60
82
|
)
|
|
61
83
|
combined: dict[str, Any] = {}
|
|
62
|
-
for model in models:
|
|
84
|
+
for model in sorted(models, key=lambda m: m.__name__):
|
|
63
85
|
combined[model.__name__] = model.model_json_schema()
|
|
64
86
|
|
|
65
87
|
payload = json.dumps(
|
|
@@ -69,6 +91,8 @@ def emit_models_schema(
|
|
|
69
91
|
sort_keys=True,
|
|
70
92
|
)
|
|
71
93
|
config.output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
if payload and not payload.endswith("\n"):
|
|
95
|
+
payload += "\n"
|
|
72
96
|
config.output_path.write_text(payload, encoding="utf-8")
|
|
73
97
|
return config.output_path
|
|
74
98
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Application-level logging helpers with CLI-friendly formatting."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Final
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
LOG_LEVEL_ORDER: Final[tuple[str, ...]] = ("silent", "error", "warn", "info", "debug")
|
|
14
|
+
LOG_LEVELS: Final[dict[str, int]] = {
|
|
15
|
+
"silent": 100,
|
|
16
|
+
"error": 40,
|
|
17
|
+
"warn": 30,
|
|
18
|
+
"info": 20,
|
|
19
|
+
"debug": 10,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Index in LOG_LEVEL_ORDER representing the default "info" verbosity tier
|
|
24
|
+
DEFAULT_VERBOSITY_INDEX: Final[int] = LOG_LEVEL_ORDER.index("info")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_COLOR_BY_LEVEL: Final[dict[str, str]] = {
|
|
28
|
+
"debug": typer.colors.BLUE,
|
|
29
|
+
"info": typer.colors.GREEN,
|
|
30
|
+
"warn": typer.colors.YELLOW,
|
|
31
|
+
"error": typer.colors.RED,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(slots=True)
|
|
36
|
+
class LoggerConfig:
|
|
37
|
+
level: int = LOG_LEVELS["info"]
|
|
38
|
+
json: bool = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Logger:
|
|
42
|
+
def __init__(self, config: LoggerConfig | None = None) -> None:
|
|
43
|
+
self.config = config or LoggerConfig()
|
|
44
|
+
|
|
45
|
+
def configure(self, *, level: str | None = None, json_mode: bool | None = None) -> None:
|
|
46
|
+
if level is not None:
|
|
47
|
+
canonical = level.lower()
|
|
48
|
+
if canonical not in LOG_LEVELS:
|
|
49
|
+
raise ValueError(f"Unknown log level: {level}")
|
|
50
|
+
self.config.level = LOG_LEVELS[canonical]
|
|
51
|
+
if json_mode is not None:
|
|
52
|
+
self.config.json = json_mode
|
|
53
|
+
|
|
54
|
+
def debug(self, message: str, **extras: Any) -> None:
|
|
55
|
+
self._emit("debug", message, **extras)
|
|
56
|
+
|
|
57
|
+
def info(self, message: str, **extras: Any) -> None:
|
|
58
|
+
self._emit("info", message, **extras)
|
|
59
|
+
|
|
60
|
+
def warn(self, message: str, **extras: Any) -> None:
|
|
61
|
+
self._emit("warn", message, **extras)
|
|
62
|
+
|
|
63
|
+
def error(self, message: str, **extras: Any) -> None:
|
|
64
|
+
self._emit("error", message, **extras)
|
|
65
|
+
|
|
66
|
+
def _emit(self, level_name: str, message: str, **extras: Any) -> None:
|
|
67
|
+
level_value = LOG_LEVELS.get(level_name)
|
|
68
|
+
if level_value is None:
|
|
69
|
+
raise ValueError(f"Unknown log level: {level_name}")
|
|
70
|
+
|
|
71
|
+
if level_value < self.config.level:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
payload_context = dict(extras)
|
|
75
|
+
event_name = payload_context.pop("event", message)
|
|
76
|
+
|
|
77
|
+
if self.config.json:
|
|
78
|
+
payload = {
|
|
79
|
+
"timestamp": datetime.now().isoformat(timespec="seconds"),
|
|
80
|
+
"level": level_name,
|
|
81
|
+
"event": event_name,
|
|
82
|
+
"message": message,
|
|
83
|
+
"context": payload_context or None,
|
|
84
|
+
}
|
|
85
|
+
sys.stdout.write(json.dumps(payload, sort_keys=True) + "\n")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
stream_to_err = level_name in {"warn", "error"}
|
|
89
|
+
color = _COLOR_BY_LEVEL.get(level_name, typer.colors.WHITE)
|
|
90
|
+
|
|
91
|
+
typer.secho(message, fg=color, err=stream_to_err)
|
|
92
|
+
if payload_context and self.config.level <= LOG_LEVELS["debug"]:
|
|
93
|
+
typer.secho(
|
|
94
|
+
json.dumps(payload_context, sort_keys=True, indent=2),
|
|
95
|
+
fg=_COLOR_BY_LEVEL["debug"],
|
|
96
|
+
err=stream_to_err,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_GLOBAL_LOGGER = Logger()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_logger() -> Logger:
|
|
104
|
+
return _GLOBAL_LOGGER
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
__all__ = [
|
|
108
|
+
"Logger",
|
|
109
|
+
"LoggerConfig",
|
|
110
|
+
"LOG_LEVELS",
|
|
111
|
+
"LOG_LEVEL_ORDER",
|
|
112
|
+
"DEFAULT_VERBOSITY_INDEX",
|
|
113
|
+
"get_logger",
|
|
114
|
+
]
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$defs": {
|
|
3
|
+
"EmittersSchema": {
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"description": "Schema for emitter configuration sections.",
|
|
6
|
+
"properties": {
|
|
7
|
+
"pytest": {
|
|
8
|
+
"$ref": "#/$defs/PytestEmitterSchema",
|
|
9
|
+
"description": "Configuration for the built-in pytest fixture emitter."
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"title": "EmittersSchema",
|
|
13
|
+
"type": "object"
|
|
14
|
+
},
|
|
15
|
+
"FieldPolicyOptionsSchema": {
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"description": "Schema describing supported field policy overrides.",
|
|
18
|
+
"properties": {
|
|
19
|
+
"enum_policy": {
|
|
20
|
+
"anyOf": [
|
|
21
|
+
{
|
|
22
|
+
"enum": [
|
|
23
|
+
"first",
|
|
24
|
+
"random"
|
|
25
|
+
],
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "null"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"default": null,
|
|
33
|
+
"description": "Enum selection policy for matching fields.",
|
|
34
|
+
"title": "Enum Policy"
|
|
35
|
+
},
|
|
36
|
+
"p_none": {
|
|
37
|
+
"anyOf": [
|
|
38
|
+
{
|
|
39
|
+
"maximum": 1.0,
|
|
40
|
+
"minimum": 0.0,
|
|
41
|
+
"type": "number"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "null"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"default": null,
|
|
48
|
+
"description": "Probability override for returning None on optional fields.",
|
|
49
|
+
"title": "P None"
|
|
50
|
+
},
|
|
51
|
+
"union_policy": {
|
|
52
|
+
"anyOf": [
|
|
53
|
+
{
|
|
54
|
+
"enum": [
|
|
55
|
+
"first",
|
|
56
|
+
"random"
|
|
57
|
+
],
|
|
58
|
+
"type": "string"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "null"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"default": null,
|
|
65
|
+
"description": "Union selection policy for matching fields.",
|
|
66
|
+
"title": "Union Policy"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"title": "FieldPolicyOptionsSchema",
|
|
70
|
+
"type": "object"
|
|
71
|
+
},
|
|
72
|
+
"JsonSchema": {
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"description": "Schema for JSON emitter settings.",
|
|
75
|
+
"properties": {
|
|
76
|
+
"indent": {
|
|
77
|
+
"default": 2,
|
|
78
|
+
"description": "Indentation level for JSON output (0 for compact).",
|
|
79
|
+
"minimum": 0,
|
|
80
|
+
"title": "Indent",
|
|
81
|
+
"type": "integer"
|
|
82
|
+
},
|
|
83
|
+
"orjson": {
|
|
84
|
+
"default": false,
|
|
85
|
+
"description": "Use orjson for serialization when available.",
|
|
86
|
+
"title": "Orjson",
|
|
87
|
+
"type": "boolean"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"title": "JsonSchema",
|
|
91
|
+
"type": "object"
|
|
92
|
+
},
|
|
93
|
+
"PytestEmitterSchema": {
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"description": "Schema for pytest emitter settings.",
|
|
96
|
+
"properties": {
|
|
97
|
+
"scope": {
|
|
98
|
+
"default": "function",
|
|
99
|
+
"description": "Default pytest fixture scope.",
|
|
100
|
+
"enum": [
|
|
101
|
+
"function",
|
|
102
|
+
"module",
|
|
103
|
+
"session"
|
|
104
|
+
],
|
|
105
|
+
"title": "Scope",
|
|
106
|
+
"type": "string"
|
|
107
|
+
},
|
|
108
|
+
"style": {
|
|
109
|
+
"default": "functions",
|
|
110
|
+
"description": "How emitted pytest fixtures are structured.",
|
|
111
|
+
"enum": [
|
|
112
|
+
"functions",
|
|
113
|
+
"factory",
|
|
114
|
+
"class"
|
|
115
|
+
],
|
|
116
|
+
"title": "Style",
|
|
117
|
+
"type": "string"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"title": "PytestEmitterSchema",
|
|
121
|
+
"type": "object"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"$id": "https://raw.githubusercontent.com/CasperKristiansson/pydantic-fixturegen/main/pydantic_fixturegen/schemas/config.schema.json",
|
|
125
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
126
|
+
"additionalProperties": true,
|
|
127
|
+
"description": "Authoritative schema for `[tool.pydantic_fixturegen]` configuration.",
|
|
128
|
+
"properties": {
|
|
129
|
+
"emitters": {
|
|
130
|
+
"$ref": "#/$defs/EmittersSchema",
|
|
131
|
+
"description": "Emitter-specific configuration sections."
|
|
132
|
+
},
|
|
133
|
+
"enum_policy": {
|
|
134
|
+
"default": "first",
|
|
135
|
+
"description": "Strategy for selecting enum members.",
|
|
136
|
+
"enum": [
|
|
137
|
+
"first",
|
|
138
|
+
"random"
|
|
139
|
+
],
|
|
140
|
+
"title": "Enum Policy",
|
|
141
|
+
"type": "string"
|
|
142
|
+
},
|
|
143
|
+
"exclude": {
|
|
144
|
+
"description": "Glob patterns of fully-qualified model names to exclude by default.",
|
|
145
|
+
"items": {
|
|
146
|
+
"type": "string"
|
|
147
|
+
},
|
|
148
|
+
"title": "Exclude",
|
|
149
|
+
"type": "array"
|
|
150
|
+
},
|
|
151
|
+
"field_policies": {
|
|
152
|
+
"additionalProperties": {
|
|
153
|
+
"$ref": "#/$defs/FieldPolicyOptionsSchema"
|
|
154
|
+
},
|
|
155
|
+
"description": "Field policy definitions keyed by glob or regex patterns that may target model names or dotted field paths.",
|
|
156
|
+
"title": "Field Policies",
|
|
157
|
+
"type": "object"
|
|
158
|
+
},
|
|
159
|
+
"include": {
|
|
160
|
+
"description": "Glob patterns of fully-qualified model names to include by default.",
|
|
161
|
+
"items": {
|
|
162
|
+
"type": "string"
|
|
163
|
+
},
|
|
164
|
+
"title": "Include",
|
|
165
|
+
"type": "array"
|
|
166
|
+
},
|
|
167
|
+
"json": {
|
|
168
|
+
"$ref": "#/$defs/JsonSchema",
|
|
169
|
+
"description": "Settings shared by JSON-based emitters."
|
|
170
|
+
},
|
|
171
|
+
"locale": {
|
|
172
|
+
"default": "en_US",
|
|
173
|
+
"description": "Default Faker locale used when generating data.",
|
|
174
|
+
"title": "Locale",
|
|
175
|
+
"type": "string"
|
|
176
|
+
},
|
|
177
|
+
"overrides": {
|
|
178
|
+
"additionalProperties": {
|
|
179
|
+
"additionalProperties": true,
|
|
180
|
+
"type": "object"
|
|
181
|
+
},
|
|
182
|
+
"description": "Per-model overrides keyed by fully-qualified model name.",
|
|
183
|
+
"title": "Overrides",
|
|
184
|
+
"type": "object"
|
|
185
|
+
},
|
|
186
|
+
"p_none": {
|
|
187
|
+
"anyOf": [
|
|
188
|
+
{
|
|
189
|
+
"maximum": 1.0,
|
|
190
|
+
"minimum": 0.0,
|
|
191
|
+
"type": "number"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"type": "null"
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"default": null,
|
|
198
|
+
"description": "Probability of sampling `None` for optional fields when unspecified.",
|
|
199
|
+
"title": "P None"
|
|
200
|
+
},
|
|
201
|
+
"preset": {
|
|
202
|
+
"anyOf": [
|
|
203
|
+
{
|
|
204
|
+
"type": "string"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"type": "null"
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"default": null,
|
|
211
|
+
"description": "Curated preset name applied before other configuration (e.g., 'boundary').",
|
|
212
|
+
"title": "Preset"
|
|
213
|
+
},
|
|
214
|
+
"seed": {
|
|
215
|
+
"anyOf": [
|
|
216
|
+
{
|
|
217
|
+
"type": "integer"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"type": "string"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"type": "null"
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
"default": null,
|
|
227
|
+
"description": "Global seed controlling deterministic generation. Accepts int or string.",
|
|
228
|
+
"title": "Seed"
|
|
229
|
+
},
|
|
230
|
+
"union_policy": {
|
|
231
|
+
"default": "first",
|
|
232
|
+
"description": "Strategy for selecting branches of `typing.Union`.",
|
|
233
|
+
"enum": [
|
|
234
|
+
"first",
|
|
235
|
+
"random",
|
|
236
|
+
"weighted"
|
|
237
|
+
],
|
|
238
|
+
"title": "Union Policy",
|
|
239
|
+
"type": "string"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"title": "pydantic-fixturegen configuration",
|
|
243
|
+
"type": "object"
|
|
244
|
+
}
|