schemathesis 4.0.26__py3-none-any.whl → 4.1.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/cli/commands/run/__init__.py +8 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +1 -0
- schemathesis/cli/commands/run/handlers/output.py +27 -17
- schemathesis/config/_operations.py +5 -0
- schemathesis/config/_phases.py +43 -5
- schemathesis/config/_projects.py +18 -0
- schemathesis/config/schema.json +31 -0
- schemathesis/core/__init__.py +1 -0
- schemathesis/core/failures.py +2 -1
- schemathesis/engine/context.py +39 -4
- schemathesis/engine/core.py +30 -9
- schemathesis/engine/events.py +12 -2
- schemathesis/engine/observations.py +42 -0
- schemathesis/engine/phases/__init__.py +5 -0
- schemathesis/engine/phases/probes.py +9 -3
- schemathesis/engine/phases/stateful/_executor.py +9 -1
- schemathesis/engine/phases/unit/__init__.py +1 -0
- schemathesis/engine/phases/unit/_executor.py +13 -0
- schemathesis/filters.py +4 -0
- schemathesis/generation/case.py +1 -1
- schemathesis/generation/coverage.py +16 -2
- schemathesis/generation/hypothesis/builder.py +14 -1
- schemathesis/hooks.py +40 -14
- schemathesis/openapi/loaders.py +1 -1
- schemathesis/pytest/plugin.py +6 -0
- schemathesis/schemas.py +9 -1
- schemathesis/specs/openapi/_hypothesis.py +16 -3
- schemathesis/specs/openapi/checks.py +4 -1
- schemathesis/specs/openapi/formats.py +15 -2
- schemathesis/specs/openapi/schemas.py +26 -6
- schemathesis/specs/openapi/stateful/inference.py +250 -0
- schemathesis/transport/requests.py +3 -0
- {schemathesis-4.0.26.dist-info → schemathesis-4.1.1.dist-info}/METADATA +3 -2
- {schemathesis-4.0.26.dist-info → schemathesis-4.1.1.dist-info}/RECORD +37 -35
- {schemathesis-4.0.26.dist-info → schemathesis-4.1.1.dist-info}/WHEEL +0 -0
- {schemathesis-4.0.26.dist-info → schemathesis-4.1.1.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.0.26.dist-info → schemathesis-4.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
"""Inferencing connections between API operations.
|
2
|
+
|
3
|
+
The current implementation extracts information from the `Location` header and
|
4
|
+
generates OpenAPI links for exact and prefix matches.
|
5
|
+
|
6
|
+
When a `Location` header points to `/users/123`, the inference:
|
7
|
+
|
8
|
+
1. Finds the exact match: `GET /users/{userId}`
|
9
|
+
2. Finds prefix matches: `GET /users/{userId}/posts`, `GET /users/{userId}/posts/{postId}`
|
10
|
+
3. Generates OpenAPI links with regex parameter extractors
|
11
|
+
"""
|
12
|
+
|
13
|
+
from __future__ import annotations
|
14
|
+
|
15
|
+
import re
|
16
|
+
from dataclasses import dataclass
|
17
|
+
from typing import TYPE_CHECKING, Any, Mapping, Union
|
18
|
+
from urllib.parse import urlsplit
|
19
|
+
|
20
|
+
from werkzeug.exceptions import MethodNotAllowed, NotFound
|
21
|
+
from werkzeug.routing import Map, MapAdapter, Rule
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from schemathesis.engine.observations import LocationHeaderEntry
|
25
|
+
from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
|
26
|
+
|
27
|
+
|
28
|
+
@dataclass(unsafe_hash=True)
|
29
|
+
class OperationById:
|
30
|
+
"""API operation identified by operationId."""
|
31
|
+
|
32
|
+
value: str
|
33
|
+
method: str
|
34
|
+
path: str
|
35
|
+
|
36
|
+
__slots__ = ("value", "method", "path")
|
37
|
+
|
38
|
+
def to_link_base(self) -> dict[str, Any]:
|
39
|
+
return {"operationId": self.value, "x-inferred": True}
|
40
|
+
|
41
|
+
|
42
|
+
@dataclass(unsafe_hash=True)
|
43
|
+
class OperationByRef:
|
44
|
+
"""API operation identified by JSON reference path."""
|
45
|
+
|
46
|
+
value: str
|
47
|
+
method: str
|
48
|
+
path: str
|
49
|
+
|
50
|
+
__slots__ = ("value", "method", "path")
|
51
|
+
|
52
|
+
def to_link_base(self) -> dict[str, Any]:
|
53
|
+
return {"operationRef": self.value, "x-inferred": True}
|
54
|
+
|
55
|
+
|
56
|
+
OperationReference = Union[OperationById, OperationByRef]
|
57
|
+
# Method, path, response code, sorted path parameter names
|
58
|
+
SeenLinkKey = tuple[str, str, int, tuple[str, ...]]
|
59
|
+
|
60
|
+
|
61
|
+
@dataclass
|
62
|
+
class MatchList:
|
63
|
+
"""Results of matching a location path against API operation."""
|
64
|
+
|
65
|
+
exact: OperationReference
|
66
|
+
inexact: list[OperationReference]
|
67
|
+
parameters: Mapping[str, Any]
|
68
|
+
|
69
|
+
__slots__ = ("exact", "inexact", "parameters")
|
70
|
+
|
71
|
+
|
72
|
+
@dataclass
|
73
|
+
class LinkInferencer:
|
74
|
+
"""Infer OpenAPI links from Location headers for stateful testing."""
|
75
|
+
|
76
|
+
_adapter: MapAdapter
|
77
|
+
# All API operations for prefix matching
|
78
|
+
_operations: list[OperationReference]
|
79
|
+
_base_url: str | None
|
80
|
+
_base_path: str
|
81
|
+
_links_field_name: str
|
82
|
+
|
83
|
+
__slots__ = ("_adapter", "_operations", "_base_url", "_base_path", "_links_field_name")
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
def from_schema(cls, schema: BaseOpenAPISchema) -> LinkInferencer:
|
87
|
+
# NOTE: Use `matchit` for routing in the future
|
88
|
+
rules = []
|
89
|
+
operations = []
|
90
|
+
for method, path, definition in schema._operation_iter():
|
91
|
+
operation_id = definition.get("operationId")
|
92
|
+
operation: OperationById | OperationByRef
|
93
|
+
if operation_id:
|
94
|
+
operation = OperationById(operation_id, method=method, path=path)
|
95
|
+
else:
|
96
|
+
encoded_path = path.replace("~", "~0").replace("/", "~1")
|
97
|
+
operation = OperationByRef(f"#/paths/{encoded_path}/{method}", method=method, path=path)
|
98
|
+
|
99
|
+
operations.append(operation)
|
100
|
+
|
101
|
+
# Replace `{parameter}` with `<parameter>` as angle brackets are used for parameters in werkzeug
|
102
|
+
path = re.sub(r"\{([^}]+)\}", r"<\1>", path)
|
103
|
+
rules.append(Rule(path, endpoint=operation, methods=[method.upper()]))
|
104
|
+
|
105
|
+
return cls(
|
106
|
+
_adapter=Map(rules).bind("", ""),
|
107
|
+
_operations=operations,
|
108
|
+
_base_url=schema.config.base_url,
|
109
|
+
_base_path=schema.base_path,
|
110
|
+
_links_field_name=schema.links_field,
|
111
|
+
)
|
112
|
+
|
113
|
+
def match(self, path: str) -> tuple[OperationReference, Mapping[str, str]] | None:
|
114
|
+
"""Match path to API operation and extract path parameters."""
|
115
|
+
try:
|
116
|
+
return self._adapter.match(path)
|
117
|
+
except (NotFound, MethodNotAllowed):
|
118
|
+
return None
|
119
|
+
|
120
|
+
def _build_links_from_matches(self, matches: MatchList) -> list[dict]:
|
121
|
+
"""Build links from already-found matches."""
|
122
|
+
exact = self._build_link_from_match(matches.exact, matches.parameters)
|
123
|
+
parameters = exact["parameters"]
|
124
|
+
links = [exact]
|
125
|
+
for inexact in matches.inexact:
|
126
|
+
link = inexact.to_link_base()
|
127
|
+
# Parameter extraction is the same, only operations are different
|
128
|
+
link["parameters"] = parameters
|
129
|
+
links.append(link)
|
130
|
+
return links
|
131
|
+
|
132
|
+
def _find_matches_from_normalized_location(self, normalized_location: str) -> MatchList | None:
|
133
|
+
"""Find matches from an already-normalized location."""
|
134
|
+
match = self.match(normalized_location)
|
135
|
+
if not match:
|
136
|
+
# It may happen that there is no match, but it is unlikely as the API assumed to return a valid Location
|
137
|
+
# that points to an existing API operation. In such cases, if they appear in practice the logic here could be extended
|
138
|
+
# to support partial matches
|
139
|
+
return None
|
140
|
+
exact, parameters = match
|
141
|
+
if not parameters:
|
142
|
+
# Links without parameters don't make sense
|
143
|
+
return None
|
144
|
+
matches = MatchList(exact=exact, inexact=[], parameters=parameters)
|
145
|
+
|
146
|
+
# Find prefix matches, excluding the exact match
|
147
|
+
# For example:
|
148
|
+
#
|
149
|
+
# Location: /users/123 -> /users/{user_id} (exact match)
|
150
|
+
# /users/{user_id}/posts , /users/{user_id}/posts/{post_id} (partial matches)
|
151
|
+
#
|
152
|
+
for candidate in self._operations:
|
153
|
+
if candidate == exact:
|
154
|
+
continue
|
155
|
+
if candidate.path.startswith(exact.path):
|
156
|
+
matches.inexact.append(candidate)
|
157
|
+
|
158
|
+
return matches
|
159
|
+
|
160
|
+
def _build_link_from_match(
|
161
|
+
self, operation: OperationById | OperationByRef, path_parameters: Mapping[str, Any]
|
162
|
+
) -> dict:
|
163
|
+
link = operation.to_link_base()
|
164
|
+
|
165
|
+
# Build regex expressions to extract path parameters
|
166
|
+
parameters = {}
|
167
|
+
for name in path_parameters:
|
168
|
+
# Replace the target parameter with capture group and others with non-slash matcher
|
169
|
+
pattern = operation.path
|
170
|
+
for candidate in path_parameters:
|
171
|
+
if candidate == name:
|
172
|
+
pattern = pattern.replace(f"{{{candidate}}}", "(.+)")
|
173
|
+
else:
|
174
|
+
pattern = pattern.replace(f"{{{candidate}}}", "[^/]+")
|
175
|
+
|
176
|
+
parameters[name] = f"$response.header.Location#regex:{pattern}"
|
177
|
+
|
178
|
+
link["parameters"] = parameters
|
179
|
+
|
180
|
+
return link
|
181
|
+
|
182
|
+
def _normalize_location(self, location: str) -> str | None:
|
183
|
+
"""Normalize location header, handling both relative and absolute URLs."""
|
184
|
+
location = location.strip()
|
185
|
+
if not location:
|
186
|
+
return None
|
187
|
+
|
188
|
+
# Check if it's an absolute URL
|
189
|
+
if location.startswith(("http://", "https://")):
|
190
|
+
if not self._base_url:
|
191
|
+
# Can't validate absolute URLs without base_url
|
192
|
+
return None
|
193
|
+
|
194
|
+
parsed = urlsplit(location)
|
195
|
+
base_parsed = urlsplit(self._base_url)
|
196
|
+
|
197
|
+
# Must match scheme, netloc, and start with the base path
|
198
|
+
if parsed.scheme != base_parsed.scheme or parsed.netloc != base_parsed.netloc:
|
199
|
+
return None
|
200
|
+
|
201
|
+
return self._strip_base_path_from_location(parsed.path)
|
202
|
+
|
203
|
+
# Relative URL - strip base path if present, otherwise use as-is
|
204
|
+
stripped = self._strip_base_path_from_location(location)
|
205
|
+
return stripped if stripped is not None else location
|
206
|
+
|
207
|
+
def _strip_base_path_from_location(self, path: str) -> str | None:
|
208
|
+
"""Strip base path from location path if it starts with base path."""
|
209
|
+
base_path = self._base_path.rstrip("/")
|
210
|
+
if not path.startswith(base_path):
|
211
|
+
return None
|
212
|
+
|
213
|
+
# Strip the base path to get relative path
|
214
|
+
relative_path = path[len(base_path) :]
|
215
|
+
return relative_path if relative_path.startswith("/") else "/" + relative_path
|
216
|
+
|
217
|
+
def inject_links(self, operation: dict[str, Any], entries: list[LocationHeaderEntry]) -> int:
|
218
|
+
from schemathesis.specs.openapi.schemas import _get_response_definition_by_status
|
219
|
+
|
220
|
+
responses = operation.setdefault("responses", {})
|
221
|
+
# To avoid unnecessary work, we need to skip entries that we know will produce already inferred links
|
222
|
+
seen: set[SeenLinkKey] = set()
|
223
|
+
injected = 0
|
224
|
+
|
225
|
+
for entry in entries:
|
226
|
+
location = self._normalize_location(entry.value)
|
227
|
+
if location is None:
|
228
|
+
# Skip invalid/empty locations or absolute URLs that don't match base_url
|
229
|
+
continue
|
230
|
+
|
231
|
+
matches = self._find_matches_from_normalized_location(location)
|
232
|
+
if matches is None:
|
233
|
+
# Skip locations that don't match any API apiration
|
234
|
+
continue
|
235
|
+
|
236
|
+
key = (matches.exact.method, matches.exact.path, entry.status_code, tuple(sorted(matches.parameters)))
|
237
|
+
if key in seen:
|
238
|
+
# Skip duplicate link generation for same operation/status/parameters combination
|
239
|
+
continue
|
240
|
+
seen.add(key)
|
241
|
+
# Find the right bucket for the response status or create a new one
|
242
|
+
definition = _get_response_definition_by_status(entry.status_code, responses)
|
243
|
+
if definition is None:
|
244
|
+
definition = responses.setdefault(str(entry.status_code), {})
|
245
|
+
links = definition.setdefault(self._links_field_name, {})
|
246
|
+
|
247
|
+
for idx, link in enumerate(self._build_links_from_matches(matches)):
|
248
|
+
links[f"X-Inferred-Link-{idx}"] = link
|
249
|
+
injected += 1
|
250
|
+
return injected
|
@@ -91,6 +91,7 @@ class RequestsTransport(BaseTransport["requests.Session"]):
|
|
91
91
|
|
92
92
|
config = case.operation.schema.config
|
93
93
|
|
94
|
+
max_redirects = kwargs.pop("max_redirects", None) or config.max_redirects_for(operation=case.operation)
|
94
95
|
timeout = config.request_timeout_for(operation=case.operation)
|
95
96
|
verify = config.tls_verify_for(operation=case.operation)
|
96
97
|
cert = config.request_cert_for(operation=case.operation)
|
@@ -131,6 +132,8 @@ class RequestsTransport(BaseTransport["requests.Session"]):
|
|
131
132
|
current_session_auth = session.auth
|
132
133
|
session.auth = None
|
133
134
|
close_session = False
|
135
|
+
if max_redirects is not None:
|
136
|
+
session.max_redirects = max_redirects
|
134
137
|
session.headers = {}
|
135
138
|
|
136
139
|
verify = data.get("verify", True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemathesis
|
3
|
-
Version: 4.
|
3
|
+
Version: 4.1.1
|
4
4
|
Summary: Property-based testing framework for Open API and GraphQL based apps
|
5
5
|
Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
|
6
6
|
Project-URL: Changelog, https://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
|
@@ -26,12 +26,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
26
26
|
Classifier: Programming Language :: Python :: 3.12
|
27
27
|
Classifier: Programming Language :: Python :: 3.13
|
28
28
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
29
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
29
30
|
Classifier: Topic :: Software Development :: Testing
|
30
31
|
Requires-Python: >=3.9
|
31
32
|
Requires-Dist: backoff<3.0,>=2.1.2
|
32
33
|
Requires-Dist: click<9,>=8.0
|
33
34
|
Requires-Dist: colorama<1.0,>=0.4
|
34
|
-
Requires-Dist: harfile<1.0,>=0.3.
|
35
|
+
Requires-Dist: harfile<1.0,>=0.3.1
|
35
36
|
Requires-Dist: httpx<1.0,>=0.22.0
|
36
37
|
Requires-Dist: hypothesis-graphql<1,>=0.11.1
|
37
38
|
Requires-Dist: hypothesis-jsonschema<0.24,>=0.23.1
|
@@ -2,17 +2,17 @@ schemathesis/__init__.py,sha256=QqVUCBQr-RDEstgCZLsxzIa9HJslVSeijrm9gES4b_0,1423
|
|
2
2
|
schemathesis/auths.py,sha256=JdEwPRS9WKmPcxzGXYYz9pjlIUMQYCfif7ZJU0Kde-I,16400
|
3
3
|
schemathesis/checks.py,sha256=GTdejjXDooAOuq66nvCK3i-AMPBuU-_-aNeSeL9JIlc,6561
|
4
4
|
schemathesis/errors.py,sha256=T8nobEi5tQX_SkwaYb8JFoIlF9F_vOQVprZ8EVPAhjA,931
|
5
|
-
schemathesis/filters.py,sha256=
|
6
|
-
schemathesis/hooks.py,sha256=
|
5
|
+
schemathesis/filters.py,sha256=Nk1Z6_L0fJBIzFGwjUhx9oDHJiGn-kVxbAXWeN9gRcQ,13525
|
6
|
+
schemathesis/hooks.py,sha256=q2wqYNgpMCO8ImSBkbrWDSwN0BSELelqJMgAAgGvv2M,14836
|
7
7
|
schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
schemathesis/schemas.py,sha256=
|
8
|
+
schemathesis/schemas.py,sha256=i0L6jYulGGASD1RiQsA6sOTNt1v812KHSZKIhnuX34s,28965
|
9
9
|
schemathesis/cli/__init__.py,sha256=U9gjzWWpiFhaqevPjZbwyTNjABdpvXETI4HgwdGKnvs,877
|
10
10
|
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
11
11
|
schemathesis/cli/constants.py,sha256=CVcQNHEiX-joAQmyuEVKWPOSxDHsOw_EXXZsEclzLuY,341
|
12
12
|
schemathesis/cli/core.py,sha256=ue7YUdVo3YvuzGL4s6i62NL6YqNDeVPBSnQ1znrvG2w,480
|
13
13
|
schemathesis/cli/commands/__init__.py,sha256=DNzKEnXu7GjGSVe0244ZErmygUBA3nGSyVY6JP3ixD0,3740
|
14
14
|
schemathesis/cli/commands/data.py,sha256=_ALywjIeCZjuaoDQFy-Kj8RZkEGqXd-Y95O47h8Jszs,171
|
15
|
-
schemathesis/cli/commands/run/__init__.py,sha256=
|
15
|
+
schemathesis/cli/commands/run/__init__.py,sha256=_ApiSVh9q-TsJQ_-IiVBNnLCtTCDMTnOLwuJhOvbCp4,18925
|
16
16
|
schemathesis/cli/commands/run/context.py,sha256=taegOHWc_B-HDwiU1R9Oi4q57mdfLXc-B954QUj8t7A,7984
|
17
17
|
schemathesis/cli/commands/run/events.py,sha256=ew0TQOc9T2YBZynYWv95k9yfAk8-hGuZDLMxjT8EhvY,1595
|
18
18
|
schemathesis/cli/commands/run/executor.py,sha256=kFbZw583SZ-jqjv8goTp2yEJOpZ_bzecyTeZvdc6qTE,5327
|
@@ -21,9 +21,9 @@ schemathesis/cli/commands/run/loaders.py,sha256=6j0ez7wduAUYbUT28ELKxMf-dYEWr_67
|
|
21
21
|
schemathesis/cli/commands/run/validation.py,sha256=DQaMiBLN2tYT9hONvv8xnyPvNXZH768UlOdUxTd5kZs,9193
|
22
22
|
schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31MAXXn1qI7uU4FtiDwroXZI,1915
|
23
23
|
schemathesis/cli/commands/run/handlers/base.py,sha256=yDsTtCiztLksfk7cRzg8JlaAVOfS-zwK3tsJMOXAFyc,530
|
24
|
-
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=
|
24
|
+
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=_PBynFlQ5wSg7hEG316hOX4gbVrv6CpYHdFrcG1qeiA,19477
|
25
25
|
schemathesis/cli/commands/run/handlers/junitxml.py,sha256=ydk6Ofj-Uti6H8EucT4Snp85cmTA5W7uVpKkoHrIDKE,2586
|
26
|
-
schemathesis/cli/commands/run/handlers/output.py,sha256=
|
26
|
+
schemathesis/cli/commands/run/handlers/output.py,sha256=p1_vXzxrrVuC8cN3PV3e7PUjstReDInsfAdXeCUsn08,63727
|
27
27
|
schemathesis/cli/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
28
|
schemathesis/cli/ext/fs.py,sha256=3lvoAsEDDdih75ITJJNxemd3nwxX55gGWrI7uDxm0cM,447
|
29
29
|
schemathesis/cli/ext/groups.py,sha256=kQ37t6qeArcKaY2y5VxyK3_KwAkBKCVm58IYV8gewds,2720
|
@@ -36,23 +36,23 @@ schemathesis/config/_env.py,sha256=8XfIyrnGNQuCDnfG0lwmKRFbasRUjgeQGBAMupsmtOU,6
|
|
36
36
|
schemathesis/config/_error.py,sha256=jfv9chQ4NGoDYypszNGymr0zxXVo65yP0AWK1WVEPIM,5781
|
37
37
|
schemathesis/config/_generation.py,sha256=giWs4z17z9nRe_9Z3mAZ3LEoyh4hkcJnlAA6LSy6iEo,5210
|
38
38
|
schemathesis/config/_health_check.py,sha256=zC9inla5ibMBlEy5WyM4_TME7ju_KH3Bwfo21RI3Gks,561
|
39
|
-
schemathesis/config/_operations.py,sha256=
|
39
|
+
schemathesis/config/_operations.py,sha256=RhTr0EtJZV3z28-dh9-JUpLePQpj-eG6B952ykWoHaQ,12322
|
40
40
|
schemathesis/config/_output.py,sha256=3G9SOi-4oNcQPHeNRG3HggFCwvcKOW1kF28a9m0H-pU,4434
|
41
41
|
schemathesis/config/_parameters.py,sha256=i76Hwaf834fBAMmtKfKTl1SFCicJ-Y-5tZt5QNGW2fA,618
|
42
|
-
schemathesis/config/_phases.py,sha256=
|
43
|
-
schemathesis/config/_projects.py,sha256=
|
42
|
+
schemathesis/config/_phases.py,sha256=AR5AYBF12xr4iUTCJIwaFcHqnlo7o7QhHI1l1gof4uw,7755
|
43
|
+
schemathesis/config/_projects.py,sha256=NAUTp48OLr4wxWaLpgDoPT5NAPG40LvmKug7BNt4quk,20201
|
44
44
|
schemathesis/config/_rate_limit.py,sha256=ekEW-jP_Ichk_O6hYpj-h2TTTKfp7Fm0nyFUbvlWcbA,456
|
45
45
|
schemathesis/config/_report.py,sha256=ZECDpaCY4WWHD5UbjvgZoSjLz-rlTvfd5Ivzdgzqf2I,3891
|
46
46
|
schemathesis/config/_validator.py,sha256=IcE8geFZ0ZwR18rkIRs25i7pTl7Z84XbjYGUB-mqReU,258
|
47
47
|
schemathesis/config/_warnings.py,sha256=sI0VZcTj3dOnphhBwYwU_KTagxr89HGWTtQ99HcY84k,772
|
48
|
-
schemathesis/config/schema.json,sha256=
|
49
|
-
schemathesis/core/__init__.py,sha256=
|
48
|
+
schemathesis/config/schema.json,sha256=3CRSBfUK2vLVl1h5PfeK_YDdEoBZSojYl8Q1kT-ITLE,19846
|
49
|
+
schemathesis/core/__init__.py,sha256=h4gmDePIPvPiVuYxnjrpPKytSZPi6fZeVdTG6c822E4,1973
|
50
50
|
schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1058
|
51
51
|
schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
|
52
52
|
schemathesis/core/curl.py,sha256=yuaCe_zHLGwUjEeloQi6W3tOA3cGdnHDNI17-5jia0o,1723
|
53
53
|
schemathesis/core/deserialization.py,sha256=qjXUPaz_mc1OSgXzTUSkC8tuVR8wgVQtb9g3CcAF6D0,2951
|
54
54
|
schemathesis/core/errors.py,sha256=pwiyGhX7tId88Toe2H4ZYsCDc_OvUJtW8Wv-xDv2UD4,16361
|
55
|
-
schemathesis/core/failures.py,sha256=
|
55
|
+
schemathesis/core/failures.py,sha256=yFpAxWdEnm0Ri8z8RqRI9H7vcLH5ztOeSIi4m4SGx5g,8996
|
56
56
|
schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
|
57
57
|
schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
|
58
58
|
schemathesis/core/lazy_import.py,sha256=aMhWYgbU2JOltyWBb32vnWBb6kykOghucEzI_F70yVE,470
|
@@ -69,29 +69,30 @@ schemathesis/core/version.py,sha256=dOBUWrY3-uA2NQXJp9z7EtZgkR6jYeLg8sMhQCL1mcI,
|
|
69
69
|
schemathesis/core/output/__init__.py,sha256=SiHqONFskXl73AtP5dV29L14nZoKo7B-IeG52KZB32M,1446
|
70
70
|
schemathesis/core/output/sanitization.py,sha256=Ev3tae8dVwsYd7yVb2_1VBFYs92WFsQ4Eu1fGaymItE,2013
|
71
71
|
schemathesis/engine/__init__.py,sha256=QaFE-FinaTAaarteADo2RRMJ-Sz6hZB9TzD5KjMinIA,706
|
72
|
-
schemathesis/engine/context.py,sha256=
|
72
|
+
schemathesis/engine/context.py,sha256=Kz-Z_CEZPDgOe1uOJU7ElyjY7Q2E6V9rtFi-tlFov8I,5367
|
73
73
|
schemathesis/engine/control.py,sha256=FXzP8dxL47j1Giqpy2-Bsr_MdMw9YiATSK_UfpFwDtk,1348
|
74
|
-
schemathesis/engine/core.py,sha256=
|
74
|
+
schemathesis/engine/core.py,sha256=qlPHnZVq2RrUe93fOciXd1hC3E1gVyF2BIWMPMeLIj8,6655
|
75
75
|
schemathesis/engine/errors.py,sha256=wbFTOE0pmpU79oOzx1l-ypKG92wdFySmNzrN_KTNizM,19168
|
76
|
-
schemathesis/engine/events.py,sha256=
|
76
|
+
schemathesis/engine/events.py,sha256=jpCtMkWWfNe2jUeZh_Ly_wfZEF44EOodL-I_W4C9rgg,6594
|
77
|
+
schemathesis/engine/observations.py,sha256=T-5R8GeVIqvxpCMxc6vZ04UUxUTx3w7689r3Dc6bIcE,1416
|
77
78
|
schemathesis/engine/recorder.py,sha256=K3HfMARrT5mPWXPnYebjjcq5CcsBRhMrtZwEL9_Lvtg,8432
|
78
|
-
schemathesis/engine/phases/__init__.py,sha256=
|
79
|
-
schemathesis/engine/phases/probes.py,sha256=
|
79
|
+
schemathesis/engine/phases/__init__.py,sha256=7Yp7dQbd6-K9pavIJeURg6jiNeMpW8UU-Iiikr668ts,3278
|
80
|
+
schemathesis/engine/phases/probes.py,sha256=YogjJcZJcTMS8sMdGnG4oXKmMUj_4r_J7MY-BBJtCRU,5690
|
80
81
|
schemathesis/engine/phases/stateful/__init__.py,sha256=Lz1rgNqCfUSIz173XqCGsiMuUI5bh4L-RIFexU1-c_Q,2461
|
81
|
-
schemathesis/engine/phases/stateful/_executor.py,sha256=
|
82
|
+
schemathesis/engine/phases/stateful/_executor.py,sha256=6YOgAGynP4UKKTITe7S39SCcnRp5OXQ7fCc8BJIFW3A,15592
|
82
83
|
schemathesis/engine/phases/stateful/context.py,sha256=A7X1SLDOWFpCvFN9IiIeNVZM0emjqatmJL_k9UsO7vM,2946
|
83
|
-
schemathesis/engine/phases/unit/__init__.py,sha256=
|
84
|
-
schemathesis/engine/phases/unit/_executor.py,sha256=
|
84
|
+
schemathesis/engine/phases/unit/__init__.py,sha256=9dDcxyj887pktnE9YDIPNaR-vc7iqKQWIrFr77SbUTQ,8786
|
85
|
+
schemathesis/engine/phases/unit/_executor.py,sha256=pqUISTWvnXb61rgrnzwlFn7PyR-ZsGis2kbRgBMH4EA,16519
|
85
86
|
schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
|
86
87
|
schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
|
87
|
-
schemathesis/generation/case.py,sha256=
|
88
|
-
schemathesis/generation/coverage.py,sha256=
|
88
|
+
schemathesis/generation/case.py,sha256=Qc2_5JrWuUkCzAFTTgnVqNUJ2sioslmINTXiY7nHHgA,12326
|
89
|
+
schemathesis/generation/coverage.py,sha256=8kxz08sMe7u3yT09W1PtIg_Y-9f1O-hBTuPyeSge2pU,56569
|
89
90
|
schemathesis/generation/meta.py,sha256=yYR7EB1f5n7RrzWHZ6YATepurnnc_hEe7HnztRbaaA0,2699
|
90
91
|
schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz5-kt4,2836
|
91
92
|
schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
|
92
93
|
schemathesis/generation/overrides.py,sha256=OBWqDQPreiliaf2M-oyXppVKHoJkCRzxtwSJx1b6AFw,3759
|
93
94
|
schemathesis/generation/hypothesis/__init__.py,sha256=jK3G4i0SdcyhqwPQg91RH_yg437lSY-smeIQ-wZLWPc,1959
|
94
|
-
schemathesis/generation/hypothesis/builder.py,sha256=
|
95
|
+
schemathesis/generation/hypothesis/builder.py,sha256=HOZLev-b-fkaFwDIxQHlpS59y7anyK9mJ-qw5IeQZqY,36777
|
95
96
|
schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
|
96
97
|
schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
|
97
98
|
schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
|
@@ -103,14 +104,14 @@ schemathesis/graphql/checks.py,sha256=IADbxiZjgkBWrC5yzHDtohRABX6zKXk5w_zpWNwdzY
|
|
103
104
|
schemathesis/graphql/loaders.py,sha256=2tgG4HIvFmjHLr_KexVXnT8hSBM-dKG_fuXTZgE97So,9445
|
104
105
|
schemathesis/openapi/__init__.py,sha256=-KcsSAM19uOM0N5J4s-yTnQ1BFsptYhW1E51cEf6kVM,311
|
105
106
|
schemathesis/openapi/checks.py,sha256=VaQRxko6KwZL6saIzc4uUgJa_fj086O7Y6QFK8Zg-7A,12419
|
106
|
-
schemathesis/openapi/loaders.py,sha256
|
107
|
+
schemathesis/openapi/loaders.py,sha256=-DSFWcvD_PmekTyy0qZrJ1YYODh9C1KuAJJIytEnS1s,10733
|
107
108
|
schemathesis/openapi/generation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
108
109
|
schemathesis/openapi/generation/filters.py,sha256=pY9cUZdL_kQK80Z2aylTOqqa12zmaYUlYC5BfYgeQMk,2395
|
109
110
|
schemathesis/pytest/__init__.py,sha256=7W0q-Thcw03IAQfXE_Mo8JPZpUdHJzfu85fjK1ZdfQM,88
|
110
111
|
schemathesis/pytest/control_flow.py,sha256=F8rAPsPeNv_sJiJgbZYtTpwKWjauZmqFUaKroY2GmQI,217
|
111
112
|
schemathesis/pytest/lazy.py,sha256=u58q0orI0zisivLJKJkSo53RaQMPLSMiE0vJ1TQ9_uA,11073
|
112
113
|
schemathesis/pytest/loaders.py,sha256=Sbv8e5F77_x4amLP50iwubfm6kpOhx7LhLFGsVXW5Ys,925
|
113
|
-
schemathesis/pytest/plugin.py,sha256
|
114
|
+
schemathesis/pytest/plugin.py,sha256=-c92Qi2aEvW45KLAzHj9EDXbY_mYq5B5ZdkRY9Ks_DY,14463
|
114
115
|
schemathesis/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
115
116
|
schemathesis/python/asgi.py,sha256=5PyvuTBaivvyPUEi3pwJni91K1kX5Zc0u9c6c1D8a1Q,287
|
116
117
|
schemathesis/python/wsgi.py,sha256=uShAgo_NChbfYaV1117e6UHp0MTg7jaR0Sy_to3Jmf8,219
|
@@ -121,18 +122,18 @@ schemathesis/specs/graphql/scalars.py,sha256=6lew8mnwhrtg23leiEbG43mLGPLlRln8mCl
|
|
121
122
|
schemathesis/specs/graphql/schemas.py,sha256=B6FULWIIWoN_gx9OeQOd-6qWsz9yVg5h1XlxFe06XPM,14097
|
122
123
|
schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
|
123
124
|
schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
|
124
|
-
schemathesis/specs/openapi/_hypothesis.py,sha256
|
125
|
-
schemathesis/specs/openapi/checks.py,sha256=
|
125
|
+
schemathesis/specs/openapi/_hypothesis.py,sha256=XK-g2284wCsqOuPhubpE-PQ5_YotUwNodAnjeHs_-R0,21712
|
126
|
+
schemathesis/specs/openapi/checks.py,sha256=P4WaLJ_dqssrZI-fpRwsy4kZ8u6O93YvpQ-98JuMkIQ,30320
|
126
127
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
127
128
|
schemathesis/specs/openapi/converter.py,sha256=LkpCCAxZzET4Qa_3YStSNuhGlsm5G6TVwpxYu6lPO4g,4169
|
128
129
|
schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
|
129
130
|
schemathesis/specs/openapi/examples.py,sha256=JsAy8Dexw36SUAO5MBB44ji3zEK9Y_JU5d2LRshjEI4,22023
|
130
|
-
schemathesis/specs/openapi/formats.py,sha256=
|
131
|
+
schemathesis/specs/openapi/formats.py,sha256=4tYRdckauHxkJCmOhmdwDq_eOpHPaKloi89lzMPbPzw,3975
|
131
132
|
schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
|
132
133
|
schemathesis/specs/openapi/parameters.py,sha256=XpuZ2sex2aYUzKDK17GXVNWFBmvamuyVtloQ1oGQhLk,14468
|
133
134
|
schemathesis/specs/openapi/patterns.py,sha256=GqPZEXMRdWENQxanWjBOalIZ2MQUjuxk21kmdiI703E,18027
|
134
135
|
schemathesis/specs/openapi/references.py,sha256=40YcDExPLR2B8EOwt-Csw-5MYFi2xj_DXf91J0Pc9d4,8855
|
135
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
136
|
+
schemathesis/specs/openapi/schemas.py,sha256=qJ0ChkZuXQafQ-nPwMzckSk3iC32H2O5W9Cbd7DN_2I,51379
|
136
137
|
schemathesis/specs/openapi/security.py,sha256=6UWYMhL-dPtkTineqqBFNKca1i4EuoTduw-EOLeE0aQ,7149
|
137
138
|
schemathesis/specs/openapi/serialization.py,sha256=VdDLmeHqxlWM4cxQQcCkvrU6XurivolwEEaT13ohelA,11972
|
138
139
|
schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
|
@@ -148,15 +149,16 @@ schemathesis/specs/openapi/negative/types.py,sha256=a7buCcVxNBG6ILBM3A7oNTAX0lyD
|
|
148
149
|
schemathesis/specs/openapi/negative/utils.py,sha256=ozcOIuASufLqZSgnKUACjX-EOZrrkuNdXX0SDnLoGYA,168
|
149
150
|
schemathesis/specs/openapi/stateful/__init__.py,sha256=DaV7Uuo1GTgZF1JjjoQzbzZw5HQhL6pTByiWqIlWwy4,15996
|
150
151
|
schemathesis/specs/openapi/stateful/control.py,sha256=QaXLSbwQWtai5lxvvVtQV3BLJ8n5ePqSKB00XFxp-MA,3695
|
152
|
+
schemathesis/specs/openapi/stateful/inference.py,sha256=RSkmA_fyXreCxVpgnHKYNDRgT3av33HGU1NPNtPcg2g,9602
|
151
153
|
schemathesis/specs/openapi/stateful/links.py,sha256=h5q40jUbcIk5DS_Tih1cvFJxS_QxxG0_9ZQnTs1A_zo,8806
|
152
154
|
schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
|
153
155
|
schemathesis/transport/asgi.py,sha256=qTClt6oT_xUEWnRHokACN_uqCNNUZrRPT6YG0PjbElY,926
|
154
156
|
schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEzXd0o,4743
|
155
|
-
schemathesis/transport/requests.py,sha256=
|
157
|
+
schemathesis/transport/requests.py,sha256=XWiQVG4rGnFX0rOhOZAKVIPbrlknLuS7pHYwUcOiEGs,10942
|
156
158
|
schemathesis/transport/serialization.py,sha256=igUXKZ_VJ9gV7P0TUc5PDQBJXl_s0kK9T3ljGWWvo6E,10339
|
157
159
|
schemathesis/transport/wsgi.py,sha256=KoAfvu6RJtzyj24VGB8e-Iaa9smpgXJ3VsM8EgAz2tc,6152
|
158
|
-
schemathesis-4.
|
159
|
-
schemathesis-4.
|
160
|
-
schemathesis-4.
|
161
|
-
schemathesis-4.
|
162
|
-
schemathesis-4.
|
160
|
+
schemathesis-4.1.1.dist-info/METADATA,sha256=twjFIgwG3ZuiTWbHSwLnuRZtfy03aPhzwOwQQGb-TOo,8540
|
161
|
+
schemathesis-4.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
162
|
+
schemathesis-4.1.1.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
|
163
|
+
schemathesis-4.1.1.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
|
164
|
+
schemathesis-4.1.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|