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.
- schemathesis/__init__.py +41 -79
- schemathesis/auths.py +111 -122
- schemathesis/checks.py +169 -60
- schemathesis/cli/__init__.py +15 -2117
- schemathesis/cli/commands/__init__.py +85 -0
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +590 -0
- schemathesis/cli/commands/run/context.py +204 -0
- schemathesis/cli/commands/run/events.py +60 -0
- schemathesis/cli/commands/run/executor.py +157 -0
- schemathesis/cli/commands/run/filters.py +53 -0
- schemathesis/cli/commands/run/handlers/__init__.py +46 -0
- schemathesis/cli/commands/run/handlers/base.py +18 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +474 -0
- schemathesis/cli/commands/run/handlers/junitxml.py +55 -0
- schemathesis/cli/commands/run/handlers/output.py +1628 -0
- schemathesis/cli/commands/run/loaders.py +114 -0
- schemathesis/cli/commands/run/validation.py +246 -0
- schemathesis/cli/constants.py +5 -58
- schemathesis/cli/core.py +19 -0
- schemathesis/cli/ext/fs.py +16 -0
- schemathesis/cli/ext/groups.py +84 -0
- schemathesis/cli/{options.py → ext/options.py} +36 -34
- schemathesis/config/__init__.py +189 -0
- schemathesis/config/_auth.py +51 -0
- schemathesis/config/_checks.py +268 -0
- schemathesis/config/_diff_base.py +99 -0
- schemathesis/config/_env.py +21 -0
- schemathesis/config/_error.py +156 -0
- schemathesis/config/_generation.py +149 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +327 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +187 -0
- schemathesis/config/_projects.py +527 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +120 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/_warnings.py +25 -0
- schemathesis/config/schema.json +885 -0
- schemathesis/core/__init__.py +67 -0
- schemathesis/core/compat.py +32 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +58 -0
- schemathesis/core/deserialization.py +65 -0
- schemathesis/core/errors.py +459 -0
- schemathesis/core/failures.py +315 -0
- schemathesis/core/fs.py +19 -0
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/loaders.py +104 -0
- schemathesis/core/marks.py +66 -0
- schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
- schemathesis/core/output/__init__.py +46 -0
- schemathesis/core/output/sanitization.py +54 -0
- schemathesis/{throttling.py → core/rate_limit.py} +16 -17
- schemathesis/core/registries.py +31 -0
- schemathesis/core/transforms.py +113 -0
- schemathesis/core/transport.py +223 -0
- schemathesis/core/validation.py +54 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +28 -0
- schemathesis/engine/context.py +118 -0
- schemathesis/engine/control.py +36 -0
- schemathesis/engine/core.py +169 -0
- schemathesis/engine/errors.py +464 -0
- schemathesis/engine/events.py +258 -0
- schemathesis/engine/phases/__init__.py +88 -0
- schemathesis/{runner → engine/phases}/probes.py +52 -68
- schemathesis/engine/phases/stateful/__init__.py +68 -0
- schemathesis/engine/phases/stateful/_executor.py +356 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +212 -0
- schemathesis/engine/phases/unit/_executor.py +416 -0
- schemathesis/engine/phases/unit/_pool.py +82 -0
- schemathesis/engine/recorder.py +247 -0
- schemathesis/errors.py +43 -0
- schemathesis/filters.py +17 -98
- schemathesis/generation/__init__.py +5 -33
- schemathesis/generation/case.py +317 -0
- schemathesis/generation/coverage.py +282 -175
- schemathesis/generation/hypothesis/__init__.py +36 -0
- schemathesis/generation/hypothesis/builder.py +800 -0
- schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
- schemathesis/generation/hypothesis/given.py +66 -0
- schemathesis/generation/hypothesis/reporting.py +14 -0
- schemathesis/generation/hypothesis/strategies.py +16 -0
- schemathesis/generation/meta.py +115 -0
- schemathesis/generation/metrics.py +93 -0
- schemathesis/generation/modes.py +20 -0
- schemathesis/generation/overrides.py +116 -0
- schemathesis/generation/stateful/__init__.py +37 -0
- schemathesis/generation/stateful/state_machine.py +278 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +284 -0
- schemathesis/hooks.py +80 -101
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +455 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +72 -0
- schemathesis/openapi/loaders.py +313 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +281 -0
- schemathesis/pytest/loaders.py +36 -0
- schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +128 -108
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +537 -273
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/_cache.py +1 -2
- schemathesis/specs/graphql/scalars.py +42 -6
- schemathesis/specs/graphql/schemas.py +141 -137
- schemathesis/specs/graphql/validation.py +11 -17
- schemathesis/specs/openapi/__init__.py +6 -1
- schemathesis/specs/openapi/_cache.py +1 -2
- schemathesis/specs/openapi/_hypothesis.py +142 -156
- schemathesis/specs/openapi/checks.py +368 -257
- schemathesis/specs/openapi/converter.py +4 -4
- schemathesis/specs/openapi/definitions.py +1 -1
- schemathesis/specs/openapi/examples.py +23 -21
- schemathesis/specs/openapi/expressions/__init__.py +31 -19
- schemathesis/specs/openapi/expressions/extractors.py +1 -4
- schemathesis/specs/openapi/expressions/lexer.py +1 -1
- schemathesis/specs/openapi/expressions/nodes.py +36 -41
- schemathesis/specs/openapi/expressions/parser.py +1 -1
- schemathesis/specs/openapi/formats.py +35 -7
- schemathesis/specs/openapi/media_types.py +53 -12
- schemathesis/specs/openapi/negative/__init__.py +7 -4
- schemathesis/specs/openapi/negative/mutations.py +6 -5
- schemathesis/specs/openapi/parameters.py +7 -10
- schemathesis/specs/openapi/patterns.py +94 -31
- schemathesis/specs/openapi/references.py +12 -53
- schemathesis/specs/openapi/schemas.py +233 -307
- schemathesis/specs/openapi/security.py +1 -1
- schemathesis/specs/openapi/serialization.py +12 -6
- schemathesis/specs/openapi/stateful/__init__.py +268 -133
- schemathesis/specs/openapi/stateful/control.py +87 -0
- schemathesis/specs/openapi/stateful/links.py +209 -0
- schemathesis/transport/__init__.py +142 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +124 -0
- schemathesis/transport/requests.py +244 -0
- schemathesis/{_xml.py → transport/serialization.py} +69 -11
- schemathesis/transport/wsgi.py +171 -0
- schemathesis-4.0.0.dist-info/METADATA +204 -0
- schemathesis-4.0.0.dist-info/RECORD +164 -0
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/entry_points.txt +1 -1
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/licenses/LICENSE +1 -1
- schemathesis/_compat.py +0 -74
- schemathesis/_dependency_versions.py +0 -19
- schemathesis/_hypothesis.py +0 -717
- schemathesis/_override.py +0 -50
- schemathesis/_patches.py +0 -21
- schemathesis/_rate_limiter.py +0 -7
- schemathesis/cli/callbacks.py +0 -466
- schemathesis/cli/cassettes.py +0 -561
- schemathesis/cli/context.py +0 -75
- schemathesis/cli/debug.py +0 -27
- schemathesis/cli/handlers.py +0 -19
- schemathesis/cli/junitxml.py +0 -124
- schemathesis/cli/output/__init__.py +0 -1
- schemathesis/cli/output/default.py +0 -920
- schemathesis/cli/output/short.py +0 -59
- schemathesis/cli/reporting.py +0 -79
- schemathesis/cli/sanitization.py +0 -26
- schemathesis/code_samples.py +0 -151
- schemathesis/constants.py +0 -54
- schemathesis/contrib/__init__.py +0 -11
- schemathesis/contrib/openapi/__init__.py +0 -11
- schemathesis/contrib/openapi/fill_missing_examples.py +0 -24
- schemathesis/contrib/openapi/formats/__init__.py +0 -9
- schemathesis/contrib/openapi/formats/uuid.py +0 -16
- schemathesis/contrib/unique_data.py +0 -41
- schemathesis/exceptions.py +0 -571
- schemathesis/experimental/__init__.py +0 -109
- schemathesis/extra/_aiohttp.py +0 -28
- schemathesis/extra/_flask.py +0 -13
- schemathesis/extra/_server.py +0 -18
- schemathesis/failures.py +0 -284
- schemathesis/fixups/__init__.py +0 -37
- schemathesis/fixups/fast_api.py +0 -41
- schemathesis/fixups/utf8_bom.py +0 -28
- schemathesis/generation/_methods.py +0 -44
- schemathesis/graphql.py +0 -3
- schemathesis/internal/__init__.py +0 -7
- schemathesis/internal/checks.py +0 -86
- schemathesis/internal/copy.py +0 -32
- schemathesis/internal/datetime.py +0 -5
- schemathesis/internal/deprecation.py +0 -37
- schemathesis/internal/diff.py +0 -15
- schemathesis/internal/extensions.py +0 -27
- schemathesis/internal/jsonschema.py +0 -36
- schemathesis/internal/output.py +0 -68
- schemathesis/internal/transformation.py +0 -26
- schemathesis/internal/validation.py +0 -34
- schemathesis/lazy.py +0 -474
- schemathesis/loaders.py +0 -122
- schemathesis/models.py +0 -1341
- schemathesis/parameters.py +0 -90
- schemathesis/runner/__init__.py +0 -605
- schemathesis/runner/events.py +0 -389
- schemathesis/runner/impl/__init__.py +0 -3
- schemathesis/runner/impl/context.py +0 -88
- schemathesis/runner/impl/core.py +0 -1280
- schemathesis/runner/impl/solo.py +0 -80
- schemathesis/runner/impl/threadpool.py +0 -391
- schemathesis/runner/serialization.py +0 -544
- schemathesis/sanitization.py +0 -252
- schemathesis/serializers.py +0 -328
- schemathesis/service/__init__.py +0 -18
- schemathesis/service/auth.py +0 -11
- schemathesis/service/ci.py +0 -202
- schemathesis/service/client.py +0 -133
- schemathesis/service/constants.py +0 -38
- schemathesis/service/events.py +0 -61
- schemathesis/service/extensions.py +0 -224
- schemathesis/service/hosts.py +0 -111
- schemathesis/service/metadata.py +0 -71
- schemathesis/service/models.py +0 -258
- schemathesis/service/report.py +0 -255
- schemathesis/service/serialization.py +0 -173
- schemathesis/service/usage.py +0 -66
- schemathesis/specs/graphql/loaders.py +0 -364
- schemathesis/specs/openapi/expressions/context.py +0 -16
- schemathesis/specs/openapi/links.py +0 -389
- schemathesis/specs/openapi/loaders.py +0 -707
- schemathesis/specs/openapi/stateful/statistic.py +0 -198
- schemathesis/specs/openapi/stateful/types.py +0 -14
- schemathesis/specs/openapi/validation.py +0 -26
- schemathesis/stateful/__init__.py +0 -147
- schemathesis/stateful/config.py +0 -97
- schemathesis/stateful/context.py +0 -135
- schemathesis/stateful/events.py +0 -274
- schemathesis/stateful/runner.py +0 -309
- schemathesis/stateful/sink.py +0 -68
- schemathesis/stateful/state_machine.py +0 -328
- schemathesis/stateful/statistic.py +0 -22
- schemathesis/stateful/validation.py +0 -100
- schemathesis/targets.py +0 -77
- schemathesis/transports/__init__.py +0 -369
- schemathesis/transports/asgi.py +0 -7
- schemathesis/transports/auth.py +0 -38
- schemathesis/transports/headers.py +0 -36
- schemathesis/transports/responses.py +0 -57
- schemathesis/types.py +0 -44
- schemathesis/utils.py +0 -164
- schemathesis-3.39.16.dist-info/METADATA +0 -293
- schemathesis-3.39.16.dist-info/RECORD +0 -160
- /schemathesis/{extra → cli/ext}/__init__.py +0 -0
- /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
- /schemathesis/{internal → core}/result.py +0 -0
- {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/WHEEL +0 -0
schemathesis/hooks.py
CHANGED
@@ -2,26 +2,26 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import inspect
|
4
4
|
from collections import defaultdict
|
5
|
-
from copy import deepcopy
|
6
5
|
from dataclasses import dataclass, field
|
7
6
|
from enum import Enum, unique
|
8
7
|
from functools import partial
|
9
8
|
from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast
|
10
9
|
|
11
|
-
from .
|
12
|
-
from .
|
10
|
+
from schemathesis.core.marks import Mark
|
11
|
+
from schemathesis.core.transport import Response
|
12
|
+
from schemathesis.filters import FilterSet, attach_filter_chain
|
13
13
|
|
14
14
|
if TYPE_CHECKING:
|
15
15
|
from hypothesis import strategies as st
|
16
16
|
|
17
|
-
from .
|
18
|
-
from .schemas import BaseSchema
|
19
|
-
|
20
|
-
|
17
|
+
from schemathesis.generation.case import Case
|
18
|
+
from schemathesis.schemas import APIOperation, BaseSchema
|
19
|
+
|
20
|
+
HookDispatcherMark = Mark["HookDispatcher"](attr_name="hook_dispatcher")
|
21
21
|
|
22
22
|
|
23
23
|
@unique
|
24
|
-
class HookScope(Enum):
|
24
|
+
class HookScope(int, Enum):
|
25
25
|
GLOBAL = 1
|
26
26
|
SCHEMA = 2
|
27
27
|
TEST = 3
|
@@ -37,17 +37,15 @@ class RegisteredHook:
|
|
37
37
|
|
38
38
|
@dataclass
|
39
39
|
class HookContext:
|
40
|
-
"""A context that is passed to some hook functions.
|
40
|
+
"""A context that is passed to some hook functions."""
|
41
41
|
|
42
|
-
:
|
43
|
-
|
44
|
-
"""
|
42
|
+
operation: APIOperation | None
|
43
|
+
"""API operation that is currently being processed."""
|
45
44
|
|
46
|
-
|
45
|
+
__slots__ = ("operation",)
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
return self.operation
|
47
|
+
def __init__(self, *, operation: APIOperation | None = None) -> None:
|
48
|
+
self.operation = operation
|
51
49
|
|
52
50
|
|
53
51
|
def to_filterable_hook(dispatcher: HookDispatcher) -> Callable:
|
@@ -114,60 +112,29 @@ class HookDispatcher:
|
|
114
112
|
_specs: ClassVar[dict[str, RegisteredHook]] = {}
|
115
113
|
|
116
114
|
def __post_init__(self) -> None:
|
117
|
-
self.
|
118
|
-
|
119
|
-
def register(self, hook: str | Callable) -> Callable:
|
120
|
-
"""Register a new hook.
|
121
|
-
|
122
|
-
:param hook: Either a hook function or a string.
|
123
|
-
|
124
|
-
Can be used as a decorator in two forms.
|
125
|
-
Without arguments for registering hooks and autodetecting their names:
|
126
|
-
|
127
|
-
.. code-block:: python
|
115
|
+
self.hook = to_filterable_hook(self) # type: ignore[method-assign]
|
128
116
|
|
129
|
-
|
130
|
-
def before_generate_query(context, strategy):
|
131
|
-
...
|
132
|
-
|
133
|
-
With a hook name as the first argument:
|
134
|
-
|
135
|
-
.. code-block:: python
|
136
|
-
|
137
|
-
@schemathesis.hook("before_generate_query")
|
138
|
-
def hook(context, strategy):
|
139
|
-
...
|
140
|
-
"""
|
117
|
+
def hook(self, hook: str | Callable) -> Callable:
|
141
118
|
raise NotImplementedError
|
142
119
|
|
143
|
-
def merge(self, other: HookDispatcher) -> HookDispatcher:
|
144
|
-
"""Merge two dispatches together.
|
145
|
-
|
146
|
-
The resulting dispatcher will call the `self` hooks first.
|
147
|
-
"""
|
148
|
-
all_hooks = deepcopy(self._hooks)
|
149
|
-
for name, hooks in other._hooks.items():
|
150
|
-
all_hooks[name].extend(hooks)
|
151
|
-
instance = self.__class__(scope=self.scope)
|
152
|
-
instance._hooks = all_hooks
|
153
|
-
return instance
|
154
|
-
|
155
120
|
def apply(self, hook: Callable, *, name: str | None = None) -> Callable[[Callable], Callable]:
|
156
121
|
"""Register hook to run only on one test function.
|
157
122
|
|
158
|
-
:
|
159
|
-
|
160
|
-
|
161
|
-
.. code-block:: python
|
123
|
+
Args:
|
124
|
+
hook: A hook function.
|
125
|
+
name: A hook name.
|
162
126
|
|
163
|
-
|
127
|
+
Example:
|
128
|
+
```python
|
129
|
+
def filter_query(ctx, value):
|
164
130
|
...
|
165
131
|
|
166
132
|
|
167
|
-
@schema.hooks.apply(
|
133
|
+
@schema.hooks.apply(filter_query)
|
168
134
|
@schema.parametrize()
|
169
135
|
def test_api(case):
|
170
136
|
...
|
137
|
+
```
|
171
138
|
|
172
139
|
"""
|
173
140
|
if name is None:
|
@@ -175,7 +142,7 @@ class HookDispatcher:
|
|
175
142
|
else:
|
176
143
|
hook_name = name
|
177
144
|
|
178
|
-
def decorator(func:
|
145
|
+
def decorator(func: Callable) -> Callable:
|
179
146
|
dispatcher = self.add_dispatcher(func)
|
180
147
|
dispatcher.register_hook_with_name(hook, hook_name)
|
181
148
|
return func
|
@@ -183,11 +150,13 @@ class HookDispatcher:
|
|
183
150
|
return decorator
|
184
151
|
|
185
152
|
@classmethod
|
186
|
-
def add_dispatcher(cls, func:
|
153
|
+
def add_dispatcher(cls, func: Callable) -> HookDispatcher:
|
187
154
|
"""Attach a new dispatcher instance to the test if it is not already present."""
|
188
|
-
if not
|
189
|
-
func
|
190
|
-
|
155
|
+
if not HookDispatcherMark.is_set(func):
|
156
|
+
HookDispatcherMark.set(func, cls(scope=HookScope.TEST))
|
157
|
+
dispatcher = HookDispatcherMark.get(func)
|
158
|
+
assert dispatcher is not None
|
159
|
+
return dispatcher
|
191
160
|
|
192
161
|
def register_hook_with_name(self, hook: Callable, name: str) -> Callable:
|
193
162
|
"""A helper for hooks registration."""
|
@@ -226,19 +195,10 @@ class HookDispatcher:
|
|
226
195
|
f"Hook '{name}' takes {len(spec.signature.parameters)} arguments but {len(signature.parameters)} is defined"
|
227
196
|
)
|
228
197
|
|
229
|
-
def collect_statistic(self) -> dict[str, int]:
|
230
|
-
return {name: len(hooks) for name, hooks in self._hooks.items()}
|
231
|
-
|
232
198
|
def get_all_by_name(self, name: str) -> list[Callable]:
|
233
199
|
"""Get a list of hooks registered for a name."""
|
234
200
|
return self._hooks.get(name, [])
|
235
201
|
|
236
|
-
def is_installed(self, name: str, needle: Callable) -> bool:
|
237
|
-
for hook in self.get_all_by_name(name):
|
238
|
-
if hook is needle:
|
239
|
-
return True
|
240
|
-
return False
|
241
|
-
|
242
202
|
def apply_to_container(
|
243
203
|
self, strategy: st.SearchStrategy, container: str, context: HookContext
|
244
204
|
) -> st.SearchStrategy:
|
@@ -271,10 +231,7 @@ class HookDispatcher:
|
|
271
231
|
hook(context, *args, **kwargs)
|
272
232
|
|
273
233
|
def unregister(self, hook: Callable) -> None:
|
274
|
-
"""Unregister a specific hook.
|
275
|
-
|
276
|
-
:param hook: A hook function to unregister.
|
277
|
-
"""
|
234
|
+
"""Unregister a specific hook."""
|
278
235
|
# It removes this function from all places
|
279
236
|
for hooks in self._hooks.values():
|
280
237
|
hooks[:] = [item for item in hooks if item is not hook]
|
@@ -307,19 +264,12 @@ def apply_to_all_dispatchers(
|
|
307
264
|
return strategy
|
308
265
|
|
309
266
|
|
310
|
-
def should_skip_operation(dispatcher: HookDispatcher, context: HookContext) -> bool:
|
311
|
-
for hook in dispatcher.get_all_by_name("filter_operations"):
|
312
|
-
if not hook(context):
|
313
|
-
return True
|
314
|
-
return False
|
315
|
-
|
316
|
-
|
317
267
|
def validate_filterable_hook(hook: str | Callable) -> None:
|
318
268
|
if callable(hook):
|
319
269
|
name = hook.__name__
|
320
270
|
else:
|
321
271
|
name = hook
|
322
|
-
if name in ("before_process_path", "before_load_schema", "after_load_schema"
|
272
|
+
if name in ("before_process_path", "before_load_schema", "after_load_schema"):
|
323
273
|
raise ValueError(f"Filters are not applicable to this hook: `{name}`")
|
324
274
|
|
325
275
|
|
@@ -373,11 +323,6 @@ def before_process_path(context: HookContext, path: str, methods: dict[str, Any]
|
|
373
323
|
"""Called before API path is processed."""
|
374
324
|
|
375
325
|
|
376
|
-
@all_scopes
|
377
|
-
def filter_operations(context: HookContext) -> bool | None:
|
378
|
-
"""Decide whether testing of this particular API operation should be skipped or not."""
|
379
|
-
|
380
|
-
|
381
326
|
@HookDispatcher.register_spec([HookScope.GLOBAL])
|
382
327
|
def before_load_schema(context: HookContext, raw_schema: dict[str, Any]) -> None:
|
383
328
|
"""Called before schema instance is created."""
|
@@ -402,15 +347,7 @@ def before_init_operation(context: HookContext, operation: APIOperation) -> None
|
|
402
347
|
|
403
348
|
|
404
349
|
@HookDispatcher.register_spec([HookScope.GLOBAL])
|
405
|
-
def
|
406
|
-
"""Creates an additional test per API operation. If this hook returns None, no additional test created.
|
407
|
-
|
408
|
-
Called with a copy of the original case object and the server's response to the original case.
|
409
|
-
"""
|
410
|
-
|
411
|
-
|
412
|
-
@HookDispatcher.register_spec([HookScope.GLOBAL])
|
413
|
-
def before_call(context: HookContext, case: Case) -> None:
|
350
|
+
def before_call(context: HookContext, case: Case, **kwargs: Any) -> None:
|
414
351
|
"""Called before every network call in CLI tests.
|
415
352
|
|
416
353
|
Use cases:
|
@@ -420,7 +357,7 @@ def before_call(context: HookContext, case: Case) -> None:
|
|
420
357
|
|
421
358
|
|
422
359
|
@HookDispatcher.register_spec([HookScope.GLOBAL])
|
423
|
-
def after_call(context: HookContext, case: Case, response:
|
360
|
+
def after_call(context: HookContext, case: Case, response: Response) -> None:
|
424
361
|
"""Called after every network call in CLI tests.
|
425
362
|
|
426
363
|
Note that you need to modify the response in-place.
|
@@ -434,8 +371,50 @@ def after_call(context: HookContext, case: Case, response: GenericResponse) -> N
|
|
434
371
|
GLOBAL_HOOK_DISPATCHER = HookDispatcher(scope=HookScope.GLOBAL)
|
435
372
|
dispatch = GLOBAL_HOOK_DISPATCHER.dispatch
|
436
373
|
get_all_by_name = GLOBAL_HOOK_DISPATCHER.get_all_by_name
|
437
|
-
is_installed = GLOBAL_HOOK_DISPATCHER.is_installed
|
438
|
-
collect_statistic = GLOBAL_HOOK_DISPATCHER.collect_statistic
|
439
|
-
register = GLOBAL_HOOK_DISPATCHER.register
|
440
374
|
unregister = GLOBAL_HOOK_DISPATCHER.unregister
|
441
375
|
unregister_all = GLOBAL_HOOK_DISPATCHER.unregister_all
|
376
|
+
|
377
|
+
|
378
|
+
def hook(hook: str | Callable) -> Callable:
|
379
|
+
"""Register a new hook.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
hook: Either a hook function (autodetecting its name) or a string matching one of the supported hook names.
|
383
|
+
|
384
|
+
Example:
|
385
|
+
Can be used as a decorator in two ways:
|
386
|
+
|
387
|
+
1. Without arguments (auto-detect the hook name from the function name):
|
388
|
+
|
389
|
+
```python
|
390
|
+
@schemathesis.hook
|
391
|
+
def filter_query(ctx, query):
|
392
|
+
\"\"\"Skip cases where query is None or invalid\"\"\"
|
393
|
+
return query and "user_id" in query
|
394
|
+
|
395
|
+
@schemathesis.hook
|
396
|
+
def before_call(ctx, case):
|
397
|
+
\"\"\"Modify headers before sending each request\"\"\"
|
398
|
+
if case.headers is None:
|
399
|
+
case.headers = {}
|
400
|
+
case.headers["X-Test-Mode"] = "true"
|
401
|
+
return None
|
402
|
+
```
|
403
|
+
|
404
|
+
2. With an explicit hook name as the first argument:
|
405
|
+
|
406
|
+
```python
|
407
|
+
@schemathesis.hook("map_headers")
|
408
|
+
def add_custom_header(ctx, headers):
|
409
|
+
\"\"\"Inject a test header into every request\"\"\"
|
410
|
+
if headers is None:
|
411
|
+
headers = {}
|
412
|
+
headers["X-Custom"] = "value"
|
413
|
+
return headers
|
414
|
+
```
|
415
|
+
|
416
|
+
"""
|
417
|
+
return GLOBAL_HOOK_DISPATCHER.hook(hook)
|
418
|
+
|
419
|
+
|
420
|
+
hook.__dict__ = GLOBAL_HOOK_DISPATCHER.hook.__dict__
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from schemathesis.openapi.loaders import from_asgi, from_dict, from_file, from_path, from_url, from_wsgi
|
2
|
+
from schemathesis.specs.openapi import format, media_type
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"from_url",
|
6
|
+
"from_asgi",
|
7
|
+
"from_wsgi",
|
8
|
+
"from_file",
|
9
|
+
"from_path",
|
10
|
+
"from_dict",
|
11
|
+
"format",
|
12
|
+
"media_type",
|
13
|
+
]
|