schemathesis 3.39.16__py3-none-any.whl → 4.0.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.
Files changed (255) hide show
  1. schemathesis/__init__.py +41 -79
  2. schemathesis/auths.py +111 -122
  3. schemathesis/checks.py +169 -60
  4. schemathesis/cli/__init__.py +15 -2117
  5. schemathesis/cli/commands/__init__.py +85 -0
  6. schemathesis/cli/commands/data.py +10 -0
  7. schemathesis/cli/commands/run/__init__.py +590 -0
  8. schemathesis/cli/commands/run/context.py +204 -0
  9. schemathesis/cli/commands/run/events.py +60 -0
  10. schemathesis/cli/commands/run/executor.py +157 -0
  11. schemathesis/cli/commands/run/filters.py +53 -0
  12. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  13. schemathesis/cli/commands/run/handlers/base.py +18 -0
  14. schemathesis/cli/commands/run/handlers/cassettes.py +474 -0
  15. schemathesis/cli/commands/run/handlers/junitxml.py +55 -0
  16. schemathesis/cli/commands/run/handlers/output.py +1628 -0
  17. schemathesis/cli/commands/run/loaders.py +114 -0
  18. schemathesis/cli/commands/run/validation.py +246 -0
  19. schemathesis/cli/constants.py +5 -58
  20. schemathesis/cli/core.py +19 -0
  21. schemathesis/cli/ext/fs.py +16 -0
  22. schemathesis/cli/ext/groups.py +84 -0
  23. schemathesis/cli/{options.py → ext/options.py} +36 -34
  24. schemathesis/config/__init__.py +189 -0
  25. schemathesis/config/_auth.py +51 -0
  26. schemathesis/config/_checks.py +268 -0
  27. schemathesis/config/_diff_base.py +99 -0
  28. schemathesis/config/_env.py +21 -0
  29. schemathesis/config/_error.py +156 -0
  30. schemathesis/config/_generation.py +149 -0
  31. schemathesis/config/_health_check.py +24 -0
  32. schemathesis/config/_operations.py +327 -0
  33. schemathesis/config/_output.py +171 -0
  34. schemathesis/config/_parameters.py +19 -0
  35. schemathesis/config/_phases.py +187 -0
  36. schemathesis/config/_projects.py +527 -0
  37. schemathesis/config/_rate_limit.py +17 -0
  38. schemathesis/config/_report.py +120 -0
  39. schemathesis/config/_validator.py +9 -0
  40. schemathesis/config/_warnings.py +25 -0
  41. schemathesis/config/schema.json +885 -0
  42. schemathesis/core/__init__.py +67 -0
  43. schemathesis/core/compat.py +32 -0
  44. schemathesis/core/control.py +2 -0
  45. schemathesis/core/curl.py +58 -0
  46. schemathesis/core/deserialization.py +65 -0
  47. schemathesis/core/errors.py +459 -0
  48. schemathesis/core/failures.py +315 -0
  49. schemathesis/core/fs.py +19 -0
  50. schemathesis/core/hooks.py +20 -0
  51. schemathesis/core/loaders.py +104 -0
  52. schemathesis/core/marks.py +66 -0
  53. schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
  54. schemathesis/core/output/__init__.py +46 -0
  55. schemathesis/core/output/sanitization.py +54 -0
  56. schemathesis/{throttling.py → core/rate_limit.py} +16 -17
  57. schemathesis/core/registries.py +31 -0
  58. schemathesis/core/transforms.py +113 -0
  59. schemathesis/core/transport.py +223 -0
  60. schemathesis/core/validation.py +54 -0
  61. schemathesis/core/version.py +7 -0
  62. schemathesis/engine/__init__.py +28 -0
  63. schemathesis/engine/context.py +118 -0
  64. schemathesis/engine/control.py +36 -0
  65. schemathesis/engine/core.py +169 -0
  66. schemathesis/engine/errors.py +464 -0
  67. schemathesis/engine/events.py +258 -0
  68. schemathesis/engine/phases/__init__.py +88 -0
  69. schemathesis/{runner → engine/phases}/probes.py +52 -68
  70. schemathesis/engine/phases/stateful/__init__.py +68 -0
  71. schemathesis/engine/phases/stateful/_executor.py +356 -0
  72. schemathesis/engine/phases/stateful/context.py +85 -0
  73. schemathesis/engine/phases/unit/__init__.py +212 -0
  74. schemathesis/engine/phases/unit/_executor.py +416 -0
  75. schemathesis/engine/phases/unit/_pool.py +82 -0
  76. schemathesis/engine/recorder.py +247 -0
  77. schemathesis/errors.py +43 -0
  78. schemathesis/filters.py +17 -98
  79. schemathesis/generation/__init__.py +5 -33
  80. schemathesis/generation/case.py +317 -0
  81. schemathesis/generation/coverage.py +282 -175
  82. schemathesis/generation/hypothesis/__init__.py +36 -0
  83. schemathesis/generation/hypothesis/builder.py +800 -0
  84. schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
  85. schemathesis/generation/hypothesis/given.py +66 -0
  86. schemathesis/generation/hypothesis/reporting.py +14 -0
  87. schemathesis/generation/hypothesis/strategies.py +16 -0
  88. schemathesis/generation/meta.py +115 -0
  89. schemathesis/generation/metrics.py +93 -0
  90. schemathesis/generation/modes.py +20 -0
  91. schemathesis/generation/overrides.py +116 -0
  92. schemathesis/generation/stateful/__init__.py +37 -0
  93. schemathesis/generation/stateful/state_machine.py +278 -0
  94. schemathesis/graphql/__init__.py +15 -0
  95. schemathesis/graphql/checks.py +109 -0
  96. schemathesis/graphql/loaders.py +284 -0
  97. schemathesis/hooks.py +80 -101
  98. schemathesis/openapi/__init__.py +13 -0
  99. schemathesis/openapi/checks.py +455 -0
  100. schemathesis/openapi/generation/__init__.py +0 -0
  101. schemathesis/openapi/generation/filters.py +72 -0
  102. schemathesis/openapi/loaders.py +313 -0
  103. schemathesis/pytest/__init__.py +5 -0
  104. schemathesis/pytest/control_flow.py +7 -0
  105. schemathesis/pytest/lazy.py +281 -0
  106. schemathesis/pytest/loaders.py +36 -0
  107. schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +128 -108
  108. schemathesis/python/__init__.py +0 -0
  109. schemathesis/python/asgi.py +12 -0
  110. schemathesis/python/wsgi.py +12 -0
  111. schemathesis/schemas.py +537 -273
  112. schemathesis/specs/graphql/__init__.py +0 -1
  113. schemathesis/specs/graphql/_cache.py +1 -2
  114. schemathesis/specs/graphql/scalars.py +42 -6
  115. schemathesis/specs/graphql/schemas.py +141 -137
  116. schemathesis/specs/graphql/validation.py +11 -17
  117. schemathesis/specs/openapi/__init__.py +6 -1
  118. schemathesis/specs/openapi/_cache.py +1 -2
  119. schemathesis/specs/openapi/_hypothesis.py +142 -156
  120. schemathesis/specs/openapi/checks.py +368 -257
  121. schemathesis/specs/openapi/converter.py +4 -4
  122. schemathesis/specs/openapi/definitions.py +1 -1
  123. schemathesis/specs/openapi/examples.py +23 -21
  124. schemathesis/specs/openapi/expressions/__init__.py +31 -19
  125. schemathesis/specs/openapi/expressions/extractors.py +1 -4
  126. schemathesis/specs/openapi/expressions/lexer.py +1 -1
  127. schemathesis/specs/openapi/expressions/nodes.py +36 -41
  128. schemathesis/specs/openapi/expressions/parser.py +1 -1
  129. schemathesis/specs/openapi/formats.py +35 -7
  130. schemathesis/specs/openapi/media_types.py +53 -12
  131. schemathesis/specs/openapi/negative/__init__.py +7 -4
  132. schemathesis/specs/openapi/negative/mutations.py +6 -5
  133. schemathesis/specs/openapi/parameters.py +7 -10
  134. schemathesis/specs/openapi/patterns.py +94 -31
  135. schemathesis/specs/openapi/references.py +12 -53
  136. schemathesis/specs/openapi/schemas.py +233 -307
  137. schemathesis/specs/openapi/security.py +1 -1
  138. schemathesis/specs/openapi/serialization.py +12 -6
  139. schemathesis/specs/openapi/stateful/__init__.py +268 -133
  140. schemathesis/specs/openapi/stateful/control.py +87 -0
  141. schemathesis/specs/openapi/stateful/links.py +209 -0
  142. schemathesis/transport/__init__.py +142 -0
  143. schemathesis/transport/asgi.py +26 -0
  144. schemathesis/transport/prepare.py +124 -0
  145. schemathesis/transport/requests.py +244 -0
  146. schemathesis/{_xml.py → transport/serialization.py} +69 -11
  147. schemathesis/transport/wsgi.py +171 -0
  148. schemathesis-4.0.0.dist-info/METADATA +204 -0
  149. schemathesis-4.0.0.dist-info/RECORD +164 -0
  150. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/entry_points.txt +1 -1
  151. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/licenses/LICENSE +1 -1
  152. schemathesis/_compat.py +0 -74
  153. schemathesis/_dependency_versions.py +0 -19
  154. schemathesis/_hypothesis.py +0 -717
  155. schemathesis/_override.py +0 -50
  156. schemathesis/_patches.py +0 -21
  157. schemathesis/_rate_limiter.py +0 -7
  158. schemathesis/cli/callbacks.py +0 -466
  159. schemathesis/cli/cassettes.py +0 -561
  160. schemathesis/cli/context.py +0 -75
  161. schemathesis/cli/debug.py +0 -27
  162. schemathesis/cli/handlers.py +0 -19
  163. schemathesis/cli/junitxml.py +0 -124
  164. schemathesis/cli/output/__init__.py +0 -1
  165. schemathesis/cli/output/default.py +0 -920
  166. schemathesis/cli/output/short.py +0 -59
  167. schemathesis/cli/reporting.py +0 -79
  168. schemathesis/cli/sanitization.py +0 -26
  169. schemathesis/code_samples.py +0 -151
  170. schemathesis/constants.py +0 -54
  171. schemathesis/contrib/__init__.py +0 -11
  172. schemathesis/contrib/openapi/__init__.py +0 -11
  173. schemathesis/contrib/openapi/fill_missing_examples.py +0 -24
  174. schemathesis/contrib/openapi/formats/__init__.py +0 -9
  175. schemathesis/contrib/openapi/formats/uuid.py +0 -16
  176. schemathesis/contrib/unique_data.py +0 -41
  177. schemathesis/exceptions.py +0 -571
  178. schemathesis/experimental/__init__.py +0 -109
  179. schemathesis/extra/_aiohttp.py +0 -28
  180. schemathesis/extra/_flask.py +0 -13
  181. schemathesis/extra/_server.py +0 -18
  182. schemathesis/failures.py +0 -284
  183. schemathesis/fixups/__init__.py +0 -37
  184. schemathesis/fixups/fast_api.py +0 -41
  185. schemathesis/fixups/utf8_bom.py +0 -28
  186. schemathesis/generation/_methods.py +0 -44
  187. schemathesis/graphql.py +0 -3
  188. schemathesis/internal/__init__.py +0 -7
  189. schemathesis/internal/checks.py +0 -86
  190. schemathesis/internal/copy.py +0 -32
  191. schemathesis/internal/datetime.py +0 -5
  192. schemathesis/internal/deprecation.py +0 -37
  193. schemathesis/internal/diff.py +0 -15
  194. schemathesis/internal/extensions.py +0 -27
  195. schemathesis/internal/jsonschema.py +0 -36
  196. schemathesis/internal/output.py +0 -68
  197. schemathesis/internal/transformation.py +0 -26
  198. schemathesis/internal/validation.py +0 -34
  199. schemathesis/lazy.py +0 -474
  200. schemathesis/loaders.py +0 -122
  201. schemathesis/models.py +0 -1341
  202. schemathesis/parameters.py +0 -90
  203. schemathesis/runner/__init__.py +0 -605
  204. schemathesis/runner/events.py +0 -389
  205. schemathesis/runner/impl/__init__.py +0 -3
  206. schemathesis/runner/impl/context.py +0 -88
  207. schemathesis/runner/impl/core.py +0 -1280
  208. schemathesis/runner/impl/solo.py +0 -80
  209. schemathesis/runner/impl/threadpool.py +0 -391
  210. schemathesis/runner/serialization.py +0 -544
  211. schemathesis/sanitization.py +0 -252
  212. schemathesis/serializers.py +0 -328
  213. schemathesis/service/__init__.py +0 -18
  214. schemathesis/service/auth.py +0 -11
  215. schemathesis/service/ci.py +0 -202
  216. schemathesis/service/client.py +0 -133
  217. schemathesis/service/constants.py +0 -38
  218. schemathesis/service/events.py +0 -61
  219. schemathesis/service/extensions.py +0 -224
  220. schemathesis/service/hosts.py +0 -111
  221. schemathesis/service/metadata.py +0 -71
  222. schemathesis/service/models.py +0 -258
  223. schemathesis/service/report.py +0 -255
  224. schemathesis/service/serialization.py +0 -173
  225. schemathesis/service/usage.py +0 -66
  226. schemathesis/specs/graphql/loaders.py +0 -364
  227. schemathesis/specs/openapi/expressions/context.py +0 -16
  228. schemathesis/specs/openapi/links.py +0 -389
  229. schemathesis/specs/openapi/loaders.py +0 -707
  230. schemathesis/specs/openapi/stateful/statistic.py +0 -198
  231. schemathesis/specs/openapi/stateful/types.py +0 -14
  232. schemathesis/specs/openapi/validation.py +0 -26
  233. schemathesis/stateful/__init__.py +0 -147
  234. schemathesis/stateful/config.py +0 -97
  235. schemathesis/stateful/context.py +0 -135
  236. schemathesis/stateful/events.py +0 -274
  237. schemathesis/stateful/runner.py +0 -309
  238. schemathesis/stateful/sink.py +0 -68
  239. schemathesis/stateful/state_machine.py +0 -328
  240. schemathesis/stateful/statistic.py +0 -22
  241. schemathesis/stateful/validation.py +0 -100
  242. schemathesis/targets.py +0 -77
  243. schemathesis/transports/__init__.py +0 -369
  244. schemathesis/transports/asgi.py +0 -7
  245. schemathesis/transports/auth.py +0 -38
  246. schemathesis/transports/headers.py +0 -36
  247. schemathesis/transports/responses.py +0 -57
  248. schemathesis/types.py +0 -44
  249. schemathesis/utils.py +0 -164
  250. schemathesis-3.39.16.dist-info/METADATA +0 -293
  251. schemathesis-3.39.16.dist-info/RECORD +0 -160
  252. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
  253. /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
  254. /schemathesis/{internal → core}/result.py +0 -0
  255. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,171 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from schemathesis.config._diff_base import DiffBase
7
+
8
+ # Exact keys to sanitize
9
+ DEFAULT_KEYS_TO_SANITIZE = [
10
+ "phpsessid",
11
+ "xsrf-token",
12
+ "_csrf",
13
+ "_csrf_token",
14
+ "_session",
15
+ "_xsrf",
16
+ "aiohttp_session",
17
+ "api_key",
18
+ "api-key",
19
+ "apikey",
20
+ "auth",
21
+ "authorization",
22
+ "connect.sid",
23
+ "cookie",
24
+ "credentials",
25
+ "csrf",
26
+ "csrf_token",
27
+ "csrf-token",
28
+ "csrftoken",
29
+ "ip_address",
30
+ "mysql_pwd",
31
+ "passwd",
32
+ "password",
33
+ "private_key",
34
+ "private-key",
35
+ "privatekey",
36
+ "remote_addr",
37
+ "remote-addr",
38
+ "secret",
39
+ "session",
40
+ "sessionid",
41
+ "set_cookie",
42
+ "set-cookie",
43
+ "token",
44
+ "x_api_key",
45
+ "x-api-key",
46
+ "x_csrftoken",
47
+ "x-csrftoken",
48
+ "x_forwarded_for",
49
+ "x-forwarded-for",
50
+ "x_real_ip",
51
+ "x-real-ip",
52
+ ]
53
+
54
+ # Markers indicating potentially sensitive keys
55
+ DEFAULT_SENSITIVE_MARKERS = [
56
+ "token",
57
+ "key",
58
+ "secret",
59
+ "password",
60
+ "auth",
61
+ "session",
62
+ "passwd",
63
+ "credential",
64
+ ]
65
+
66
+ DEFAULT_REPLACEMENT = "[Filtered]"
67
+
68
+
69
+ @dataclass(repr=False)
70
+ class SanitizationConfig(DiffBase):
71
+ """Configuration for sanitizing sensitive data."""
72
+
73
+ enabled: bool
74
+ keys_to_sanitize: list[str]
75
+ sensitive_markers: list[str]
76
+ replacement: str
77
+
78
+ __slots__ = ("enabled", "keys_to_sanitize", "sensitive_markers", "replacement")
79
+
80
+ def __init__(
81
+ self,
82
+ *,
83
+ enabled: bool = True,
84
+ keys_to_sanitize: list[str] | None = None,
85
+ sensitive_markers: list[str] | None = None,
86
+ replacement: str | None = None,
87
+ ) -> None:
88
+ self.enabled = enabled
89
+ self.keys_to_sanitize = keys_to_sanitize or DEFAULT_KEYS_TO_SANITIZE
90
+ self.sensitive_markers = sensitive_markers or DEFAULT_SENSITIVE_MARKERS
91
+ self.replacement = replacement or DEFAULT_REPLACEMENT
92
+
93
+ @classmethod
94
+ def from_dict(cls, data: dict[str, Any]) -> SanitizationConfig:
95
+ return cls(
96
+ enabled=data.get("enabled", True),
97
+ keys_to_sanitize=[k.lower() for k in data.get("keys-to-sanitize", [])] or DEFAULT_KEYS_TO_SANITIZE,
98
+ sensitive_markers=[m.lower() for m in data.get("sensitive-markers", [])] or DEFAULT_SENSITIVE_MARKERS,
99
+ replacement=data.get("replacement", DEFAULT_REPLACEMENT),
100
+ )
101
+
102
+ def update(self, *, enabled: bool | None = None) -> None:
103
+ if enabled is not None:
104
+ self.enabled = enabled
105
+
106
+
107
+ MAX_PAYLOAD_SIZE = 512
108
+ MAX_LINES = 10
109
+ MAX_WIDTH = 80
110
+
111
+
112
+ @dataclass(repr=False)
113
+ class TruncationConfig(DiffBase):
114
+ """Configuration for truncating large output."""
115
+
116
+ enabled: bool
117
+ max_payload_size: int
118
+ max_lines: int
119
+ max_width: int
120
+
121
+ __slots__ = ("enabled", "max_payload_size", "max_lines", "max_width")
122
+
123
+ def __init__(
124
+ self,
125
+ *,
126
+ enabled: bool = True,
127
+ max_payload_size: int = MAX_PAYLOAD_SIZE,
128
+ max_lines: int = MAX_LINES,
129
+ max_width: int = MAX_WIDTH,
130
+ ) -> None:
131
+ self.enabled = enabled
132
+ self.max_payload_size = max_payload_size
133
+ self.max_lines = max_lines
134
+ self.max_width = max_width
135
+
136
+ @classmethod
137
+ def from_dict(cls, data: dict[str, Any]) -> TruncationConfig:
138
+ return cls(
139
+ enabled=data.get("enabled", True),
140
+ max_payload_size=data.get("max-payload-size", MAX_PAYLOAD_SIZE),
141
+ max_lines=data.get("max-lines", MAX_LINES),
142
+ max_width=data.get("max-width", MAX_WIDTH),
143
+ )
144
+
145
+ def update(self, *, enabled: bool | None = None) -> None:
146
+ if enabled is not None:
147
+ self.enabled = enabled
148
+
149
+
150
+ @dataclass(repr=False)
151
+ class OutputConfig(DiffBase):
152
+ sanitization: SanitizationConfig
153
+ truncation: TruncationConfig
154
+
155
+ __slots__ = ("sanitization", "truncation")
156
+
157
+ def __init__(
158
+ self,
159
+ *,
160
+ sanitization: SanitizationConfig | None = None,
161
+ truncation: TruncationConfig | None = None,
162
+ ) -> None:
163
+ self.sanitization = sanitization or SanitizationConfig()
164
+ self.truncation = truncation or TruncationConfig()
165
+
166
+ @classmethod
167
+ def from_dict(cls, data: dict[str, Any]) -> OutputConfig:
168
+ return cls(
169
+ sanitization=SanitizationConfig.from_dict(data.get("sanitization", {})),
170
+ truncation=TruncationConfig.from_dict(data.get("truncation", {})),
171
+ )
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Literal
4
+
5
+ from schemathesis.config._env import resolve
6
+
7
+ # Define valid parameter locations from OpenAPI
8
+ ParameterLocation = Literal["path", "query", "header", "cookie", "body"]
9
+ VALID_LOCATIONS: list[ParameterLocation] = ["path", "query", "header", "cookie", "body"]
10
+
11
+
12
+ def load_parameters(data: dict[str, Any]) -> dict[str, Any]:
13
+ parameters = {}
14
+ for key, value in data.get("parameters", {}).items():
15
+ if isinstance(value, str):
16
+ parameters[key] = resolve(value)
17
+ else:
18
+ parameters[key] = value
19
+ return parameters
@@ -0,0 +1,187 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from schemathesis.config._checks import ChecksConfig
7
+ from schemathesis.config._diff_base import DiffBase
8
+ from schemathesis.config._generation import GenerationConfig
9
+ from schemathesis.core import DEFAULT_STATEFUL_STEP_COUNT
10
+
11
+ DEFAULT_UNEXPECTED_METHODS = {"get", "put", "post", "delete", "options", "patch", "trace"}
12
+
13
+
14
+ @dataclass(repr=False)
15
+ class PhaseConfig(DiffBase):
16
+ enabled: bool
17
+ generation: GenerationConfig
18
+ checks: ChecksConfig
19
+
20
+ __slots__ = ("enabled", "generation", "checks")
21
+
22
+ def __init__(
23
+ self,
24
+ *,
25
+ enabled: bool = True,
26
+ generation: GenerationConfig | None = None,
27
+ checks: ChecksConfig | None = None,
28
+ ) -> None:
29
+ self.enabled = enabled
30
+ self.generation = generation or GenerationConfig()
31
+ self.checks = checks or ChecksConfig()
32
+
33
+ @classmethod
34
+ def from_dict(cls, data: dict[str, Any]) -> PhaseConfig:
35
+ return cls(
36
+ enabled=data.get("enabled", True),
37
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
38
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
39
+ )
40
+
41
+
42
+ @dataclass(repr=False)
43
+ class ExamplesPhaseConfig(DiffBase):
44
+ enabled: bool
45
+ fill_missing: bool
46
+ generation: GenerationConfig
47
+ checks: ChecksConfig
48
+
49
+ __slots__ = ("enabled", "fill_missing", "generation", "checks")
50
+
51
+ def __init__(
52
+ self,
53
+ *,
54
+ enabled: bool = True,
55
+ fill_missing: bool = False,
56
+ generation: GenerationConfig | None = None,
57
+ checks: ChecksConfig | None = None,
58
+ ) -> None:
59
+ self.enabled = enabled
60
+ self.fill_missing = fill_missing
61
+ self.generation = generation or GenerationConfig()
62
+ self.checks = checks or ChecksConfig()
63
+
64
+ @classmethod
65
+ def from_dict(cls, data: dict[str, Any]) -> ExamplesPhaseConfig:
66
+ return cls(
67
+ enabled=data.get("enabled", True),
68
+ fill_missing=data.get("fill-missing", False),
69
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
70
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
71
+ )
72
+
73
+
74
+ @dataclass(repr=False)
75
+ class CoveragePhaseConfig(DiffBase):
76
+ enabled: bool
77
+ generate_duplicate_query_parameters: bool
78
+ generation: GenerationConfig
79
+ checks: ChecksConfig
80
+ unexpected_methods: set[str]
81
+
82
+ __slots__ = ("enabled", "generate_duplicate_query_parameters", "generation", "checks", "unexpected_methods")
83
+
84
+ def __init__(
85
+ self,
86
+ *,
87
+ enabled: bool = True,
88
+ generate_duplicate_query_parameters: bool = False,
89
+ generation: GenerationConfig | None = None,
90
+ checks: ChecksConfig | None = None,
91
+ unexpected_methods: set[str] | None = None,
92
+ ) -> None:
93
+ self.enabled = enabled
94
+ self.generate_duplicate_query_parameters = generate_duplicate_query_parameters
95
+ self.unexpected_methods = unexpected_methods or DEFAULT_UNEXPECTED_METHODS
96
+ self.generation = generation or GenerationConfig()
97
+ self.checks = checks or ChecksConfig()
98
+
99
+ @classmethod
100
+ def from_dict(cls, data: dict[str, Any]) -> CoveragePhaseConfig:
101
+ return cls(
102
+ enabled=data.get("enabled", True),
103
+ generate_duplicate_query_parameters=data.get("generate-duplicate-query-parameters", False),
104
+ unexpected_methods={method.lower() for method in data.get("unexpected-methods", [])}
105
+ if "unexpected-methods" in data
106
+ else None,
107
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
108
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
109
+ )
110
+
111
+
112
+ @dataclass(repr=False)
113
+ class StatefulPhaseConfig(DiffBase):
114
+ enabled: bool
115
+ generation: GenerationConfig
116
+ checks: ChecksConfig
117
+ max_steps: int
118
+
119
+ __slots__ = ("enabled", "generation", "checks", "max_steps")
120
+
121
+ def __init__(
122
+ self,
123
+ *,
124
+ enabled: bool = True,
125
+ generation: GenerationConfig | None = None,
126
+ checks: ChecksConfig | None = None,
127
+ max_steps: int | None = None,
128
+ ) -> None:
129
+ self.enabled = enabled
130
+ self.max_steps = max_steps or DEFAULT_STATEFUL_STEP_COUNT
131
+ self.generation = generation or GenerationConfig()
132
+ self.checks = checks or ChecksConfig()
133
+
134
+ @classmethod
135
+ def from_dict(cls, data: dict[str, Any]) -> StatefulPhaseConfig:
136
+ return cls(
137
+ enabled=data.get("enabled", True),
138
+ max_steps=data.get("max-steps"),
139
+ generation=GenerationConfig.from_dict(data.get("generation", {})),
140
+ checks=ChecksConfig.from_dict(data.get("checks", {})),
141
+ )
142
+
143
+
144
+ @dataclass(repr=False)
145
+ class PhasesConfig(DiffBase):
146
+ examples: ExamplesPhaseConfig
147
+ coverage: CoveragePhaseConfig
148
+ fuzzing: PhaseConfig
149
+ stateful: StatefulPhaseConfig
150
+
151
+ __slots__ = ("examples", "coverage", "fuzzing", "stateful")
152
+
153
+ def __init__(
154
+ self,
155
+ *,
156
+ examples: ExamplesPhaseConfig | None = None,
157
+ coverage: CoveragePhaseConfig | None = None,
158
+ fuzzing: PhaseConfig | None = None,
159
+ stateful: StatefulPhaseConfig | None = None,
160
+ ) -> None:
161
+ self.examples = examples or ExamplesPhaseConfig()
162
+ self.coverage = coverage or CoveragePhaseConfig()
163
+ self.fuzzing = fuzzing or PhaseConfig()
164
+ self.stateful = stateful or StatefulPhaseConfig()
165
+
166
+ def get_by_name(self, *, name: str) -> PhaseConfig | CoveragePhaseConfig | StatefulPhaseConfig:
167
+ return {
168
+ "examples": self.examples,
169
+ "coverage": self.coverage,
170
+ "fuzzing": self.fuzzing,
171
+ "stateful": self.stateful,
172
+ }[name] # type: ignore[return-value]
173
+
174
+ @classmethod
175
+ def from_dict(cls, data: dict[str, Any]) -> PhasesConfig:
176
+ return cls(
177
+ examples=ExamplesPhaseConfig.from_dict(data.get("examples", {})),
178
+ coverage=CoveragePhaseConfig.from_dict(data.get("coverage", {})),
179
+ fuzzing=PhaseConfig.from_dict(data.get("fuzzing", {})),
180
+ stateful=StatefulPhaseConfig.from_dict(data.get("stateful", {})),
181
+ )
182
+
183
+ def update(self, *, phases: list[str]) -> None:
184
+ self.examples.enabled = "examples" in phases
185
+ self.coverage.enabled = "coverage" in phases
186
+ self.fuzzing.enabled = "fuzzing" in phases
187
+ self.stateful.enabled = "stateful" in phases