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.
- schemathesis/__init__.py +27 -65
- schemathesis/auths.py +26 -68
- schemathesis/checks.py +130 -60
- schemathesis/cli/__init__.py +5 -2105
- schemathesis/cli/commands/__init__.py +37 -0
- schemathesis/cli/commands/run/__init__.py +662 -0
- schemathesis/cli/commands/run/checks.py +80 -0
- schemathesis/cli/commands/run/context.py +117 -0
- schemathesis/cli/commands/run/events.py +30 -0
- schemathesis/cli/commands/run/executor.py +141 -0
- schemathesis/cli/commands/run/filters.py +202 -0
- schemathesis/cli/commands/run/handlers/__init__.py +46 -0
- schemathesis/cli/commands/run/handlers/base.py +18 -0
- schemathesis/cli/{cassettes.py → commands/run/handlers/cassettes.py} +178 -247
- schemathesis/cli/commands/run/handlers/junitxml.py +54 -0
- schemathesis/cli/commands/run/handlers/output.py +1368 -0
- schemathesis/cli/commands/run/hypothesis.py +105 -0
- schemathesis/cli/commands/run/loaders.py +129 -0
- schemathesis/cli/{callbacks.py → commands/run/validation.py} +59 -175
- schemathesis/cli/constants.py +5 -58
- schemathesis/cli/core.py +17 -0
- schemathesis/cli/ext/fs.py +14 -0
- schemathesis/cli/ext/groups.py +55 -0
- schemathesis/cli/{options.py → ext/options.py} +37 -16
- schemathesis/cli/hooks.py +36 -0
- schemathesis/contrib/__init__.py +1 -3
- schemathesis/contrib/openapi/__init__.py +1 -3
- schemathesis/contrib/openapi/fill_missing_examples.py +3 -7
- schemathesis/core/__init__.py +58 -0
- schemathesis/core/compat.py +25 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +58 -0
- schemathesis/core/deserialization.py +65 -0
- schemathesis/core/errors.py +370 -0
- schemathesis/core/failures.py +315 -0
- schemathesis/core/fs.py +19 -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/{internal/output.py → core/output/__init__.py} +1 -0
- schemathesis/core/output/sanitization.py +197 -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 +108 -0
- schemathesis/core/validation.py +38 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +30 -0
- schemathesis/engine/config.py +59 -0
- schemathesis/engine/context.py +119 -0
- schemathesis/engine/control.py +36 -0
- schemathesis/engine/core.py +157 -0
- schemathesis/engine/errors.py +394 -0
- schemathesis/engine/events.py +243 -0
- schemathesis/engine/phases/__init__.py +66 -0
- schemathesis/{runner → engine/phases}/probes.py +49 -68
- schemathesis/engine/phases/stateful/__init__.py +66 -0
- schemathesis/engine/phases/stateful/_executor.py +301 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +175 -0
- schemathesis/engine/phases/unit/_executor.py +322 -0
- schemathesis/engine/phases/unit/_pool.py +74 -0
- schemathesis/engine/recorder.py +246 -0
- schemathesis/errors.py +31 -0
- schemathesis/experimental/__init__.py +9 -40
- schemathesis/filters.py +7 -95
- schemathesis/generation/__init__.py +3 -3
- schemathesis/generation/case.py +190 -0
- schemathesis/generation/coverage.py +22 -22
- schemathesis/{_patches.py → generation/hypothesis/__init__.py} +15 -6
- schemathesis/generation/hypothesis/builder.py +585 -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/modes.py +28 -0
- schemathesis/generation/overrides.py +96 -0
- schemathesis/generation/stateful/__init__.py +20 -0
- schemathesis/{stateful → generation/stateful}/state_machine.py +84 -109
- schemathesis/generation/targets.py +69 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +131 -0
- schemathesis/hooks.py +17 -62
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +387 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +63 -0
- schemathesis/openapi/loaders.py +178 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +273 -0
- schemathesis/pytest/loaders.py +12 -0
- schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +94 -107
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +456 -228
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/_cache.py +1 -2
- schemathesis/specs/graphql/scalars.py +5 -3
- schemathesis/specs/graphql/schemas.py +122 -123
- 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 +97 -134
- schemathesis/specs/openapi/checks.py +238 -219
- schemathesis/specs/openapi/converter.py +4 -4
- schemathesis/specs/openapi/definitions.py +1 -1
- schemathesis/specs/openapi/examples.py +22 -20
- schemathesis/specs/openapi/expressions/__init__.py +11 -15
- schemathesis/specs/openapi/expressions/extractors.py +1 -4
- schemathesis/specs/openapi/expressions/nodes.py +33 -32
- schemathesis/specs/openapi/formats.py +3 -2
- schemathesis/specs/openapi/links.py +123 -299
- schemathesis/specs/openapi/media_types.py +10 -12
- schemathesis/specs/openapi/negative/__init__.py +2 -1
- schemathesis/specs/openapi/negative/mutations.py +3 -2
- schemathesis/specs/openapi/parameters.py +8 -6
- schemathesis/specs/openapi/patterns.py +1 -1
- schemathesis/specs/openapi/references.py +11 -51
- schemathesis/specs/openapi/schemas.py +177 -191
- schemathesis/specs/openapi/security.py +1 -1
- schemathesis/specs/openapi/serialization.py +10 -6
- schemathesis/specs/openapi/stateful/__init__.py +97 -91
- schemathesis/transport/__init__.py +104 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +99 -0
- schemathesis/transport/requests.py +221 -0
- schemathesis/{_xml.py → transport/serialization.py} +69 -7
- schemathesis/transport/wsgi.py +165 -0
- {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/METADATA +18 -14
- schemathesis-4.0.0a2.dist-info/RECORD +151 -0
- {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/entry_points.txt +1 -1
- schemathesis/_compat.py +0 -74
- schemathesis/_dependency_versions.py +0 -19
- schemathesis/_hypothesis.py +0 -559
- schemathesis/_override.py +0 -50
- schemathesis/_rate_limiter.py +0 -7
- 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 -936
- 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 -56
- 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/extra/_aiohttp.py +0 -28
- schemathesis/extra/_flask.py +0 -13
- schemathesis/extra/_server.py +0 -18
- schemathesis/failures.py +0 -277
- 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 -84
- schemathesis/internal/copy.py +0 -32
- schemathesis/internal/datetime.py +0 -5
- schemathesis/internal/deprecation.py +0 -38
- schemathesis/internal/diff.py +0 -15
- schemathesis/internal/extensions.py +0 -27
- schemathesis/internal/jsonschema.py +0 -36
- 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 -104
- schemathesis/runner/impl/core.py +0 -1246
- 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/loaders.py +0 -708
- 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/statistic.py +0 -22
- schemathesis/stateful/validation.py +0 -100
- schemathesis/targets.py +0 -77
- schemathesis/transports/__init__.py +0 -359
- 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.7.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.7.dist-info → schemathesis-4.0.0a2.dist-info}/WHEEL +0 -0
- {schemathesis-3.39.7.dist-info → schemathesis-4.0.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,190 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
5
|
+
|
6
|
+
from schemathesis.checks import CHECKS, CheckContext, CheckFunction, run_checks
|
7
|
+
from schemathesis.core import NOT_SET, SCHEMATHESIS_TEST_CASE_HEADER, NotSet, curl
|
8
|
+
from schemathesis.core.failures import FailureGroup, failure_report_title, format_failures
|
9
|
+
from schemathesis.core.transport import Response
|
10
|
+
from schemathesis.generation import generate_random_case_id
|
11
|
+
from schemathesis.generation.meta import CaseMetadata
|
12
|
+
from schemathesis.generation.overrides import Override, store_components
|
13
|
+
from schemathesis.hooks import HookContext, dispatch
|
14
|
+
from schemathesis.transport.prepare import prepare_request
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
import requests.auth
|
18
|
+
from requests.structures import CaseInsensitiveDict
|
19
|
+
|
20
|
+
from schemathesis.schemas import APIOperation
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class Case:
|
25
|
+
"""A single test case parameters."""
|
26
|
+
|
27
|
+
operation: APIOperation
|
28
|
+
method: str
|
29
|
+
path: str
|
30
|
+
# Unique test case identifier
|
31
|
+
id: str = field(default_factory=generate_random_case_id, compare=False)
|
32
|
+
path_parameters: dict[str, Any] | None = None
|
33
|
+
headers: CaseInsensitiveDict | None = None
|
34
|
+
cookies: dict[str, Any] | None = None
|
35
|
+
query: dict[str, Any] | None = None
|
36
|
+
# By default, there is no body, but we can't use `None` as the default value because it clashes with `null`
|
37
|
+
# which is a valid payload.
|
38
|
+
body: list | dict[str, Any] | str | int | float | bool | bytes | NotSet = NOT_SET
|
39
|
+
# The media type for cases with a payload. For example, "application/json"
|
40
|
+
media_type: str | None = None
|
41
|
+
|
42
|
+
meta: CaseMetadata | None = field(compare=False, default=None)
|
43
|
+
|
44
|
+
_auth: requests.auth.AuthBase | None = None
|
45
|
+
_has_explicit_auth: bool = False
|
46
|
+
|
47
|
+
def __post_init__(self) -> None:
|
48
|
+
self._components = store_components(self)
|
49
|
+
|
50
|
+
@property
|
51
|
+
def _override(self) -> Override:
|
52
|
+
return Override.from_components(self._components, self)
|
53
|
+
|
54
|
+
def __repr__(self) -> str:
|
55
|
+
output = f"{self.__class__.__name__}("
|
56
|
+
first = True
|
57
|
+
for name in ("path_parameters", "headers", "cookies", "query", "body"):
|
58
|
+
value = getattr(self, name)
|
59
|
+
if value is not None and not isinstance(value, NotSet):
|
60
|
+
if first:
|
61
|
+
first = False
|
62
|
+
else:
|
63
|
+
output += ", "
|
64
|
+
output += f"{name}={value!r}"
|
65
|
+
return f"{output})"
|
66
|
+
|
67
|
+
def __hash__(self) -> int:
|
68
|
+
return hash(self.as_curl_command({SCHEMATHESIS_TEST_CASE_HEADER: "0"}))
|
69
|
+
|
70
|
+
def _repr_pretty_(self, *args: Any, **kwargs: Any) -> None: ...
|
71
|
+
|
72
|
+
def as_curl_command(self, headers: Mapping[str, Any] | None = None, verify: bool = True) -> str:
|
73
|
+
"""Construct a curl command for a given case."""
|
74
|
+
request_data = prepare_request(self, headers, self.operation.schema.output_config.sanitize)
|
75
|
+
return curl.generate(
|
76
|
+
method=str(request_data.method),
|
77
|
+
url=str(request_data.url),
|
78
|
+
body=request_data.body,
|
79
|
+
verify=verify,
|
80
|
+
headers=dict(request_data.headers),
|
81
|
+
known_generated_headers=dict(self.headers or {}),
|
82
|
+
)
|
83
|
+
|
84
|
+
def as_transport_kwargs(self, base_url: str | None = None, headers: dict[str, str] | None = None) -> dict[str, Any]:
|
85
|
+
"""Convert the test case into a dictionary acceptable by the underlying transport call."""
|
86
|
+
return self.operation.schema.transport.serialize_case(self, base_url=base_url, headers=headers)
|
87
|
+
|
88
|
+
def call(
|
89
|
+
self,
|
90
|
+
base_url: str | None = None,
|
91
|
+
session: requests.Session | None = None,
|
92
|
+
headers: dict[str, Any] | None = None,
|
93
|
+
params: dict[str, Any] | None = None,
|
94
|
+
cookies: dict[str, Any] | None = None,
|
95
|
+
**kwargs: Any,
|
96
|
+
) -> Response:
|
97
|
+
hook_context = HookContext(operation=self.operation)
|
98
|
+
dispatch("before_call", hook_context, self, **kwargs)
|
99
|
+
if self.operation.app is not None:
|
100
|
+
kwargs["app"] = self.operation.app
|
101
|
+
response = self.operation.schema.transport.send(
|
102
|
+
self,
|
103
|
+
session=session,
|
104
|
+
base_url=base_url,
|
105
|
+
headers=headers,
|
106
|
+
params=params,
|
107
|
+
cookies=cookies,
|
108
|
+
**kwargs,
|
109
|
+
)
|
110
|
+
dispatch("after_call", hook_context, self, response)
|
111
|
+
return response
|
112
|
+
|
113
|
+
def validate_response(
|
114
|
+
self,
|
115
|
+
response: Response,
|
116
|
+
checks: list[CheckFunction] | None = None,
|
117
|
+
additional_checks: list[CheckFunction] | None = None,
|
118
|
+
excluded_checks: list[CheckFunction] | None = None,
|
119
|
+
headers: dict[str, Any] | None = None,
|
120
|
+
transport_kwargs: dict[str, Any] | None = None,
|
121
|
+
) -> None:
|
122
|
+
"""Validate application response.
|
123
|
+
|
124
|
+
By default, all available checks will be applied.
|
125
|
+
|
126
|
+
:param response: Application response.
|
127
|
+
:param checks: A tuple of check functions that accept ``response`` and ``case``.
|
128
|
+
:param additional_checks: A tuple of additional checks that will be executed after ones from the ``checks``
|
129
|
+
argument.
|
130
|
+
:param excluded_checks: Checks excluded from the default ones.
|
131
|
+
"""
|
132
|
+
__tracebackhide__ = True
|
133
|
+
from requests.structures import CaseInsensitiveDict
|
134
|
+
|
135
|
+
checks = [
|
136
|
+
check
|
137
|
+
for check in list(checks or CHECKS.get_all()) + list(additional_checks or [])
|
138
|
+
if check not in set(excluded_checks or [])
|
139
|
+
]
|
140
|
+
|
141
|
+
ctx = CheckContext(
|
142
|
+
override=self._override,
|
143
|
+
auth=None,
|
144
|
+
headers=CaseInsensitiveDict(headers) if headers else None,
|
145
|
+
config={},
|
146
|
+
transport_kwargs=transport_kwargs,
|
147
|
+
recorder=None,
|
148
|
+
)
|
149
|
+
failures = run_checks(
|
150
|
+
case=self,
|
151
|
+
response=response,
|
152
|
+
ctx=ctx,
|
153
|
+
checks=checks,
|
154
|
+
on_failure=lambda _, collected, failure: collected.add(failure),
|
155
|
+
)
|
156
|
+
if failures:
|
157
|
+
_failures = list(failures)
|
158
|
+
message = failure_report_title(_failures) + "\n"
|
159
|
+
verify = getattr(response, "verify", True)
|
160
|
+
curl = self.as_curl_command(headers=dict(response.request.headers), verify=verify)
|
161
|
+
message += format_failures(
|
162
|
+
case_id=None,
|
163
|
+
response=response,
|
164
|
+
failures=_failures,
|
165
|
+
curl=curl,
|
166
|
+
config=self.operation.schema.output_config,
|
167
|
+
)
|
168
|
+
raise FailureGroup(_failures, message) from None
|
169
|
+
|
170
|
+
def call_and_validate(
|
171
|
+
self,
|
172
|
+
base_url: str | None = None,
|
173
|
+
session: requests.Session | None = None,
|
174
|
+
headers: dict[str, Any] | None = None,
|
175
|
+
checks: list[CheckFunction] | None = None,
|
176
|
+
additional_checks: list[CheckFunction] | None = None,
|
177
|
+
excluded_checks: list[CheckFunction] | None = None,
|
178
|
+
**kwargs: Any,
|
179
|
+
) -> Response:
|
180
|
+
__tracebackhide__ = True
|
181
|
+
response = self.call(base_url, session, headers, **kwargs)
|
182
|
+
self.validate_response(
|
183
|
+
response,
|
184
|
+
checks,
|
185
|
+
headers=headers,
|
186
|
+
additional_checks=additional_checks,
|
187
|
+
excluded_checks=excluded_checks,
|
188
|
+
transport_kwargs=kwargs,
|
189
|
+
)
|
190
|
+
return response
|
@@ -16,14 +16,16 @@ from hypothesis_jsonschema import from_schema
|
|
16
16
|
from hypothesis_jsonschema._canonicalise import canonicalish
|
17
17
|
from hypothesis_jsonschema._from_schema import STRING_FORMATS as BUILT_IN_STRING_FORMATS
|
18
18
|
|
19
|
-
from
|
20
|
-
from
|
19
|
+
from schemathesis.core import NOT_SET
|
20
|
+
from schemathesis.core.compat import RefResolutionError
|
21
|
+
from schemathesis.core.transforms import deepclone
|
22
|
+
from schemathesis.core.validation import has_invalid_characters, is_latin_1_encodable
|
23
|
+
from schemathesis.generation import GenerationMode
|
24
|
+
from schemathesis.generation.hypothesis import examples
|
25
|
+
|
21
26
|
from ..specs.openapi.converter import update_pattern_in_schema
|
22
27
|
from ..specs.openapi.formats import STRING_FORMATS, get_default_format_strategies
|
23
28
|
from ..specs.openapi.patterns import update_quantifier
|
24
|
-
from ..transports.headers import has_invalid_characters, is_latin_1_encodable
|
25
|
-
from ._hypothesis import get_single_example
|
26
|
-
from ._methods import DataGenerationMethod
|
27
29
|
|
28
30
|
|
29
31
|
def _replace_zero_with_nonzero(x: float) -> float:
|
@@ -62,18 +64,18 @@ UNKNOWN_PROPERTY_VALUE = 42
|
|
62
64
|
@dataclass
|
63
65
|
class GeneratedValue:
|
64
66
|
value: Any
|
65
|
-
|
67
|
+
generation_mode: GenerationMode
|
66
68
|
description: str
|
67
69
|
parameter: str | None
|
68
70
|
location: str | None
|
69
71
|
|
70
|
-
__slots__ = ("value", "
|
72
|
+
__slots__ = ("value", "generation_mode", "description", "parameter", "location")
|
71
73
|
|
72
74
|
@classmethod
|
73
75
|
def with_positive(cls, value: Any, *, description: str) -> GeneratedValue:
|
74
76
|
return cls(
|
75
77
|
value=value,
|
76
|
-
|
78
|
+
generation_mode=GenerationMode.POSITIVE,
|
77
79
|
description=description,
|
78
80
|
location=None,
|
79
81
|
parameter=None,
|
@@ -85,7 +87,7 @@ class GeneratedValue:
|
|
85
87
|
) -> GeneratedValue:
|
86
88
|
return cls(
|
87
89
|
value=value,
|
88
|
-
|
90
|
+
generation_mode=GenerationMode.NEGATIVE,
|
89
91
|
description=description,
|
90
92
|
location=location,
|
91
93
|
parameter=parameter,
|
@@ -98,28 +100,26 @@ NegativeValue = GeneratedValue.with_negative
|
|
98
100
|
|
99
101
|
@lru_cache(maxsize=128)
|
100
102
|
def cached_draw(strategy: st.SearchStrategy) -> Any:
|
101
|
-
return
|
103
|
+
return examples.generate_one(strategy)
|
102
104
|
|
103
105
|
|
104
106
|
@dataclass
|
105
107
|
class CoverageContext:
|
106
|
-
|
108
|
+
generation_modes: list[GenerationMode]
|
107
109
|
location: str
|
108
110
|
path: list[str | int]
|
109
111
|
|
110
|
-
__slots__ = ("location", "
|
112
|
+
__slots__ = ("location", "generation_modes", "path")
|
111
113
|
|
112
114
|
def __init__(
|
113
115
|
self,
|
114
116
|
*,
|
115
117
|
location: str,
|
116
|
-
|
118
|
+
generation_modes: list[GenerationMode] | None = None,
|
117
119
|
path: list[str | int] | None = None,
|
118
120
|
) -> None:
|
119
121
|
self.location = location
|
120
|
-
self.
|
121
|
-
data_generation_methods if data_generation_methods is not None else DataGenerationMethod.all()
|
122
|
-
)
|
122
|
+
self.generation_modes = generation_modes if generation_modes is not None else GenerationMode.all()
|
123
123
|
self.path = path or []
|
124
124
|
|
125
125
|
@contextmanager
|
@@ -137,14 +137,14 @@ class CoverageContext:
|
|
137
137
|
def with_positive(self) -> CoverageContext:
|
138
138
|
return CoverageContext(
|
139
139
|
location=self.location,
|
140
|
-
|
140
|
+
generation_modes=[GenerationMode.POSITIVE],
|
141
141
|
path=self.path,
|
142
142
|
)
|
143
143
|
|
144
144
|
def with_negative(self) -> CoverageContext:
|
145
145
|
return CoverageContext(
|
146
146
|
location=self.location,
|
147
|
-
|
147
|
+
generation_modes=[GenerationMode.NEGATIVE],
|
148
148
|
path=self.path,
|
149
149
|
)
|
150
150
|
|
@@ -255,7 +255,7 @@ def _cover_positive_for_type(
|
|
255
255
|
template = ctx.generate_from_schema(template_schema)
|
256
256
|
else:
|
257
257
|
template = None
|
258
|
-
if
|
258
|
+
if GenerationMode.POSITIVE in ctx.generation_modes:
|
259
259
|
ctx = ctx.with_positive()
|
260
260
|
enum = schema.get("enum", NOT_SET)
|
261
261
|
const = schema.get("const", NOT_SET)
|
@@ -298,7 +298,7 @@ def _ignore_unfixable(
|
|
298
298
|
*,
|
299
299
|
# Cache exception types here as `jsonschema` uses a custom `__getattr__` on the module level
|
300
300
|
# and it may cause errors during the interpreter shutdown
|
301
|
-
ref_error: type[Exception] =
|
301
|
+
ref_error: type[Exception] = RefResolutionError,
|
302
302
|
schema_error: type[Exception] = jsonschema.SchemaError,
|
303
303
|
) -> Generator:
|
304
304
|
try:
|
@@ -333,7 +333,7 @@ def cover_schema_iter(
|
|
333
333
|
for ty in types:
|
334
334
|
with _ignore_unfixable():
|
335
335
|
yield from _cover_positive_for_type(ctx, schema, ty)
|
336
|
-
if
|
336
|
+
if GenerationMode.NEGATIVE in ctx.generation_modes:
|
337
337
|
template = None
|
338
338
|
for key, value in schema.items():
|
339
339
|
with _ignore_unfixable(), ctx.at(key):
|
@@ -467,7 +467,7 @@ def _get_properties(schema: dict | bool) -> dict | bool:
|
|
467
467
|
return {"enum": schema["examples"]}
|
468
468
|
if schema.get("type") == "object":
|
469
469
|
return _get_template_schema(schema, "object")
|
470
|
-
_schema =
|
470
|
+
_schema = deepclone(schema)
|
471
471
|
update_pattern_in_schema(_schema)
|
472
472
|
return _schema
|
473
473
|
return schema
|
@@ -1,14 +1,23 @@
|
|
1
|
-
"""A set of performance-related patches."""
|
2
|
-
|
3
1
|
from typing import Any
|
4
2
|
|
3
|
+
# Maximum test running time
|
4
|
+
DEFAULT_DEADLINE = 15000
|
5
|
+
|
5
6
|
|
6
|
-
def
|
7
|
+
def setup() -> None:
|
8
|
+
from hypothesis.internal.entropy import deterministic_PRNG
|
7
9
|
from hypothesis.internal.reflection import is_first_param_referenced_in_function
|
8
10
|
from hypothesis.strategies._internal import core
|
9
11
|
from hypothesis_jsonschema import _from_schema, _resolve
|
10
12
|
|
11
|
-
from .
|
13
|
+
from schemathesis.core.transforms import deepclone
|
14
|
+
|
15
|
+
# Forcefully initializes Hypothesis' global PRNG to avoid races that initialize it
|
16
|
+
# if e.g. Schemathesis CLI is used with multiple workers
|
17
|
+
with deterministic_PRNG():
|
18
|
+
pass
|
19
|
+
|
20
|
+
# A set of performance-related patches
|
12
21
|
|
13
22
|
# This one is used a lot, and under the hood it re-parses the AST of the same function
|
14
23
|
def _is_first_param_referenced_in_function(f: Any) -> bool:
|
@@ -17,5 +26,5 @@ def install() -> None:
|
|
17
26
|
return is_first_param_referenced_in_function(f)
|
18
27
|
|
19
28
|
core.is_first_param_referenced_in_function = _is_first_param_referenced_in_function # type: ignore
|
20
|
-
_resolve.deepcopy =
|
21
|
-
_from_schema.deepcopy =
|
29
|
+
_resolve.deepcopy = deepclone # type: ignore
|
30
|
+
_from_schema.deepcopy = deepclone # type: ignore
|