schemathesis 3.29.2__py3-none-any.whl → 3.30.1__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 +3 -3
- schemathesis/_compat.py +2 -2
- schemathesis/_dependency_versions.py +1 -3
- schemathesis/_hypothesis.py +6 -0
- schemathesis/_lazy_import.py +1 -0
- schemathesis/_override.py +1 -0
- schemathesis/_rate_limiter.py +2 -1
- schemathesis/_xml.py +1 -0
- schemathesis/auths.py +4 -2
- schemathesis/checks.py +8 -5
- schemathesis/cli/__init__.py +28 -1
- schemathesis/cli/callbacks.py +3 -4
- schemathesis/cli/cassettes.py +6 -4
- schemathesis/cli/constants.py +2 -0
- schemathesis/cli/context.py +5 -0
- schemathesis/cli/debug.py +2 -1
- schemathesis/cli/handlers.py +1 -1
- schemathesis/cli/junitxml.py +5 -4
- schemathesis/cli/options.py +1 -0
- schemathesis/cli/output/default.py +56 -24
- schemathesis/cli/output/short.py +21 -10
- schemathesis/cli/sanitization.py +1 -0
- schemathesis/code_samples.py +1 -0
- schemathesis/constants.py +1 -0
- schemathesis/contrib/openapi/__init__.py +1 -1
- schemathesis/contrib/openapi/fill_missing_examples.py +2 -0
- schemathesis/contrib/openapi/formats/uuid.py +2 -1
- schemathesis/contrib/unique_data.py +2 -1
- schemathesis/exceptions.py +42 -61
- schemathesis/experimental/__init__.py +14 -0
- schemathesis/extra/_aiohttp.py +1 -0
- schemathesis/extra/_server.py +1 -0
- schemathesis/extra/pytest_plugin.py +13 -24
- schemathesis/failures.py +42 -8
- schemathesis/filters.py +2 -1
- schemathesis/fixups/__init__.py +1 -0
- schemathesis/fixups/fast_api.py +2 -2
- schemathesis/fixups/utf8_bom.py +1 -2
- schemathesis/generation/__init__.py +2 -1
- schemathesis/hooks.py +3 -1
- schemathesis/internal/copy.py +19 -3
- schemathesis/internal/deprecation.py +1 -1
- schemathesis/internal/jsonschema.py +2 -1
- schemathesis/internal/output.py +68 -0
- schemathesis/internal/result.py +1 -1
- schemathesis/internal/transformation.py +1 -0
- schemathesis/lazy.py +11 -2
- schemathesis/loaders.py +4 -2
- schemathesis/models.py +22 -7
- schemathesis/parameters.py +1 -0
- schemathesis/runner/__init__.py +1 -1
- schemathesis/runner/events.py +22 -4
- schemathesis/runner/impl/core.py +69 -33
- schemathesis/runner/impl/solo.py +2 -1
- schemathesis/runner/impl/threadpool.py +4 -0
- schemathesis/runner/probes.py +1 -1
- schemathesis/runner/serialization.py +1 -1
- schemathesis/sanitization.py +2 -0
- schemathesis/schemas.py +7 -4
- schemathesis/service/ci.py +1 -0
- schemathesis/service/client.py +7 -7
- schemathesis/service/events.py +2 -1
- schemathesis/service/extensions.py +5 -5
- schemathesis/service/hosts.py +1 -0
- schemathesis/service/metadata.py +2 -1
- schemathesis/service/models.py +2 -1
- schemathesis/service/report.py +3 -3
- schemathesis/service/serialization.py +62 -23
- schemathesis/service/usage.py +1 -0
- schemathesis/specs/graphql/_cache.py +1 -1
- schemathesis/specs/graphql/loaders.py +17 -1
- schemathesis/specs/graphql/nodes.py +1 -0
- schemathesis/specs/graphql/scalars.py +2 -2
- schemathesis/specs/graphql/schemas.py +7 -7
- schemathesis/specs/graphql/validation.py +1 -2
- schemathesis/specs/openapi/_hypothesis.py +17 -11
- schemathesis/specs/openapi/checks.py +102 -9
- schemathesis/specs/openapi/converter.py +2 -1
- schemathesis/specs/openapi/definitions.py +2 -1
- schemathesis/specs/openapi/examples.py +7 -9
- schemathesis/specs/openapi/expressions/__init__.py +29 -2
- schemathesis/specs/openapi/expressions/context.py +1 -1
- schemathesis/specs/openapi/expressions/extractors.py +23 -0
- schemathesis/specs/openapi/expressions/lexer.py +19 -18
- schemathesis/specs/openapi/expressions/nodes.py +24 -4
- schemathesis/specs/openapi/expressions/parser.py +26 -5
- schemathesis/specs/openapi/filters.py +1 -0
- schemathesis/specs/openapi/links.py +35 -7
- schemathesis/specs/openapi/loaders.py +31 -11
- schemathesis/specs/openapi/negative/__init__.py +2 -1
- schemathesis/specs/openapi/negative/mutations.py +1 -0
- schemathesis/specs/openapi/parameters.py +1 -0
- schemathesis/specs/openapi/schemas.py +28 -39
- schemathesis/specs/openapi/security.py +1 -0
- schemathesis/specs/openapi/serialization.py +1 -0
- schemathesis/specs/openapi/stateful/__init__.py +159 -70
- schemathesis/specs/openapi/stateful/statistic.py +198 -0
- schemathesis/specs/openapi/stateful/types.py +13 -0
- schemathesis/specs/openapi/utils.py +1 -0
- schemathesis/specs/openapi/validation.py +1 -0
- schemathesis/stateful/__init__.py +4 -2
- schemathesis/stateful/config.py +66 -0
- schemathesis/stateful/context.py +103 -0
- schemathesis/stateful/events.py +215 -0
- schemathesis/stateful/runner.py +238 -0
- schemathesis/stateful/sink.py +68 -0
- schemathesis/stateful/state_machine.py +39 -22
- schemathesis/stateful/statistic.py +20 -0
- schemathesis/stateful/validation.py +66 -0
- schemathesis/targets.py +1 -0
- schemathesis/throttling.py +23 -3
- schemathesis/transports/__init__.py +28 -10
- schemathesis/transports/auth.py +1 -0
- schemathesis/transports/content_types.py +1 -1
- schemathesis/transports/headers.py +2 -1
- schemathesis/transports/responses.py +6 -4
- schemathesis/types.py +1 -0
- schemathesis/utils.py +1 -0
- {schemathesis-3.29.2.dist-info → schemathesis-3.30.1.dist-info}/METADATA +3 -3
- schemathesis-3.30.1.dist-info/RECORD +151 -0
- schemathesis/specs/openapi/stateful/links.py +0 -92
- schemathesis-3.29.2.dist-info/RECORD +0 -141
- {schemathesis-3.29.2.dist-info → schemathesis-3.30.1.dist-info}/WHEEL +0 -0
- {schemathesis-3.29.2.dist-info → schemathesis-3.30.1.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.29.2.dist-info → schemathesis-3.30.1.dist-info}/licenses/LICENSE +0 -0
schemathesis/service/client.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import hashlib
|
|
4
4
|
import http
|
|
5
|
+
import json
|
|
5
6
|
from dataclasses import asdict
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
7
8
|
from urllib.parse import urljoin
|
|
8
9
|
|
|
9
10
|
import requests
|
|
@@ -14,19 +15,18 @@ from .ci import CIProvider
|
|
|
14
15
|
from .constants import CI_PROVIDER_HEADER, REPORT_CORRELATION_ID_HEADER, REQUEST_TIMEOUT, UPLOAD_SOURCE_HEADER
|
|
15
16
|
from .metadata import Metadata, collect_dependency_versions
|
|
16
17
|
from .models import (
|
|
17
|
-
AnalysisSuccess,
|
|
18
18
|
AnalysisError,
|
|
19
19
|
AnalysisResult,
|
|
20
|
-
|
|
20
|
+
AnalysisSuccess,
|
|
21
21
|
AuthResponse,
|
|
22
22
|
FailedUploadResponse,
|
|
23
|
-
|
|
24
|
-
UploadSource,
|
|
23
|
+
ProjectDetails,
|
|
25
24
|
ProjectEnvironment,
|
|
26
25
|
Specification,
|
|
26
|
+
UploadResponse,
|
|
27
|
+
UploadSource,
|
|
27
28
|
)
|
|
28
29
|
|
|
29
|
-
|
|
30
30
|
if TYPE_CHECKING:
|
|
31
31
|
from ..runner import probes
|
|
32
32
|
|
schemathesis/service/events.py
CHANGED
|
@@ -3,17 +3,17 @@ from __future__ import annotations
|
|
|
3
3
|
import base64
|
|
4
4
|
import re
|
|
5
5
|
from ipaddress import IPv4Network, IPv6Network
|
|
6
|
-
from typing import TYPE_CHECKING, Callable, Optional
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
7
7
|
|
|
8
8
|
from ..graphql import nodes
|
|
9
|
-
from ..internal.result import
|
|
9
|
+
from ..internal.result import Err, Ok, Result
|
|
10
10
|
from .models import (
|
|
11
11
|
Extension,
|
|
12
|
-
SchemaPatchesExtension,
|
|
13
|
-
StrategyDefinition,
|
|
14
|
-
OpenApiStringFormatsExtension,
|
|
15
12
|
GraphQLScalarsExtension,
|
|
16
13
|
MediaTypesExtension,
|
|
14
|
+
OpenApiStringFormatsExtension,
|
|
15
|
+
SchemaPatchesExtension,
|
|
16
|
+
StrategyDefinition,
|
|
17
17
|
TransformFunctionDefinition,
|
|
18
18
|
)
|
|
19
19
|
|
schemathesis/service/hosts.py
CHANGED
schemathesis/service/metadata.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""Useful info to collect from CLI usage."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
+
|
|
4
5
|
import os
|
|
5
6
|
import platform
|
|
6
|
-
from importlib import metadata
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
+
from importlib import metadata
|
|
8
9
|
|
|
9
10
|
from ..constants import SCHEMATHESIS_VERSION
|
|
10
11
|
from .constants import DOCKER_IMAGE_ENV_VAR
|
schemathesis/service/models.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from dataclasses import dataclass, field
|
|
3
4
|
from enum import Enum
|
|
4
|
-
from typing import Any, Iterable, TypedDict, Union
|
|
5
|
+
from typing import Any, Iterable, Literal, TypedDict, Union
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class UploadSource(str, Enum):
|
schemathesis/service/report.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import enum
|
|
3
4
|
import json
|
|
4
5
|
import os
|
|
@@ -9,7 +10,7 @@ from contextlib import suppress
|
|
|
9
10
|
from dataclasses import asdict, dataclass, field
|
|
10
11
|
from io import BytesIO
|
|
11
12
|
from queue import Queue
|
|
12
|
-
from typing import
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
13
14
|
|
|
14
15
|
import click
|
|
15
16
|
|
|
@@ -22,11 +23,10 @@ from .metadata import Metadata
|
|
|
22
23
|
from .models import UploadResponse
|
|
23
24
|
from .serialization import serialize_event
|
|
24
25
|
|
|
25
|
-
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from .client import ServiceClient
|
|
28
27
|
from ..cli.context import ExecutionContext
|
|
29
28
|
from ..runner.events import ExecutionEvent
|
|
29
|
+
from .client import ServiceClient
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@dataclass
|
|
@@ -7,9 +7,10 @@ from ..exceptions import format_exception
|
|
|
7
7
|
from ..internal.result import Err, Ok
|
|
8
8
|
from ..internal.transformation import merge_recursively
|
|
9
9
|
from ..models import Response
|
|
10
|
-
from .models import AnalysisSuccess
|
|
11
10
|
from ..runner import events
|
|
12
|
-
from ..runner.serialization import SerializedCase
|
|
11
|
+
from ..runner.serialization import SerializedCase, SerializedCheck
|
|
12
|
+
from ..stateful import events as stateful_events
|
|
13
|
+
from .models import AnalysisSuccess
|
|
13
14
|
|
|
14
15
|
S = TypeVar("S", bound=events.ExecutionEvent)
|
|
15
16
|
SerializeFunc = Callable[[S], Optional[Dict[str, Any]]]
|
|
@@ -81,6 +82,27 @@ def _serialize_response(response: Response) -> dict[str, Any]:
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
def _serialize_check(check: SerializedCheck) -> dict[str, Any]:
|
|
86
|
+
return {
|
|
87
|
+
"name": check.name,
|
|
88
|
+
"value": check.value,
|
|
89
|
+
"request": {
|
|
90
|
+
"method": check.request.method,
|
|
91
|
+
"uri": check.request.uri,
|
|
92
|
+
"body": check.request.body,
|
|
93
|
+
"headers": check.request.headers,
|
|
94
|
+
},
|
|
95
|
+
"response": _serialize_response(check.response) if check.response is not None else None,
|
|
96
|
+
"example": _serialize_case(check.example),
|
|
97
|
+
"message": check.message,
|
|
98
|
+
"context": asdict(check.context) if check.context is not None else None, # type: ignore
|
|
99
|
+
"history": [
|
|
100
|
+
{"case": _serialize_case(entry.case), "response": _serialize_response(entry.response)}
|
|
101
|
+
for entry in check.history
|
|
102
|
+
],
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
84
106
|
def serialize_after_execution(event: events.AfterExecution) -> dict[str, Any] | None:
|
|
85
107
|
return {
|
|
86
108
|
"correlation_id": event.correlation_id,
|
|
@@ -89,27 +111,7 @@ def serialize_after_execution(event: events.AfterExecution) -> dict[str, Any] |
|
|
|
89
111
|
"elapsed_time": event.elapsed_time,
|
|
90
112
|
"data_generation_method": event.data_generation_method,
|
|
91
113
|
"result": {
|
|
92
|
-
"checks": [
|
|
93
|
-
{
|
|
94
|
-
"name": check.name,
|
|
95
|
-
"value": check.value,
|
|
96
|
-
"request": {
|
|
97
|
-
"method": check.request.method,
|
|
98
|
-
"uri": check.request.uri,
|
|
99
|
-
"body": check.request.body,
|
|
100
|
-
"headers": check.request.headers,
|
|
101
|
-
},
|
|
102
|
-
"response": _serialize_response(check.response) if check.response is not None else None,
|
|
103
|
-
"example": _serialize_case(check.example),
|
|
104
|
-
"message": check.message,
|
|
105
|
-
"context": asdict(check.context) if check.context is not None else None, # type: ignore
|
|
106
|
-
"history": [
|
|
107
|
-
{"case": _serialize_case(entry.case), "response": _serialize_response(entry.response)}
|
|
108
|
-
for entry in check.history
|
|
109
|
-
],
|
|
110
|
-
}
|
|
111
|
-
for check in event.result.checks
|
|
112
|
-
],
|
|
114
|
+
"checks": [_serialize_check(check) for check in event.result.checks],
|
|
113
115
|
"errors": [asdict(error) for error in event.result.errors],
|
|
114
116
|
"skip_reason": event.result.skip_reason,
|
|
115
117
|
},
|
|
@@ -147,6 +149,37 @@ def serialize_finished(event: events.Finished) -> dict[str, Any] | None:
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
|
|
152
|
+
def serialize_stateful_event(event: events.StatefulEvent) -> dict[str, Any] | None:
|
|
153
|
+
return _serialize_stateful_event(event.data)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _serialize_stateful_event(event: stateful_events.StatefulEvent) -> dict[str, Any] | None:
|
|
157
|
+
data: dict[str, Any]
|
|
158
|
+
if isinstance(event, stateful_events.RunStarted):
|
|
159
|
+
data = {
|
|
160
|
+
"timestamp": event.timestamp,
|
|
161
|
+
"started_at": event.started_at,
|
|
162
|
+
}
|
|
163
|
+
elif isinstance(event, stateful_events.SuiteFinished):
|
|
164
|
+
data = {
|
|
165
|
+
"timestamp": event.timestamp,
|
|
166
|
+
"status": event.status,
|
|
167
|
+
"failures": [_serialize_check(SerializedCheck.from_check(failure)) for failure in event.failures],
|
|
168
|
+
}
|
|
169
|
+
elif isinstance(event, stateful_events.Errored):
|
|
170
|
+
data = {
|
|
171
|
+
"timestamp": event.timestamp,
|
|
172
|
+
"exception": format_exception(event.exception, True),
|
|
173
|
+
}
|
|
174
|
+
else:
|
|
175
|
+
data = asdict(event)
|
|
176
|
+
return {"data": {event.__class__.__name__: data}}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def serialize_after_stateful_execution(event: events.AfterStatefulExecution) -> dict[str, Any] | None:
|
|
180
|
+
return {"result": asdict(event.result)}
|
|
181
|
+
|
|
182
|
+
|
|
150
183
|
SERIALIZER_MAP = {
|
|
151
184
|
events.Initialized: serialize_initialized,
|
|
152
185
|
events.BeforeProbing: serialize_before_probing,
|
|
@@ -157,6 +190,8 @@ SERIALIZER_MAP = {
|
|
|
157
190
|
events.AfterExecution: serialize_after_execution,
|
|
158
191
|
events.Interrupted: serialize_interrupted,
|
|
159
192
|
events.InternalError: serialize_internal_error,
|
|
193
|
+
events.StatefulEvent: serialize_stateful_event,
|
|
194
|
+
events.AfterStatefulExecution: serialize_after_stateful_execution,
|
|
160
195
|
events.Finished: serialize_finished,
|
|
161
196
|
}
|
|
162
197
|
|
|
@@ -173,6 +208,8 @@ def serialize_event(
|
|
|
173
208
|
on_after_execution: SerializeFunc | None = None,
|
|
174
209
|
on_interrupted: SerializeFunc | None = None,
|
|
175
210
|
on_internal_error: SerializeFunc | None = None,
|
|
211
|
+
on_stateful_event: SerializeFunc | None = None,
|
|
212
|
+
on_after_stateful_execution: SerializeFunc | None = None,
|
|
176
213
|
on_finished: SerializeFunc | None = None,
|
|
177
214
|
extra: dict[str, Any] | None = None,
|
|
178
215
|
) -> dict[str, dict[str, Any] | None]:
|
|
@@ -188,6 +225,8 @@ def serialize_event(
|
|
|
188
225
|
events.AfterExecution: on_after_execution,
|
|
189
226
|
events.Interrupted: on_interrupted,
|
|
190
227
|
events.InternalError: on_internal_error,
|
|
228
|
+
events.StatefulEvent: on_stateful_event,
|
|
229
|
+
events.AfterStatefulExecution: on_after_stateful_execution,
|
|
191
230
|
events.Finished: on_finished,
|
|
192
231
|
}.get(event.__class__)
|
|
193
232
|
if serializer is None:
|
schemathesis/service/usage.py
CHANGED
|
@@ -16,6 +16,7 @@ from ...generation import (
|
|
|
16
16
|
GenerationConfig,
|
|
17
17
|
)
|
|
18
18
|
from ...hooks import HookContext, dispatch
|
|
19
|
+
from ...internal.output import OutputConfig
|
|
19
20
|
from ...internal.validation import require_relative_url
|
|
20
21
|
from ...loaders import load_schema_from_url
|
|
21
22
|
from ...throttling import build_limiter
|
|
@@ -52,6 +53,7 @@ def from_path(
|
|
|
52
53
|
base_url: str | None = None,
|
|
53
54
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
54
55
|
generation_config: GenerationConfig | None = None,
|
|
56
|
+
output_config: OutputConfig | None = None,
|
|
55
57
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
56
58
|
rate_limit: str | None = None,
|
|
57
59
|
encoding: str = "utf8",
|
|
@@ -69,6 +71,8 @@ def from_path(
|
|
|
69
71
|
base_url=base_url,
|
|
70
72
|
data_generation_methods=data_generation_methods,
|
|
71
73
|
code_sample_style=code_sample_style,
|
|
74
|
+
generation_config=generation_config,
|
|
75
|
+
output_config=output_config,
|
|
72
76
|
location=pathlib.Path(path).absolute().as_uri(),
|
|
73
77
|
rate_limit=rate_limit,
|
|
74
78
|
sanitize_output=sanitize_output,
|
|
@@ -161,6 +165,7 @@ def from_file(
|
|
|
161
165
|
base_url: str | None = None,
|
|
162
166
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
163
167
|
generation_config: GenerationConfig | None = None,
|
|
168
|
+
output_config: OutputConfig | None = None,
|
|
164
169
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
165
170
|
location: str | None = None,
|
|
166
171
|
rate_limit: str | None = None,
|
|
@@ -198,6 +203,8 @@ def from_file(
|
|
|
198
203
|
app=app,
|
|
199
204
|
base_url=base_url,
|
|
200
205
|
data_generation_methods=data_generation_methods,
|
|
206
|
+
generation_config=generation_config,
|
|
207
|
+
output_config=output_config,
|
|
201
208
|
code_sample_style=code_sample_style,
|
|
202
209
|
location=location,
|
|
203
210
|
rate_limit=rate_limit,
|
|
@@ -221,6 +228,7 @@ def from_dict(
|
|
|
221
228
|
location: str | None = None,
|
|
222
229
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
223
230
|
generation_config: GenerationConfig | None = None,
|
|
231
|
+
output_config: OutputConfig | None = None,
|
|
224
232
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
225
233
|
rate_limit: str | None = None,
|
|
226
234
|
sanitize_output: bool = True,
|
|
@@ -233,8 +241,8 @@ def from_dict(
|
|
|
233
241
|
:param app: A WSGI app instance.
|
|
234
242
|
:return: GraphQLSchema
|
|
235
243
|
"""
|
|
236
|
-
from .schemas import GraphQLSchema
|
|
237
244
|
from ... import transports
|
|
245
|
+
from .schemas import GraphQLSchema
|
|
238
246
|
|
|
239
247
|
_code_sample_style = CodeSampleStyle.from_str(code_sample_style)
|
|
240
248
|
hook_context = HookContext()
|
|
@@ -250,6 +258,8 @@ def from_dict(
|
|
|
250
258
|
base_url=base_url,
|
|
251
259
|
app=app,
|
|
252
260
|
data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
|
|
261
|
+
generation_config=generation_config or GenerationConfig(),
|
|
262
|
+
output_config=output_config or OutputConfig(),
|
|
253
263
|
code_sample_style=_code_sample_style,
|
|
254
264
|
rate_limiter=rate_limiter,
|
|
255
265
|
sanitize_output=sanitize_output,
|
|
@@ -266,6 +276,7 @@ def from_wsgi(
|
|
|
266
276
|
base_url: str | None = None,
|
|
267
277
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
268
278
|
generation_config: GenerationConfig | None = None,
|
|
279
|
+
output_config: OutputConfig | None = None,
|
|
269
280
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
270
281
|
rate_limit: str | None = None,
|
|
271
282
|
sanitize_output: bool = True,
|
|
@@ -294,6 +305,8 @@ def from_wsgi(
|
|
|
294
305
|
base_url=base_url,
|
|
295
306
|
app=app,
|
|
296
307
|
data_generation_methods=data_generation_methods,
|
|
308
|
+
generation_config=generation_config,
|
|
309
|
+
output_config=output_config,
|
|
297
310
|
code_sample_style=code_sample_style,
|
|
298
311
|
rate_limit=rate_limit,
|
|
299
312
|
sanitize_output=sanitize_output,
|
|
@@ -307,6 +320,7 @@ def from_asgi(
|
|
|
307
320
|
base_url: str | None = None,
|
|
308
321
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
309
322
|
generation_config: GenerationConfig | None = None,
|
|
323
|
+
output_config: OutputConfig | None = None,
|
|
310
324
|
code_sample_style: str = CodeSampleStyle.default().name,
|
|
311
325
|
rate_limit: str | None = None,
|
|
312
326
|
sanitize_output: bool = True,
|
|
@@ -332,6 +346,8 @@ def from_asgi(
|
|
|
332
346
|
base_url=base_url,
|
|
333
347
|
app=app,
|
|
334
348
|
data_generation_methods=data_generation_methods,
|
|
349
|
+
generation_config=generation_config,
|
|
350
|
+
output_config=output_config,
|
|
335
351
|
code_sample_style=code_sample_style,
|
|
336
352
|
rate_limit=rate_limit,
|
|
337
353
|
sanitize_output=sanitize_output,
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
from functools import lru_cache
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
from ...exceptions import UsageError
|
|
8
7
|
|
|
9
8
|
if TYPE_CHECKING:
|
|
@@ -31,9 +30,10 @@ def scalar(name: str, strategy: st.SearchStrategy[graphql.ValueNode]) -> None:
|
|
|
31
30
|
@lru_cache
|
|
32
31
|
def get_extra_scalar_strategies() -> dict[str, st.SearchStrategy]:
|
|
33
32
|
"""Get all extra GraphQL strategies."""
|
|
34
|
-
from . import nodes
|
|
35
33
|
from hypothesis import strategies as st
|
|
36
34
|
|
|
35
|
+
from . import nodes
|
|
36
|
+
|
|
37
37
|
dates = st.dates().map(str)
|
|
38
38
|
times = st.times().map("%sZ".__mod__)
|
|
39
39
|
|
|
@@ -6,16 +6,16 @@ from dataclasses import dataclass, field
|
|
|
6
6
|
from difflib import get_close_matches
|
|
7
7
|
from enum import unique
|
|
8
8
|
from typing import (
|
|
9
|
+
TYPE_CHECKING,
|
|
9
10
|
Any,
|
|
10
11
|
Callable,
|
|
11
12
|
Generator,
|
|
13
|
+
Iterator,
|
|
14
|
+
Mapping,
|
|
15
|
+
NoReturn,
|
|
12
16
|
Sequence,
|
|
13
17
|
TypeVar,
|
|
14
18
|
cast,
|
|
15
|
-
TYPE_CHECKING,
|
|
16
|
-
NoReturn,
|
|
17
|
-
Mapping,
|
|
18
|
-
Iterator,
|
|
19
19
|
)
|
|
20
20
|
from urllib.parse import urlsplit, urlunsplit
|
|
21
21
|
|
|
@@ -25,12 +25,11 @@ from hypothesis.strategies import SearchStrategy
|
|
|
25
25
|
from hypothesis_graphql import strategies as gql_st
|
|
26
26
|
from requests.structures import CaseInsensitiveDict
|
|
27
27
|
|
|
28
|
-
from ..openapi.constants import LOCATION_TO_CONTAINER
|
|
29
28
|
from ... import auths
|
|
30
29
|
from ...auths import AuthStorage
|
|
31
30
|
from ...checks import not_a_server_error
|
|
32
31
|
from ...constants import NOT_SET
|
|
33
|
-
from ...exceptions import
|
|
32
|
+
from ...exceptions import OperationNotFound, OperationSchemaError
|
|
34
33
|
from ...generation import DataGenerationMethod, GenerationConfig
|
|
35
34
|
from ...hooks import (
|
|
36
35
|
GLOBAL_HOOK_DISPATCHER,
|
|
@@ -41,9 +40,10 @@ from ...hooks import (
|
|
|
41
40
|
)
|
|
42
41
|
from ...internal.result import Ok, Result
|
|
43
42
|
from ...models import APIOperation, Case, CheckFunction, OperationDefinition
|
|
44
|
-
from ...schemas import
|
|
43
|
+
from ...schemas import APIOperationMap, BaseSchema
|
|
45
44
|
from ...stateful import Stateful, StatefulTest
|
|
46
45
|
from ...types import Body, Cookies, Headers, NotSet, PathParameters, Query
|
|
46
|
+
from ..openapi.constants import LOCATION_TO_CONTAINER
|
|
47
47
|
from ._cache import OperationCache
|
|
48
48
|
from .scalars import CUSTOM_SCALARS, get_extra_scalar_strategies
|
|
49
49
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import string
|
|
3
4
|
import time
|
|
4
5
|
from base64 import b64encode
|
|
@@ -9,28 +10,29 @@ from typing import Any, Callable, Dict, Iterable, Optional
|
|
|
9
10
|
from urllib.parse import quote_plus
|
|
10
11
|
from weakref import WeakKeyDictionary
|
|
11
12
|
|
|
12
|
-
from hypothesis import
|
|
13
|
+
from hypothesis import reject
|
|
14
|
+
from hypothesis import strategies as st
|
|
13
15
|
from hypothesis_jsonschema import from_schema
|
|
14
16
|
from requests.auth import _basic_auth_str
|
|
15
17
|
from requests.structures import CaseInsensitiveDict
|
|
16
18
|
from requests.utils import to_key_val_list
|
|
17
19
|
|
|
20
|
+
from ... import auths, serializers
|
|
18
21
|
from ..._hypothesis import prepare_urlencoded
|
|
19
22
|
from ...constants import NOT_SET
|
|
20
|
-
from
|
|
21
|
-
from ... import auths, serializers
|
|
23
|
+
from ...exceptions import BodyInGetRequestError, SerializationNotPossible
|
|
22
24
|
from ...generation import DataGenerationMethod, GenerationConfig
|
|
23
|
-
from ...internal.copy import fast_deepcopy
|
|
24
|
-
from ...exceptions import SerializationNotPossible, BodyInGetRequestError
|
|
25
25
|
from ...hooks import HookContext, HookDispatcher, apply_to_all_dispatchers
|
|
26
|
+
from ...internal.copy import fast_deepcopy
|
|
26
27
|
from ...internal.validation import is_illegal_surrogate
|
|
27
28
|
from ...models import APIOperation, Case, cant_serialize
|
|
29
|
+
from ...serializers import Binary
|
|
28
30
|
from ...transports.content_types import parse_content_type
|
|
29
31
|
from ...transports.headers import has_invalid_characters, is_latin_1_encodable
|
|
30
32
|
from ...types import NotSet
|
|
31
|
-
from ...serializers import Binary
|
|
32
33
|
from ...utils import compose, skip
|
|
33
34
|
from .constants import LOCATION_TO_CONTAINER
|
|
35
|
+
from .formats import STRING_FORMATS
|
|
34
36
|
from .media_types import MEDIA_TYPES
|
|
35
37
|
from .negative import negative_schema
|
|
36
38
|
from .negative.utils import can_negate
|
|
@@ -360,11 +362,15 @@ def get_parameters_strategy(
|
|
|
360
362
|
if operation in _PARAMETER_STRATEGIES_CACHE and nested_cache_key in _PARAMETER_STRATEGIES_CACHE[operation]:
|
|
361
363
|
return _PARAMETER_STRATEGIES_CACHE[operation][nested_cache_key]
|
|
362
364
|
schema = parameters_to_json_schema(operation, parameters)
|
|
363
|
-
if
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
365
|
+
if location == "path":
|
|
366
|
+
if not operation.schema.validate_schema:
|
|
367
|
+
# If schema validation is disabled, we try to generate data even if the parameter definition
|
|
368
|
+
# contains errors.
|
|
369
|
+
# In this case, we know that the `required` keyword should always be `True`.
|
|
370
|
+
schema["required"] = list(schema["properties"])
|
|
371
|
+
for prop in schema.get("properties", {}).values():
|
|
372
|
+
if prop.get("type") == "string":
|
|
373
|
+
prop.setdefault("minLength", 1)
|
|
368
374
|
schema = operation.schema.prepare_schema(schema)
|
|
369
375
|
for name in exclude:
|
|
370
376
|
# Values from `exclude` are not necessarily valid for the schema - they come from user-defined examples
|