schemathesis 3.13.0__py3-none-any.whl → 4.4.2__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.
Files changed (245) hide show
  1. schemathesis/__init__.py +53 -25
  2. schemathesis/auths.py +507 -0
  3. schemathesis/checks.py +190 -25
  4. schemathesis/cli/__init__.py +27 -1016
  5. schemathesis/cli/__main__.py +4 -0
  6. schemathesis/cli/commands/__init__.py +133 -0
  7. schemathesis/cli/commands/data.py +10 -0
  8. schemathesis/cli/commands/run/__init__.py +602 -0
  9. schemathesis/cli/commands/run/context.py +228 -0
  10. schemathesis/cli/commands/run/events.py +60 -0
  11. schemathesis/cli/commands/run/executor.py +157 -0
  12. schemathesis/cli/commands/run/filters.py +53 -0
  13. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  14. schemathesis/cli/commands/run/handlers/base.py +45 -0
  15. schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
  16. schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
  17. schemathesis/cli/commands/run/handlers/output.py +1750 -0
  18. schemathesis/cli/commands/run/loaders.py +118 -0
  19. schemathesis/cli/commands/run/validation.py +256 -0
  20. schemathesis/cli/constants.py +5 -0
  21. schemathesis/cli/core.py +19 -0
  22. schemathesis/cli/ext/fs.py +16 -0
  23. schemathesis/cli/ext/groups.py +203 -0
  24. schemathesis/cli/ext/options.py +81 -0
  25. schemathesis/config/__init__.py +202 -0
  26. schemathesis/config/_auth.py +51 -0
  27. schemathesis/config/_checks.py +268 -0
  28. schemathesis/config/_diff_base.py +101 -0
  29. schemathesis/config/_env.py +21 -0
  30. schemathesis/config/_error.py +163 -0
  31. schemathesis/config/_generation.py +157 -0
  32. schemathesis/config/_health_check.py +24 -0
  33. schemathesis/config/_operations.py +335 -0
  34. schemathesis/config/_output.py +171 -0
  35. schemathesis/config/_parameters.py +19 -0
  36. schemathesis/config/_phases.py +253 -0
  37. schemathesis/config/_projects.py +543 -0
  38. schemathesis/config/_rate_limit.py +17 -0
  39. schemathesis/config/_report.py +120 -0
  40. schemathesis/config/_validator.py +9 -0
  41. schemathesis/config/_warnings.py +89 -0
  42. schemathesis/config/schema.json +975 -0
  43. schemathesis/core/__init__.py +72 -0
  44. schemathesis/core/adapter.py +34 -0
  45. schemathesis/core/compat.py +32 -0
  46. schemathesis/core/control.py +2 -0
  47. schemathesis/core/curl.py +100 -0
  48. schemathesis/core/deserialization.py +210 -0
  49. schemathesis/core/errors.py +588 -0
  50. schemathesis/core/failures.py +316 -0
  51. schemathesis/core/fs.py +19 -0
  52. schemathesis/core/hooks.py +20 -0
  53. schemathesis/core/jsonschema/__init__.py +13 -0
  54. schemathesis/core/jsonschema/bundler.py +183 -0
  55. schemathesis/core/jsonschema/keywords.py +40 -0
  56. schemathesis/core/jsonschema/references.py +222 -0
  57. schemathesis/core/jsonschema/types.py +41 -0
  58. schemathesis/core/lazy_import.py +15 -0
  59. schemathesis/core/loaders.py +107 -0
  60. schemathesis/core/marks.py +66 -0
  61. schemathesis/core/media_types.py +79 -0
  62. schemathesis/core/output/__init__.py +46 -0
  63. schemathesis/core/output/sanitization.py +54 -0
  64. schemathesis/core/parameters.py +45 -0
  65. schemathesis/core/rate_limit.py +60 -0
  66. schemathesis/core/registries.py +34 -0
  67. schemathesis/core/result.py +27 -0
  68. schemathesis/core/schema_analysis.py +17 -0
  69. schemathesis/core/shell.py +203 -0
  70. schemathesis/core/transforms.py +144 -0
  71. schemathesis/core/transport.py +223 -0
  72. schemathesis/core/validation.py +73 -0
  73. schemathesis/core/version.py +7 -0
  74. schemathesis/engine/__init__.py +28 -0
  75. schemathesis/engine/context.py +152 -0
  76. schemathesis/engine/control.py +44 -0
  77. schemathesis/engine/core.py +201 -0
  78. schemathesis/engine/errors.py +446 -0
  79. schemathesis/engine/events.py +284 -0
  80. schemathesis/engine/observations.py +42 -0
  81. schemathesis/engine/phases/__init__.py +108 -0
  82. schemathesis/engine/phases/analysis.py +28 -0
  83. schemathesis/engine/phases/probes.py +172 -0
  84. schemathesis/engine/phases/stateful/__init__.py +68 -0
  85. schemathesis/engine/phases/stateful/_executor.py +364 -0
  86. schemathesis/engine/phases/stateful/context.py +85 -0
  87. schemathesis/engine/phases/unit/__init__.py +220 -0
  88. schemathesis/engine/phases/unit/_executor.py +459 -0
  89. schemathesis/engine/phases/unit/_pool.py +82 -0
  90. schemathesis/engine/recorder.py +254 -0
  91. schemathesis/errors.py +47 -0
  92. schemathesis/filters.py +395 -0
  93. schemathesis/generation/__init__.py +25 -0
  94. schemathesis/generation/case.py +478 -0
  95. schemathesis/generation/coverage.py +1528 -0
  96. schemathesis/generation/hypothesis/__init__.py +121 -0
  97. schemathesis/generation/hypothesis/builder.py +992 -0
  98. schemathesis/generation/hypothesis/examples.py +56 -0
  99. schemathesis/generation/hypothesis/given.py +66 -0
  100. schemathesis/generation/hypothesis/reporting.py +285 -0
  101. schemathesis/generation/meta.py +227 -0
  102. schemathesis/generation/metrics.py +93 -0
  103. schemathesis/generation/modes.py +20 -0
  104. schemathesis/generation/overrides.py +127 -0
  105. schemathesis/generation/stateful/__init__.py +37 -0
  106. schemathesis/generation/stateful/state_machine.py +294 -0
  107. schemathesis/graphql/__init__.py +15 -0
  108. schemathesis/graphql/checks.py +109 -0
  109. schemathesis/graphql/loaders.py +285 -0
  110. schemathesis/hooks.py +270 -91
  111. schemathesis/openapi/__init__.py +13 -0
  112. schemathesis/openapi/checks.py +467 -0
  113. schemathesis/openapi/generation/__init__.py +0 -0
  114. schemathesis/openapi/generation/filters.py +72 -0
  115. schemathesis/openapi/loaders.py +315 -0
  116. schemathesis/pytest/__init__.py +5 -0
  117. schemathesis/pytest/control_flow.py +7 -0
  118. schemathesis/pytest/lazy.py +341 -0
  119. schemathesis/pytest/loaders.py +36 -0
  120. schemathesis/pytest/plugin.py +357 -0
  121. schemathesis/python/__init__.py +0 -0
  122. schemathesis/python/asgi.py +12 -0
  123. schemathesis/python/wsgi.py +12 -0
  124. schemathesis/schemas.py +683 -247
  125. schemathesis/specs/graphql/__init__.py +0 -1
  126. schemathesis/specs/graphql/nodes.py +27 -0
  127. schemathesis/specs/graphql/scalars.py +86 -0
  128. schemathesis/specs/graphql/schemas.py +395 -123
  129. schemathesis/specs/graphql/validation.py +33 -0
  130. schemathesis/specs/openapi/__init__.py +9 -1
  131. schemathesis/specs/openapi/_hypothesis.py +578 -317
  132. schemathesis/specs/openapi/adapter/__init__.py +10 -0
  133. schemathesis/specs/openapi/adapter/parameters.py +729 -0
  134. schemathesis/specs/openapi/adapter/protocol.py +59 -0
  135. schemathesis/specs/openapi/adapter/references.py +19 -0
  136. schemathesis/specs/openapi/adapter/responses.py +368 -0
  137. schemathesis/specs/openapi/adapter/security.py +144 -0
  138. schemathesis/specs/openapi/adapter/v2.py +30 -0
  139. schemathesis/specs/openapi/adapter/v3_0.py +30 -0
  140. schemathesis/specs/openapi/adapter/v3_1.py +30 -0
  141. schemathesis/specs/openapi/analysis.py +96 -0
  142. schemathesis/specs/openapi/checks.py +753 -74
  143. schemathesis/specs/openapi/converter.py +176 -37
  144. schemathesis/specs/openapi/definitions.py +599 -4
  145. schemathesis/specs/openapi/examples.py +581 -165
  146. schemathesis/specs/openapi/expressions/__init__.py +52 -5
  147. schemathesis/specs/openapi/expressions/extractors.py +25 -0
  148. schemathesis/specs/openapi/expressions/lexer.py +34 -31
  149. schemathesis/specs/openapi/expressions/nodes.py +97 -46
  150. schemathesis/specs/openapi/expressions/parser.py +35 -13
  151. schemathesis/specs/openapi/formats.py +122 -0
  152. schemathesis/specs/openapi/media_types.py +75 -0
  153. schemathesis/specs/openapi/negative/__init__.py +117 -68
  154. schemathesis/specs/openapi/negative/mutations.py +294 -104
  155. schemathesis/specs/openapi/negative/utils.py +3 -6
  156. schemathesis/specs/openapi/patterns.py +458 -0
  157. schemathesis/specs/openapi/references.py +60 -81
  158. schemathesis/specs/openapi/schemas.py +648 -650
  159. schemathesis/specs/openapi/serialization.py +53 -30
  160. schemathesis/specs/openapi/stateful/__init__.py +404 -69
  161. schemathesis/specs/openapi/stateful/control.py +87 -0
  162. schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
  163. schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
  164. schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
  165. schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
  166. schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
  167. schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
  168. schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
  169. schemathesis/specs/openapi/stateful/inference.py +254 -0
  170. schemathesis/specs/openapi/stateful/links.py +219 -78
  171. schemathesis/specs/openapi/types/__init__.py +3 -0
  172. schemathesis/specs/openapi/types/common.py +23 -0
  173. schemathesis/specs/openapi/types/v2.py +129 -0
  174. schemathesis/specs/openapi/types/v3.py +134 -0
  175. schemathesis/specs/openapi/utils.py +7 -6
  176. schemathesis/specs/openapi/warnings.py +75 -0
  177. schemathesis/transport/__init__.py +224 -0
  178. schemathesis/transport/asgi.py +26 -0
  179. schemathesis/transport/prepare.py +126 -0
  180. schemathesis/transport/requests.py +278 -0
  181. schemathesis/transport/serialization.py +329 -0
  182. schemathesis/transport/wsgi.py +175 -0
  183. schemathesis-4.4.2.dist-info/METADATA +213 -0
  184. schemathesis-4.4.2.dist-info/RECORD +192 -0
  185. {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
  186. schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
  187. {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
  188. schemathesis/_compat.py +0 -41
  189. schemathesis/_hypothesis.py +0 -115
  190. schemathesis/cli/callbacks.py +0 -188
  191. schemathesis/cli/cassettes.py +0 -253
  192. schemathesis/cli/context.py +0 -36
  193. schemathesis/cli/debug.py +0 -21
  194. schemathesis/cli/handlers.py +0 -11
  195. schemathesis/cli/junitxml.py +0 -41
  196. schemathesis/cli/options.py +0 -51
  197. schemathesis/cli/output/__init__.py +0 -1
  198. schemathesis/cli/output/default.py +0 -508
  199. schemathesis/cli/output/short.py +0 -40
  200. schemathesis/constants.py +0 -79
  201. schemathesis/exceptions.py +0 -207
  202. schemathesis/extra/_aiohttp.py +0 -27
  203. schemathesis/extra/_flask.py +0 -10
  204. schemathesis/extra/_server.py +0 -16
  205. schemathesis/extra/pytest_plugin.py +0 -216
  206. schemathesis/failures.py +0 -131
  207. schemathesis/fixups/__init__.py +0 -29
  208. schemathesis/fixups/fast_api.py +0 -30
  209. schemathesis/lazy.py +0 -227
  210. schemathesis/models.py +0 -1041
  211. schemathesis/parameters.py +0 -88
  212. schemathesis/runner/__init__.py +0 -460
  213. schemathesis/runner/events.py +0 -240
  214. schemathesis/runner/impl/__init__.py +0 -3
  215. schemathesis/runner/impl/core.py +0 -755
  216. schemathesis/runner/impl/solo.py +0 -85
  217. schemathesis/runner/impl/threadpool.py +0 -367
  218. schemathesis/runner/serialization.py +0 -189
  219. schemathesis/serializers.py +0 -233
  220. schemathesis/service/__init__.py +0 -3
  221. schemathesis/service/client.py +0 -46
  222. schemathesis/service/constants.py +0 -12
  223. schemathesis/service/events.py +0 -39
  224. schemathesis/service/handler.py +0 -39
  225. schemathesis/service/models.py +0 -7
  226. schemathesis/service/serialization.py +0 -153
  227. schemathesis/service/worker.py +0 -40
  228. schemathesis/specs/graphql/loaders.py +0 -215
  229. schemathesis/specs/openapi/constants.py +0 -7
  230. schemathesis/specs/openapi/expressions/context.py +0 -12
  231. schemathesis/specs/openapi/expressions/pointers.py +0 -29
  232. schemathesis/specs/openapi/filters.py +0 -44
  233. schemathesis/specs/openapi/links.py +0 -302
  234. schemathesis/specs/openapi/loaders.py +0 -453
  235. schemathesis/specs/openapi/parameters.py +0 -413
  236. schemathesis/specs/openapi/security.py +0 -129
  237. schemathesis/specs/openapi/validation.py +0 -24
  238. schemathesis/stateful.py +0 -349
  239. schemathesis/targets.py +0 -32
  240. schemathesis/types.py +0 -38
  241. schemathesis/utils.py +0 -436
  242. schemathesis-3.13.0.dist-info/METADATA +0 -202
  243. schemathesis-3.13.0.dist-info/RECORD +0 -91
  244. schemathesis-3.13.0.dist-info/entry_points.txt +0 -6
  245. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
@@ -0,0 +1,253 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Any
6
+
7
+ from schemathesis.config._checks import ChecksConfig
8
+ from schemathesis.config._diff_base import DiffBase
9
+ from schemathesis.config._generation import GenerationConfig
10
+ from schemathesis.core import DEFAULT_STATEFUL_STEP_COUNT
11
+
12
+ DEFAULT_UNEXPECTED_METHODS = {"get", "put", "post", "delete", "options", "patch", "trace"}
13
+
14
+
15
+ @dataclass(repr=False)
16
+ class PhaseConfig(DiffBase):
17
+ enabled: bool
18
+ generation: GenerationConfig
19
+ checks: ChecksConfig
20
+
21
+ __slots__ = ("enabled", "generation", "checks", "_is_default")
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ enabled: bool = True,
27
+ generation: GenerationConfig | None = None,
28
+ checks: ChecksConfig | None = None,
29
+ ) -> None:
30
+ self.enabled = enabled
31
+ self.generation = generation or GenerationConfig()
32
+ self.checks = checks or ChecksConfig()
33
+ self._is_default = enabled and generation is None and checks is None
34
+
35
+ @classmethod
36
+ def from_dict(cls, data: dict[str, Any]) -> PhaseConfig:
37
+ return cls(
38
+ enabled=data.get("enabled", True),
39
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
40
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
41
+ )
42
+
43
+
44
+ @dataclass(repr=False)
45
+ class ExamplesPhaseConfig(DiffBase):
46
+ enabled: bool
47
+ fill_missing: bool
48
+ generation: GenerationConfig
49
+ checks: ChecksConfig
50
+
51
+ __slots__ = ("enabled", "fill_missing", "generation", "checks", "_is_default")
52
+
53
+ def __init__(
54
+ self,
55
+ *,
56
+ enabled: bool = True,
57
+ fill_missing: bool = False,
58
+ generation: GenerationConfig | None = None,
59
+ checks: ChecksConfig | None = None,
60
+ ) -> None:
61
+ self.enabled = enabled
62
+ self.fill_missing = fill_missing
63
+ self.generation = generation or GenerationConfig()
64
+ self.checks = checks or ChecksConfig()
65
+ self._is_default = enabled and not fill_missing and generation is None and checks is None
66
+
67
+ @classmethod
68
+ def from_dict(cls, data: dict[str, Any]) -> ExamplesPhaseConfig:
69
+ return cls(
70
+ enabled=data.get("enabled", True),
71
+ fill_missing=data.get("fill-missing", False),
72
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
73
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
74
+ )
75
+
76
+
77
+ @dataclass(repr=False)
78
+ class CoveragePhaseConfig(DiffBase):
79
+ enabled: bool
80
+ generate_duplicate_query_parameters: bool
81
+ generation: GenerationConfig
82
+ checks: ChecksConfig
83
+ unexpected_methods: set[str]
84
+
85
+ __slots__ = (
86
+ "enabled",
87
+ "generate_duplicate_query_parameters",
88
+ "generation",
89
+ "checks",
90
+ "unexpected_methods",
91
+ "_is_default",
92
+ )
93
+
94
+ def __init__(
95
+ self,
96
+ *,
97
+ enabled: bool = True,
98
+ generate_duplicate_query_parameters: bool = False,
99
+ generation: GenerationConfig | None = None,
100
+ checks: ChecksConfig | None = None,
101
+ unexpected_methods: set[str] | None = None,
102
+ ) -> None:
103
+ self.enabled = enabled
104
+ self.generate_duplicate_query_parameters = generate_duplicate_query_parameters
105
+ self.unexpected_methods = unexpected_methods if unexpected_methods is not None else DEFAULT_UNEXPECTED_METHODS
106
+ self.generation = generation or GenerationConfig()
107
+ self.checks = checks or ChecksConfig()
108
+ self._is_default = (
109
+ enabled
110
+ and not generate_duplicate_query_parameters
111
+ and generation is None
112
+ and checks is None
113
+ and unexpected_methods is None
114
+ )
115
+
116
+ @classmethod
117
+ def from_dict(cls, data: dict[str, Any]) -> CoveragePhaseConfig:
118
+ return cls(
119
+ enabled=data.get("enabled", True),
120
+ generate_duplicate_query_parameters=data.get("generate-duplicate-query-parameters", False),
121
+ unexpected_methods={method.lower() for method in data.get("unexpected-methods", [])}
122
+ if "unexpected-methods" in data
123
+ else None,
124
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
125
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
126
+ )
127
+
128
+
129
+ class InferenceAlgorithm(str, Enum):
130
+ LOCATION_HEADERS = "location-headers"
131
+ DEPENDENCY_ANALYSIS = "dependency-analysis"
132
+
133
+
134
+ @dataclass(repr=False)
135
+ class InferenceConfig(DiffBase):
136
+ algorithms: list[InferenceAlgorithm]
137
+
138
+ __slots__ = ("algorithms",)
139
+
140
+ def __init__(
141
+ self,
142
+ *,
143
+ algorithms: list[str] | None = None,
144
+ ) -> None:
145
+ self.algorithms = (
146
+ [InferenceAlgorithm(a) for a in algorithms] if algorithms is not None else list(InferenceAlgorithm)
147
+ )
148
+
149
+ @classmethod
150
+ def from_dict(cls, data: dict[str, Any]) -> InferenceConfig:
151
+ return cls(
152
+ algorithms=data.get("algorithms", list(InferenceAlgorithm)),
153
+ )
154
+
155
+ @property
156
+ def is_enabled(self) -> bool:
157
+ """Inference is enabled if any algorithms are configured."""
158
+ return bool(self.algorithms)
159
+
160
+ def is_algorithm_enabled(self, algorithm: InferenceAlgorithm) -> bool:
161
+ return algorithm in self.algorithms
162
+
163
+
164
+ @dataclass(repr=False)
165
+ class StatefulPhaseConfig(DiffBase):
166
+ enabled: bool
167
+ generation: GenerationConfig
168
+ checks: ChecksConfig
169
+ max_steps: int
170
+ inference: InferenceConfig
171
+
172
+ __slots__ = ("enabled", "generation", "checks", "max_steps", "inference", "_is_default")
173
+
174
+ def __init__(
175
+ self,
176
+ *,
177
+ enabled: bool = True,
178
+ generation: GenerationConfig | None = None,
179
+ checks: ChecksConfig | None = None,
180
+ max_steps: int | None = None,
181
+ inference: InferenceConfig | None = None,
182
+ ) -> None:
183
+ self.enabled = enabled
184
+ self.max_steps = max_steps or DEFAULT_STATEFUL_STEP_COUNT
185
+ self.generation = generation or GenerationConfig()
186
+ self.checks = checks or ChecksConfig()
187
+ self.inference = inference or InferenceConfig()
188
+ self._is_default = enabled and generation is None and checks is None and max_steps is None and inference is None
189
+
190
+ @classmethod
191
+ def from_dict(cls, data: dict[str, Any]) -> StatefulPhaseConfig:
192
+ return cls(
193
+ enabled=data.get("enabled", True),
194
+ max_steps=data.get("max-steps"),
195
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
196
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
197
+ inference=InferenceConfig.from_dict(data.get("inference", {})),
198
+ )
199
+
200
+
201
+ @dataclass(repr=False)
202
+ class PhasesConfig(DiffBase):
203
+ examples: ExamplesPhaseConfig
204
+ coverage: CoveragePhaseConfig
205
+ fuzzing: PhaseConfig
206
+ stateful: StatefulPhaseConfig
207
+
208
+ __slots__ = ("examples", "coverage", "fuzzing", "stateful")
209
+
210
+ def __init__(
211
+ self,
212
+ *,
213
+ examples: ExamplesPhaseConfig | None = None,
214
+ coverage: CoveragePhaseConfig | None = None,
215
+ fuzzing: PhaseConfig | None = None,
216
+ stateful: StatefulPhaseConfig | None = None,
217
+ ) -> None:
218
+ self.examples = examples or ExamplesPhaseConfig()
219
+ self.coverage = coverage or CoveragePhaseConfig()
220
+ self.fuzzing = fuzzing or PhaseConfig()
221
+ self.stateful = stateful or StatefulPhaseConfig()
222
+
223
+ def get_by_name(self, *, name: str) -> PhaseConfig | CoveragePhaseConfig | StatefulPhaseConfig:
224
+ return {
225
+ "examples": self.examples,
226
+ "coverage": self.coverage,
227
+ "fuzzing": self.fuzzing,
228
+ "stateful": self.stateful,
229
+ }[name] # type: ignore[return-value]
230
+
231
+ @classmethod
232
+ def from_dict(cls, data: dict[str, Any]) -> PhasesConfig:
233
+ # Use the outer "enabled" value as default for all phases.
234
+ default_enabled = data.get("enabled", None)
235
+
236
+ def merge(sub: dict[str, Any]) -> dict[str, Any]:
237
+ # Merge the default enabled flag with the sub-dict; the sub-dict takes precedence.
238
+ if default_enabled is not None:
239
+ return {"enabled": default_enabled, **sub}
240
+ return sub
241
+
242
+ return cls(
243
+ examples=ExamplesPhaseConfig.from_dict(merge(data.get("examples", {}))),
244
+ coverage=CoveragePhaseConfig.from_dict(merge(data.get("coverage", {}))),
245
+ fuzzing=PhaseConfig.from_dict(merge(data.get("fuzzing", {}))),
246
+ stateful=StatefulPhaseConfig.from_dict(merge(data.get("stateful", {}))),
247
+ )
248
+
249
+ def update(self, *, phases: list[str]) -> None:
250
+ self.examples.enabled = "examples" in phases
251
+ self.coverage.enabled = "coverage" in phases
252
+ self.fuzzing.enabled = "fuzzing" in phases
253
+ self.stateful.enabled = "stateful" in phases