schemathesis 3.39.7__py3-none-any.whl → 4.0.0a2__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 (229) hide show
  1. schemathesis/__init__.py +27 -65
  2. schemathesis/auths.py +26 -68
  3. schemathesis/checks.py +130 -60
  4. schemathesis/cli/__init__.py +5 -2105
  5. schemathesis/cli/commands/__init__.py +37 -0
  6. schemathesis/cli/commands/run/__init__.py +662 -0
  7. schemathesis/cli/commands/run/checks.py +80 -0
  8. schemathesis/cli/commands/run/context.py +117 -0
  9. schemathesis/cli/commands/run/events.py +30 -0
  10. schemathesis/cli/commands/run/executor.py +141 -0
  11. schemathesis/cli/commands/run/filters.py +202 -0
  12. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  13. schemathesis/cli/commands/run/handlers/base.py +18 -0
  14. schemathesis/cli/{cassettes.py → commands/run/handlers/cassettes.py} +178 -247
  15. schemathesis/cli/commands/run/handlers/junitxml.py +54 -0
  16. schemathesis/cli/commands/run/handlers/output.py +1368 -0
  17. schemathesis/cli/commands/run/hypothesis.py +105 -0
  18. schemathesis/cli/commands/run/loaders.py +129 -0
  19. schemathesis/cli/{callbacks.py → commands/run/validation.py} +59 -175
  20. schemathesis/cli/constants.py +5 -58
  21. schemathesis/cli/core.py +17 -0
  22. schemathesis/cli/ext/fs.py +14 -0
  23. schemathesis/cli/ext/groups.py +55 -0
  24. schemathesis/cli/{options.py → ext/options.py} +37 -16
  25. schemathesis/cli/hooks.py +36 -0
  26. schemathesis/contrib/__init__.py +1 -3
  27. schemathesis/contrib/openapi/__init__.py +1 -3
  28. schemathesis/contrib/openapi/fill_missing_examples.py +3 -7
  29. schemathesis/core/__init__.py +58 -0
  30. schemathesis/core/compat.py +25 -0
  31. schemathesis/core/control.py +2 -0
  32. schemathesis/core/curl.py +58 -0
  33. schemathesis/core/deserialization.py +65 -0
  34. schemathesis/core/errors.py +370 -0
  35. schemathesis/core/failures.py +315 -0
  36. schemathesis/core/fs.py +19 -0
  37. schemathesis/core/loaders.py +104 -0
  38. schemathesis/core/marks.py +66 -0
  39. schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
  40. schemathesis/{internal/output.py → core/output/__init__.py} +1 -0
  41. schemathesis/core/output/sanitization.py +197 -0
  42. schemathesis/{throttling.py → core/rate_limit.py} +16 -17
  43. schemathesis/core/registries.py +31 -0
  44. schemathesis/core/transforms.py +113 -0
  45. schemathesis/core/transport.py +108 -0
  46. schemathesis/core/validation.py +38 -0
  47. schemathesis/core/version.py +7 -0
  48. schemathesis/engine/__init__.py +30 -0
  49. schemathesis/engine/config.py +59 -0
  50. schemathesis/engine/context.py +119 -0
  51. schemathesis/engine/control.py +36 -0
  52. schemathesis/engine/core.py +157 -0
  53. schemathesis/engine/errors.py +394 -0
  54. schemathesis/engine/events.py +243 -0
  55. schemathesis/engine/phases/__init__.py +66 -0
  56. schemathesis/{runner → engine/phases}/probes.py +49 -68
  57. schemathesis/engine/phases/stateful/__init__.py +66 -0
  58. schemathesis/engine/phases/stateful/_executor.py +301 -0
  59. schemathesis/engine/phases/stateful/context.py +85 -0
  60. schemathesis/engine/phases/unit/__init__.py +175 -0
  61. schemathesis/engine/phases/unit/_executor.py +322 -0
  62. schemathesis/engine/phases/unit/_pool.py +74 -0
  63. schemathesis/engine/recorder.py +246 -0
  64. schemathesis/errors.py +31 -0
  65. schemathesis/experimental/__init__.py +9 -40
  66. schemathesis/filters.py +7 -95
  67. schemathesis/generation/__init__.py +3 -3
  68. schemathesis/generation/case.py +190 -0
  69. schemathesis/generation/coverage.py +22 -22
  70. schemathesis/{_patches.py → generation/hypothesis/__init__.py} +15 -6
  71. schemathesis/generation/hypothesis/builder.py +585 -0
  72. schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
  73. schemathesis/generation/hypothesis/given.py +66 -0
  74. schemathesis/generation/hypothesis/reporting.py +14 -0
  75. schemathesis/generation/hypothesis/strategies.py +16 -0
  76. schemathesis/generation/meta.py +115 -0
  77. schemathesis/generation/modes.py +28 -0
  78. schemathesis/generation/overrides.py +96 -0
  79. schemathesis/generation/stateful/__init__.py +20 -0
  80. schemathesis/{stateful → generation/stateful}/state_machine.py +84 -109
  81. schemathesis/generation/targets.py +69 -0
  82. schemathesis/graphql/__init__.py +15 -0
  83. schemathesis/graphql/checks.py +109 -0
  84. schemathesis/graphql/loaders.py +131 -0
  85. schemathesis/hooks.py +17 -62
  86. schemathesis/openapi/__init__.py +13 -0
  87. schemathesis/openapi/checks.py +387 -0
  88. schemathesis/openapi/generation/__init__.py +0 -0
  89. schemathesis/openapi/generation/filters.py +63 -0
  90. schemathesis/openapi/loaders.py +178 -0
  91. schemathesis/pytest/__init__.py +5 -0
  92. schemathesis/pytest/control_flow.py +7 -0
  93. schemathesis/pytest/lazy.py +273 -0
  94. schemathesis/pytest/loaders.py +12 -0
  95. schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +94 -107
  96. schemathesis/python/__init__.py +0 -0
  97. schemathesis/python/asgi.py +12 -0
  98. schemathesis/python/wsgi.py +12 -0
  99. schemathesis/schemas.py +456 -228
  100. schemathesis/specs/graphql/__init__.py +0 -1
  101. schemathesis/specs/graphql/_cache.py +1 -2
  102. schemathesis/specs/graphql/scalars.py +5 -3
  103. schemathesis/specs/graphql/schemas.py +122 -123
  104. schemathesis/specs/graphql/validation.py +11 -17
  105. schemathesis/specs/openapi/__init__.py +6 -1
  106. schemathesis/specs/openapi/_cache.py +1 -2
  107. schemathesis/specs/openapi/_hypothesis.py +97 -134
  108. schemathesis/specs/openapi/checks.py +238 -219
  109. schemathesis/specs/openapi/converter.py +4 -4
  110. schemathesis/specs/openapi/definitions.py +1 -1
  111. schemathesis/specs/openapi/examples.py +22 -20
  112. schemathesis/specs/openapi/expressions/__init__.py +11 -15
  113. schemathesis/specs/openapi/expressions/extractors.py +1 -4
  114. schemathesis/specs/openapi/expressions/nodes.py +33 -32
  115. schemathesis/specs/openapi/formats.py +3 -2
  116. schemathesis/specs/openapi/links.py +123 -299
  117. schemathesis/specs/openapi/media_types.py +10 -12
  118. schemathesis/specs/openapi/negative/__init__.py +2 -1
  119. schemathesis/specs/openapi/negative/mutations.py +3 -2
  120. schemathesis/specs/openapi/parameters.py +8 -6
  121. schemathesis/specs/openapi/patterns.py +1 -1
  122. schemathesis/specs/openapi/references.py +11 -51
  123. schemathesis/specs/openapi/schemas.py +177 -191
  124. schemathesis/specs/openapi/security.py +1 -1
  125. schemathesis/specs/openapi/serialization.py +10 -6
  126. schemathesis/specs/openapi/stateful/__init__.py +97 -91
  127. schemathesis/transport/__init__.py +104 -0
  128. schemathesis/transport/asgi.py +26 -0
  129. schemathesis/transport/prepare.py +99 -0
  130. schemathesis/transport/requests.py +221 -0
  131. schemathesis/{_xml.py → transport/serialization.py} +69 -7
  132. schemathesis/transport/wsgi.py +165 -0
  133. {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/METADATA +18 -14
  134. schemathesis-4.0.0a2.dist-info/RECORD +151 -0
  135. {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/entry_points.txt +1 -1
  136. schemathesis/_compat.py +0 -74
  137. schemathesis/_dependency_versions.py +0 -19
  138. schemathesis/_hypothesis.py +0 -559
  139. schemathesis/_override.py +0 -50
  140. schemathesis/_rate_limiter.py +0 -7
  141. schemathesis/cli/context.py +0 -75
  142. schemathesis/cli/debug.py +0 -27
  143. schemathesis/cli/handlers.py +0 -19
  144. schemathesis/cli/junitxml.py +0 -124
  145. schemathesis/cli/output/__init__.py +0 -1
  146. schemathesis/cli/output/default.py +0 -936
  147. schemathesis/cli/output/short.py +0 -59
  148. schemathesis/cli/reporting.py +0 -79
  149. schemathesis/cli/sanitization.py +0 -26
  150. schemathesis/code_samples.py +0 -151
  151. schemathesis/constants.py +0 -56
  152. schemathesis/contrib/openapi/formats/__init__.py +0 -9
  153. schemathesis/contrib/openapi/formats/uuid.py +0 -16
  154. schemathesis/contrib/unique_data.py +0 -41
  155. schemathesis/exceptions.py +0 -571
  156. schemathesis/extra/_aiohttp.py +0 -28
  157. schemathesis/extra/_flask.py +0 -13
  158. schemathesis/extra/_server.py +0 -18
  159. schemathesis/failures.py +0 -277
  160. schemathesis/fixups/__init__.py +0 -37
  161. schemathesis/fixups/fast_api.py +0 -41
  162. schemathesis/fixups/utf8_bom.py +0 -28
  163. schemathesis/generation/_methods.py +0 -44
  164. schemathesis/graphql.py +0 -3
  165. schemathesis/internal/__init__.py +0 -7
  166. schemathesis/internal/checks.py +0 -84
  167. schemathesis/internal/copy.py +0 -32
  168. schemathesis/internal/datetime.py +0 -5
  169. schemathesis/internal/deprecation.py +0 -38
  170. schemathesis/internal/diff.py +0 -15
  171. schemathesis/internal/extensions.py +0 -27
  172. schemathesis/internal/jsonschema.py +0 -36
  173. schemathesis/internal/transformation.py +0 -26
  174. schemathesis/internal/validation.py +0 -34
  175. schemathesis/lazy.py +0 -474
  176. schemathesis/loaders.py +0 -122
  177. schemathesis/models.py +0 -1341
  178. schemathesis/parameters.py +0 -90
  179. schemathesis/runner/__init__.py +0 -605
  180. schemathesis/runner/events.py +0 -389
  181. schemathesis/runner/impl/__init__.py +0 -3
  182. schemathesis/runner/impl/context.py +0 -104
  183. schemathesis/runner/impl/core.py +0 -1246
  184. schemathesis/runner/impl/solo.py +0 -80
  185. schemathesis/runner/impl/threadpool.py +0 -391
  186. schemathesis/runner/serialization.py +0 -544
  187. schemathesis/sanitization.py +0 -252
  188. schemathesis/serializers.py +0 -328
  189. schemathesis/service/__init__.py +0 -18
  190. schemathesis/service/auth.py +0 -11
  191. schemathesis/service/ci.py +0 -202
  192. schemathesis/service/client.py +0 -133
  193. schemathesis/service/constants.py +0 -38
  194. schemathesis/service/events.py +0 -61
  195. schemathesis/service/extensions.py +0 -224
  196. schemathesis/service/hosts.py +0 -111
  197. schemathesis/service/metadata.py +0 -71
  198. schemathesis/service/models.py +0 -258
  199. schemathesis/service/report.py +0 -255
  200. schemathesis/service/serialization.py +0 -173
  201. schemathesis/service/usage.py +0 -66
  202. schemathesis/specs/graphql/loaders.py +0 -364
  203. schemathesis/specs/openapi/expressions/context.py +0 -16
  204. schemathesis/specs/openapi/loaders.py +0 -708
  205. schemathesis/specs/openapi/stateful/statistic.py +0 -198
  206. schemathesis/specs/openapi/stateful/types.py +0 -14
  207. schemathesis/specs/openapi/validation.py +0 -26
  208. schemathesis/stateful/__init__.py +0 -147
  209. schemathesis/stateful/config.py +0 -97
  210. schemathesis/stateful/context.py +0 -135
  211. schemathesis/stateful/events.py +0 -274
  212. schemathesis/stateful/runner.py +0 -309
  213. schemathesis/stateful/sink.py +0 -68
  214. schemathesis/stateful/statistic.py +0 -22
  215. schemathesis/stateful/validation.py +0 -100
  216. schemathesis/targets.py +0 -77
  217. schemathesis/transports/__init__.py +0 -359
  218. schemathesis/transports/asgi.py +0 -7
  219. schemathesis/transports/auth.py +0 -38
  220. schemathesis/transports/headers.py +0 -36
  221. schemathesis/transports/responses.py +0 -57
  222. schemathesis/types.py +0 -44
  223. schemathesis/utils.py +0 -164
  224. schemathesis-3.39.7.dist-info/RECORD +0 -160
  225. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
  226. /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
  227. /schemathesis/{internal → core}/result.py +0 -0
  228. {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/WHEEL +0 -0
  229. {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,90 +0,0 @@
1
- """API operation parameters.
2
-
3
- These are basic entities that describe what data could be sent to the API.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- from dataclasses import dataclass, field
9
- from typing import TYPE_CHECKING, Any, Generator, Generic, TypeVar
10
-
11
- if TYPE_CHECKING:
12
- from .models import APIOperation
13
-
14
-
15
- @dataclass(eq=False)
16
- class Parameter:
17
- """A logically separate parameter bound to a location (e.g., to "query string").
18
-
19
- For example, if the API requires multiple headers to be present, each header is presented as a separate
20
- `Parameter` instance.
21
- """
22
-
23
- # The parameter definition in the language acceptable by the API
24
- definition: Any
25
-
26
- @property
27
- def location(self) -> str:
28
- """Where this parameter is located.
29
-
30
- E.g. "query" or "body"
31
- """
32
- raise NotImplementedError
33
-
34
- @property
35
- def name(self) -> str:
36
- """Parameter name."""
37
- raise NotImplementedError
38
-
39
- @property
40
- def is_required(self) -> bool:
41
- """Whether the parameter is required for a successful API call."""
42
- raise NotImplementedError
43
-
44
- def serialize(self, operation: APIOperation) -> str:
45
- """Get parameter's string representation."""
46
- raise NotImplementedError
47
-
48
-
49
- P = TypeVar("P", bound=Parameter)
50
-
51
-
52
- @dataclass
53
- class ParameterSet(Generic[P]):
54
- """A set of parameters for the same location."""
55
-
56
- items: list[P] = field(default_factory=list)
57
-
58
- def _repr_pretty_(self, *args: Any, **kwargs: Any) -> None: ...
59
-
60
- def add(self, parameter: P) -> None:
61
- """Add a new parameter."""
62
- self.items.append(parameter)
63
-
64
- def get(self, name: str) -> P | None:
65
- for parameter in self:
66
- if parameter.name == name:
67
- return parameter
68
- return None
69
-
70
- def contains(self, name: str) -> bool:
71
- return self.get(name) is not None
72
-
73
- def __contains__(self, item: str) -> bool:
74
- return self.contains(item)
75
-
76
- def __bool__(self) -> bool:
77
- return bool(self.items)
78
-
79
- def __iter__(self) -> Generator[P, None, None]:
80
- yield from iter(self.items)
81
-
82
- def __len__(self) -> int:
83
- return len(self.items)
84
-
85
- def __getitem__(self, item: int) -> P:
86
- return self.items[item]
87
-
88
-
89
- class PayloadAlternatives(ParameterSet[P]):
90
- """A set of alternative payloads."""
@@ -1,605 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from random import Random
4
- from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable
5
- from urllib.parse import urlparse
6
-
7
- from ..constants import (
8
- DEFAULT_DEADLINE,
9
- DEFAULT_STATEFUL_RECURSION_LIMIT,
10
- HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER,
11
- )
12
- from ..exceptions import SchemaError
13
- from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod, GenerationConfig
14
- from ..internal.checks import CheckConfig
15
- from ..internal.datetime import current_datetime
16
- from ..internal.deprecation import deprecated_function
17
- from ..internal.validation import file_exists
18
- from ..loaders import load_app
19
- from ..specs.graphql import loaders as gql_loaders
20
- from ..specs.openapi import loaders as oas_loaders
21
- from ..targets import DEFAULT_TARGETS, Target
22
- from ..transports import RequestConfig
23
- from ..transports.auth import get_requests_auth
24
- from ..types import Filter, NotSet, RawAuth, RequestCert
25
- from . import events
26
- from .probes import ProbeConfig
27
-
28
- if TYPE_CHECKING:
29
- import hypothesis
30
-
31
- from .._override import CaseOverride
32
- from ..models import CheckFunction
33
- from ..schemas import BaseSchema
34
- from ..service.client import ServiceClient
35
- from ..stateful import Stateful
36
- from .impl import BaseRunner
37
-
38
-
39
- @deprecated_function(removed_in="4.0", replacement="schemathesis.runner.from_schema")
40
- def prepare(
41
- schema_uri: str | dict[str, Any],
42
- *,
43
- # Runtime behavior
44
- checks: Iterable[CheckFunction] | None = None,
45
- data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
46
- max_response_time: int | None = None,
47
- targets: Iterable[Target] = DEFAULT_TARGETS,
48
- workers_num: int = 1,
49
- seed: int | None = None,
50
- exit_first: bool = False,
51
- dry_run: bool = False,
52
- store_interactions: bool = False,
53
- stateful: Stateful | None = None,
54
- stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
55
- # Schema loading
56
- loader: Callable = oas_loaders.from_uri,
57
- base_url: str | None = None,
58
- auth: tuple[str, str] | None = None,
59
- auth_type: str | None = None,
60
- override: CaseOverride | None = None,
61
- headers: dict[str, str] | None = None,
62
- request_timeout: int | None = None,
63
- request_tls_verify: bool | str = True,
64
- request_cert: RequestCert | None = None,
65
- endpoint: Filter | None = None,
66
- method: Filter | None = None,
67
- tag: Filter | None = None,
68
- operation_id: Filter | None = None,
69
- app: str | None = None,
70
- validate_schema: bool = True,
71
- skip_deprecated_operations: bool = False,
72
- force_schema_version: str | None = None,
73
- count_operations: bool = True,
74
- count_links: bool = True,
75
- # Hypothesis-specific configuration
76
- hypothesis_deadline: int | NotSet | None = None,
77
- hypothesis_derandomize: bool | None = None,
78
- hypothesis_max_examples: int | None = None,
79
- hypothesis_phases: list[hypothesis.Phase] | None = None,
80
- hypothesis_report_multiple_bugs: bool | None = None,
81
- hypothesis_suppress_health_check: list[hypothesis.HealthCheck] | None = None,
82
- hypothesis_verbosity: hypothesis.Verbosity | None = None,
83
- probe_config: ProbeConfig | None = None,
84
- service_client: ServiceClient | None = None,
85
- ) -> Generator[events.ExecutionEvent, None, None]:
86
- """Prepare a generator that will run test cases against the given API definition."""
87
- from ..checks import DEFAULT_CHECKS
88
-
89
- checks = checks or DEFAULT_CHECKS
90
-
91
- validate_loader(loader, schema_uri)
92
-
93
- if auth is None:
94
- # Auth type doesn't matter if auth is not passed
95
- auth_type = None # type: ignore
96
- hypothesis_settings = prepare_hypothesis_settings(
97
- deadline=hypothesis_deadline,
98
- derandomize=hypothesis_derandomize,
99
- max_examples=hypothesis_max_examples,
100
- phases=hypothesis_phases,
101
- report_multiple_bugs=hypothesis_report_multiple_bugs,
102
- suppress_health_check=hypothesis_suppress_health_check,
103
- verbosity=hypothesis_verbosity,
104
- )
105
- return execute_from_schema(
106
- schema_uri=schema_uri,
107
- loader=loader,
108
- base_url=base_url,
109
- endpoint=endpoint,
110
- method=method,
111
- tag=tag,
112
- operation_id=operation_id,
113
- app=app,
114
- validate_schema=validate_schema,
115
- skip_deprecated_operations=skip_deprecated_operations,
116
- force_schema_version=force_schema_version,
117
- checks=checks,
118
- data_generation_methods=data_generation_methods,
119
- max_response_time=max_response_time,
120
- targets=targets,
121
- hypothesis_settings=hypothesis_settings,
122
- seed=seed,
123
- workers_num=workers_num,
124
- exit_first=exit_first,
125
- dry_run=dry_run,
126
- auth=auth,
127
- auth_type=auth_type,
128
- override=override,
129
- headers=headers,
130
- request_timeout=request_timeout,
131
- request_tls_verify=request_tls_verify,
132
- request_cert=request_cert,
133
- store_interactions=store_interactions,
134
- stateful=stateful,
135
- stateful_recursion_limit=stateful_recursion_limit,
136
- count_operations=count_operations,
137
- count_links=count_links,
138
- probe_config=probe_config,
139
- service_client=service_client,
140
- )
141
-
142
-
143
- def validate_loader(loader: Callable, schema_uri: str | dict[str, Any]) -> None:
144
- """Sanity checking for input schema & loader."""
145
- if loader not in (
146
- oas_loaders.from_uri,
147
- oas_loaders.from_aiohttp,
148
- oas_loaders.from_dict,
149
- oas_loaders.from_file,
150
- oas_loaders.from_path,
151
- oas_loaders.from_asgi,
152
- oas_loaders.from_wsgi,
153
- gql_loaders.from_dict,
154
- gql_loaders.from_url,
155
- gql_loaders.from_wsgi,
156
- ):
157
- # Custom loaders are not checked
158
- return
159
- if isinstance(schema_uri, dict):
160
- if loader not in (oas_loaders.from_dict, gql_loaders.from_dict):
161
- raise ValueError("Dictionary as a schema is allowed only with `from_dict` loader")
162
- elif loader in (oas_loaders.from_dict, gql_loaders.from_dict):
163
- raise ValueError("Schema should be a dictionary for `from_dict` loader")
164
-
165
-
166
- def execute_from_schema(
167
- *,
168
- schema_uri: str | dict[str, Any],
169
- loader: Callable = oas_loaders.from_uri,
170
- base_url: str | None = None,
171
- endpoint: Filter | None = None,
172
- method: Filter | None = None,
173
- tag: Filter | None = None,
174
- operation_id: Filter | None = None,
175
- app: str | None = None,
176
- validate_schema: bool = True,
177
- skip_deprecated_operations: bool = False,
178
- force_schema_version: str | None = None,
179
- checks: Iterable[CheckFunction],
180
- data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
181
- max_response_time: int | None = None,
182
- targets: Iterable[Target],
183
- workers_num: int = 1,
184
- hypothesis_settings: hypothesis.settings,
185
- auth: RawAuth | None = None,
186
- auth_type: str | None = None,
187
- override: CaseOverride | None = None,
188
- headers: dict[str, Any] | None = None,
189
- request_timeout: int | None = None,
190
- request_tls_verify: bool | str = True,
191
- request_cert: RequestCert | None = None,
192
- seed: int | None = None,
193
- exit_first: bool = False,
194
- dry_run: bool = False,
195
- store_interactions: bool = False,
196
- stateful: Stateful | None = None,
197
- stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
198
- count_operations: bool = True,
199
- count_links: bool = True,
200
- probe_config: ProbeConfig | None = None,
201
- service_client: ServiceClient | None,
202
- ) -> Generator[events.ExecutionEvent, None, None]:
203
- """Execute tests for the given schema.
204
-
205
- Provides the main testing loop and preparation step.
206
- """
207
- try:
208
- if app is not None:
209
- app = load_app(app)
210
- schema = load_schema(
211
- schema_uri,
212
- base_url=base_url,
213
- loader=loader,
214
- app=app,
215
- validate_schema=validate_schema,
216
- skip_deprecated_operations=skip_deprecated_operations,
217
- auth=auth,
218
- auth_type=auth_type,
219
- headers=headers,
220
- endpoint=endpoint,
221
- method=method,
222
- tag=tag,
223
- operation_id=operation_id,
224
- data_generation_methods=data_generation_methods,
225
- force_schema_version=force_schema_version,
226
- request_tls_verify=request_tls_verify,
227
- request_cert=request_cert,
228
- )
229
- yield from from_schema(
230
- schema,
231
- checks=checks,
232
- max_response_time=max_response_time,
233
- targets=targets,
234
- hypothesis_settings=hypothesis_settings,
235
- auth=auth,
236
- auth_type=auth_type,
237
- override=override,
238
- headers=headers,
239
- seed=seed,
240
- workers_num=workers_num,
241
- request_timeout=request_timeout,
242
- request_tls_verify=request_tls_verify,
243
- request_cert=request_cert,
244
- exit_first=exit_first,
245
- dry_run=dry_run,
246
- store_interactions=store_interactions,
247
- stateful=stateful,
248
- stateful_recursion_limit=stateful_recursion_limit,
249
- count_operations=count_operations,
250
- count_links=count_links,
251
- probe_config=probe_config,
252
- service_client=service_client,
253
- ).execute()
254
- except SchemaError as error:
255
- yield events.InternalError.from_schema_error(error)
256
- except Exception as exc:
257
- yield events.InternalError.from_exc(exc)
258
-
259
-
260
- def load_schema(
261
- schema_uri: str | dict[str, Any],
262
- *,
263
- base_url: str | None = None,
264
- loader: Callable = oas_loaders.from_uri,
265
- app: Any = None,
266
- validate_schema: bool = True,
267
- skip_deprecated_operations: bool = False,
268
- data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
269
- force_schema_version: str | None = None,
270
- request_tls_verify: bool | str = True,
271
- request_cert: RequestCert | None = None,
272
- # Network request parameters
273
- auth: tuple[str, str] | None = None,
274
- auth_type: str | None = None,
275
- headers: dict[str, str] | None = None,
276
- # Schema filters
277
- endpoint: Filter | None = None,
278
- method: Filter | None = None,
279
- tag: Filter | None = None,
280
- operation_id: Filter | None = None,
281
- ) -> BaseSchema:
282
- """Load schema via specified loader and parameters."""
283
- loader_options: dict[str, Any] = {
284
- key: value
285
- for key, value in (
286
- ("base_url", base_url),
287
- ("endpoint", endpoint),
288
- ("method", method),
289
- ("tag", tag),
290
- ("operation_id", operation_id),
291
- ("app", app),
292
- ("data_generation_methods", data_generation_methods),
293
- )
294
- if value
295
- }
296
-
297
- if not isinstance(schema_uri, dict):
298
- if file_exists(schema_uri):
299
- loader = oas_loaders.from_path
300
- elif loader is not oas_loaders.from_path:
301
- if app is not None and not urlparse(schema_uri).netloc:
302
- # If `schema` is not an existing filesystem path, or a URL then it is considered as a path within
303
- # the given app
304
- loader = oas_loaders.get_loader_for_app(app)
305
- if headers:
306
- loader_options["headers"] = headers
307
- else:
308
- if headers:
309
- loader_options["headers"] = headers
310
- if auth:
311
- loader_options["auth"] = auth
312
- if auth_type:
313
- loader_options["auth_type"] = auth_type
314
-
315
- if loader is oas_loaders.from_uri and loader_options.get("auth"):
316
- loader_options["auth"] = get_requests_auth(loader_options["auth"], loader_options.pop("auth_type", None))
317
- if loader in (oas_loaders.from_uri, oas_loaders.from_aiohttp):
318
- loader_options["verify"] = request_tls_verify
319
- loader_options["cert"] = request_cert
320
-
321
- return loader(
322
- schema_uri,
323
- validate_schema=validate_schema,
324
- skip_deprecated_operations=skip_deprecated_operations,
325
- force_schema_version=force_schema_version,
326
- **loader_options,
327
- )
328
-
329
-
330
- def from_schema(
331
- schema: BaseSchema,
332
- *,
333
- override: CaseOverride | None = None,
334
- checks: Iterable[CheckFunction] | None = None,
335
- max_response_time: int | None = None,
336
- targets: Iterable[Target] = DEFAULT_TARGETS,
337
- workers_num: int = 1,
338
- hypothesis_settings: hypothesis.settings | None = None,
339
- generation_config: GenerationConfig | None = None,
340
- auth: RawAuth | None = None,
341
- auth_type: str | None = None,
342
- headers: dict[str, Any] | None = None,
343
- request_timeout: int | None = None,
344
- request_tls_verify: bool | str = True,
345
- request_proxy: str | None = None,
346
- request_cert: RequestCert | None = None,
347
- seed: int | None = None,
348
- exit_first: bool = False,
349
- no_failfast: bool = False,
350
- max_failures: int | None = None,
351
- started_at: str | None = None,
352
- unique_data: bool = False,
353
- dry_run: bool = False,
354
- store_interactions: bool = False,
355
- stateful: Stateful | None = None,
356
- stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
357
- count_operations: bool = True,
358
- count_links: bool = True,
359
- probe_config: ProbeConfig | None = None,
360
- checks_config: CheckConfig | None = None,
361
- service_client: ServiceClient | None = None,
362
- ) -> BaseRunner:
363
- import hypothesis
364
-
365
- from ..checks import DEFAULT_CHECKS
366
- from ..transports.asgi import is_asgi_app
367
- from .impl import (
368
- SingleThreadASGIRunner,
369
- SingleThreadRunner,
370
- SingleThreadWSGIRunner,
371
- ThreadPoolASGIRunner,
372
- ThreadPoolRunner,
373
- ThreadPoolWSGIRunner,
374
- )
375
-
376
- checks = checks or DEFAULT_CHECKS
377
- checks_config = checks_config or CheckConfig()
378
- probe_config = probe_config or ProbeConfig()
379
-
380
- hypothesis_settings = hypothesis_settings or hypothesis.settings(deadline=DEFAULT_DEADLINE)
381
- request_config = RequestConfig(
382
- timeout=request_timeout,
383
- tls_verify=request_tls_verify,
384
- proxy=request_proxy,
385
- cert=request_cert,
386
- )
387
-
388
- # Use the same seed for all tests unless `derandomize=True` is used
389
- if seed is None and not hypothesis_settings.derandomize:
390
- seed = Random().getrandbits(128)
391
-
392
- started_at = started_at or current_datetime()
393
- if workers_num > 1:
394
- if not schema.app:
395
- return ThreadPoolRunner(
396
- schema=schema,
397
- checks=checks,
398
- max_response_time=max_response_time,
399
- targets=targets,
400
- hypothesis_settings=hypothesis_settings,
401
- generation_config=generation_config,
402
- auth=auth,
403
- auth_type=auth_type,
404
- override=override,
405
- headers=headers,
406
- seed=seed,
407
- workers_num=workers_num,
408
- request_config=request_config,
409
- exit_first=exit_first,
410
- no_failfast=no_failfast,
411
- max_failures=max_failures,
412
- started_at=started_at,
413
- unique_data=unique_data,
414
- dry_run=dry_run,
415
- store_interactions=store_interactions,
416
- stateful=stateful,
417
- stateful_recursion_limit=stateful_recursion_limit,
418
- count_operations=count_operations,
419
- count_links=count_links,
420
- probe_config=probe_config,
421
- checks_config=checks_config,
422
- service_client=service_client,
423
- )
424
- if is_asgi_app(schema.app):
425
- return ThreadPoolASGIRunner(
426
- schema=schema,
427
- checks=checks,
428
- max_response_time=max_response_time,
429
- targets=targets,
430
- hypothesis_settings=hypothesis_settings,
431
- generation_config=generation_config,
432
- auth=auth,
433
- auth_type=auth_type,
434
- override=override,
435
- headers=headers,
436
- seed=seed,
437
- exit_first=exit_first,
438
- no_failfast=no_failfast,
439
- max_failures=max_failures,
440
- started_at=started_at,
441
- unique_data=unique_data,
442
- dry_run=dry_run,
443
- store_interactions=store_interactions,
444
- stateful=stateful,
445
- stateful_recursion_limit=stateful_recursion_limit,
446
- count_operations=count_operations,
447
- count_links=count_links,
448
- probe_config=probe_config,
449
- checks_config=checks_config,
450
- service_client=service_client,
451
- )
452
- return ThreadPoolWSGIRunner(
453
- schema=schema,
454
- checks=checks,
455
- max_response_time=max_response_time,
456
- targets=targets,
457
- hypothesis_settings=hypothesis_settings,
458
- generation_config=generation_config,
459
- auth=auth,
460
- auth_type=auth_type,
461
- override=override,
462
- headers=headers,
463
- seed=seed,
464
- workers_num=workers_num,
465
- exit_first=exit_first,
466
- no_failfast=no_failfast,
467
- max_failures=max_failures,
468
- started_at=started_at,
469
- unique_data=unique_data,
470
- dry_run=dry_run,
471
- store_interactions=store_interactions,
472
- stateful=stateful,
473
- stateful_recursion_limit=stateful_recursion_limit,
474
- count_operations=count_operations,
475
- count_links=count_links,
476
- probe_config=probe_config,
477
- checks_config=checks_config,
478
- service_client=service_client,
479
- )
480
- if not schema.app:
481
- return SingleThreadRunner(
482
- schema=schema,
483
- checks=checks,
484
- max_response_time=max_response_time,
485
- targets=targets,
486
- hypothesis_settings=hypothesis_settings,
487
- generation_config=generation_config,
488
- auth=auth,
489
- auth_type=auth_type,
490
- override=override,
491
- headers=headers,
492
- seed=seed,
493
- request_config=request_config,
494
- exit_first=exit_first,
495
- no_failfast=no_failfast,
496
- max_failures=max_failures,
497
- started_at=started_at,
498
- unique_data=unique_data,
499
- dry_run=dry_run,
500
- store_interactions=store_interactions,
501
- stateful=stateful,
502
- stateful_recursion_limit=stateful_recursion_limit,
503
- count_operations=count_operations,
504
- count_links=count_links,
505
- probe_config=probe_config,
506
- checks_config=checks_config,
507
- service_client=service_client,
508
- )
509
- if is_asgi_app(schema.app):
510
- return SingleThreadASGIRunner(
511
- schema=schema,
512
- checks=checks,
513
- max_response_time=max_response_time,
514
- targets=targets,
515
- hypothesis_settings=hypothesis_settings,
516
- generation_config=generation_config,
517
- auth=auth,
518
- auth_type=auth_type,
519
- override=override,
520
- headers=headers,
521
- seed=seed,
522
- exit_first=exit_first,
523
- no_failfast=no_failfast,
524
- max_failures=max_failures,
525
- started_at=started_at,
526
- unique_data=unique_data,
527
- dry_run=dry_run,
528
- store_interactions=store_interactions,
529
- stateful=stateful,
530
- stateful_recursion_limit=stateful_recursion_limit,
531
- count_operations=count_operations,
532
- count_links=count_links,
533
- probe_config=probe_config,
534
- checks_config=checks_config,
535
- service_client=service_client,
536
- )
537
- return SingleThreadWSGIRunner(
538
- schema=schema,
539
- checks=checks,
540
- max_response_time=max_response_time,
541
- targets=targets,
542
- hypothesis_settings=hypothesis_settings,
543
- generation_config=generation_config,
544
- auth=auth,
545
- auth_type=auth_type,
546
- override=override,
547
- headers=headers,
548
- seed=seed,
549
- exit_first=exit_first,
550
- no_failfast=no_failfast,
551
- max_failures=max_failures,
552
- started_at=started_at,
553
- unique_data=unique_data,
554
- dry_run=dry_run,
555
- store_interactions=store_interactions,
556
- stateful=stateful,
557
- stateful_recursion_limit=stateful_recursion_limit,
558
- count_operations=count_operations,
559
- count_links=count_links,
560
- probe_config=probe_config,
561
- checks_config=checks_config,
562
- service_client=service_client,
563
- )
564
-
565
-
566
- def prepare_hypothesis_settings(
567
- database: str | None = None,
568
- deadline: int | NotSet | None = None,
569
- derandomize: bool | None = None,
570
- max_examples: int | None = None,
571
- phases: list[hypothesis.Phase] | None = None,
572
- report_multiple_bugs: bool | None = None,
573
- suppress_health_check: list[hypothesis.HealthCheck] | None = None,
574
- verbosity: hypothesis.Verbosity | None = None,
575
- ) -> hypothesis.settings:
576
- import hypothesis
577
- from hypothesis.database import DirectoryBasedExampleDatabase, InMemoryExampleDatabase
578
-
579
- kwargs = {
580
- key: value
581
- for key, value in (
582
- ("derandomize", derandomize),
583
- ("max_examples", max_examples),
584
- ("phases", phases),
585
- ("report_multiple_bugs", report_multiple_bugs),
586
- ("suppress_health_check", suppress_health_check),
587
- ("verbosity", verbosity),
588
- )
589
- if value is not None
590
- }
591
- # `deadline` is special, since Hypothesis allows passing `None`
592
- if deadline is not None:
593
- if isinstance(deadline, NotSet):
594
- kwargs["deadline"] = None
595
- else:
596
- kwargs["deadline"] = deadline
597
- if database is not None:
598
- if database.lower() == "none":
599
- kwargs["database"] = None
600
- elif database == HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER:
601
- kwargs["database"] = InMemoryExampleDatabase()
602
- else:
603
- kwargs["database"] = DirectoryBasedExampleDatabase(database)
604
- kwargs.setdefault("deadline", DEFAULT_DEADLINE)
605
- return hypothesis.settings(print_blob=False, **kwargs)