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