schemathesis 3.39.13__py3-none-any.whl → 3.39.15__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 +9 -1
- schemathesis/generation/coverage.py +37 -2
- schemathesis/specs/openapi/examples.py +37 -20
- schemathesis/specs/openapi/parameters.py +3 -0
- schemathesis/specs/openapi/references.py +4 -1
- schemathesis/specs/openapi/schemas.py +31 -20
- schemathesis/transports/__init__.py +11 -1
- {schemathesis-3.39.13.dist-info → schemathesis-3.39.15.dist-info}/METADATA +1 -1
- {schemathesis-3.39.13.dist-info → schemathesis-3.39.15.dist-info}/RECORD +12 -12
- {schemathesis-3.39.13.dist-info → schemathesis-3.39.15.dist-info}/WHEEL +0 -0
- {schemathesis-3.39.13.dist-info → schemathesis-3.39.15.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.39.13.dist-info → schemathesis-3.39.15.dist-info}/licenses/LICENSE +0 -0
schemathesis/__init__.py
CHANGED
@@ -6,6 +6,7 @@ from . import auths, checks, contrib, experimental, fixups, graphql, hooks, runn
|
|
6
6
|
from ._lazy_import import lazy_import
|
7
7
|
from .constants import SCHEMATHESIS_VERSION
|
8
8
|
from .generation import DataGenerationMethod, GenerationConfig, HeaderConfig
|
9
|
+
from .hooks import HookContext
|
9
10
|
from .models import Case
|
10
11
|
from .specs import openapi
|
11
12
|
|
@@ -65,6 +66,7 @@ __all__ = [
|
|
65
66
|
"register_check",
|
66
67
|
"register_target",
|
67
68
|
"register_string_format",
|
69
|
+
"HookContext",
|
68
70
|
]
|
69
71
|
|
70
72
|
|
@@ -74,7 +76,13 @@ def _load_generic_response() -> Any:
|
|
74
76
|
return GenericResponse
|
75
77
|
|
76
78
|
|
77
|
-
|
79
|
+
def _load_base_schema() -> Any:
|
80
|
+
from .schemas import BaseSchema
|
81
|
+
|
82
|
+
return BaseSchema
|
83
|
+
|
84
|
+
|
85
|
+
_imports = {"GenericResponse": _load_generic_response, "BaseSchema": _load_base_schema}
|
78
86
|
|
79
87
|
|
80
88
|
def __getattr__(name: str) -> Any:
|
@@ -150,7 +150,7 @@ class CoverageContext:
|
|
150
150
|
|
151
151
|
def is_valid_for_location(self, value: Any) -> bool:
|
152
152
|
if self.location in ("header", "cookie") and isinstance(value, str):
|
153
|
-
return is_latin_1_encodable(value) and not has_invalid_characters("", value)
|
153
|
+
return not value or (is_latin_1_encodable(value) and not has_invalid_characters("", value))
|
154
154
|
return True
|
155
155
|
|
156
156
|
def generate_from(self, strategy: st.SearchStrategy) -> Any:
|
@@ -437,6 +437,36 @@ def cover_schema_iter(
|
|
437
437
|
elif key == "required":
|
438
438
|
template = template or ctx.generate_from_schema(_get_template_schema(schema, "object"))
|
439
439
|
yield from _negative_required(ctx, template, value)
|
440
|
+
elif key == "maxItems" and isinstance(value, int) and value < BUFFER_SIZE:
|
441
|
+
try:
|
442
|
+
# Force the array to have one more item than allowed
|
443
|
+
new_schema = {**schema, "minItems": value + 1, "maxItems": value + 1, "type": "array"}
|
444
|
+
array_value = ctx.generate_from_schema(new_schema)
|
445
|
+
k = _to_hashable_key(array_value)
|
446
|
+
if k not in seen:
|
447
|
+
yield NegativeValue(
|
448
|
+
array_value,
|
449
|
+
description="Array with more items than allowed by maxItems",
|
450
|
+
location=ctx.current_path,
|
451
|
+
)
|
452
|
+
seen.add(k)
|
453
|
+
except (InvalidArgument, Unsatisfiable):
|
454
|
+
pass
|
455
|
+
elif key == "minItems" and isinstance(value, int) and value > 0:
|
456
|
+
try:
|
457
|
+
# Force the array to have one less item than the minimum
|
458
|
+
new_schema = {**schema, "minItems": value - 1, "maxItems": value - 1, "type": "array"}
|
459
|
+
array_value = ctx.generate_from_schema(new_schema)
|
460
|
+
k = _to_hashable_key(array_value)
|
461
|
+
if k not in seen:
|
462
|
+
yield NegativeValue(
|
463
|
+
array_value,
|
464
|
+
description="Array with fewer items than allowed by minItems",
|
465
|
+
location=ctx.current_path,
|
466
|
+
)
|
467
|
+
seen.add(k)
|
468
|
+
except (InvalidArgument, Unsatisfiable):
|
469
|
+
pass
|
440
470
|
elif (
|
441
471
|
key == "additionalProperties"
|
442
472
|
and not value
|
@@ -770,7 +800,12 @@ def _negative_enum(
|
|
770
800
|
_hashed = _to_hashable_key(x)
|
771
801
|
return _hashed not in seen
|
772
802
|
|
773
|
-
strategy = (
|
803
|
+
strategy = (
|
804
|
+
st.text(alphabet=st.characters(min_codepoint=65, max_codepoint=122, categories=["L"]), min_size=3)
|
805
|
+
| st.none()
|
806
|
+
| st.booleans()
|
807
|
+
| NUMERIC_STRATEGY
|
808
|
+
).filter(is_not_in_value)
|
774
809
|
value = ctx.generate_from(strategy)
|
775
810
|
yield NegativeValue(value, description="Invalid enum value", location=ctx.current_path)
|
776
811
|
hashed = _to_hashable_key(value)
|
@@ -398,45 +398,62 @@ def find_matching_in_responses(examples: dict[str, list], param: str) -> Iterato
|
|
398
398
|
if not isinstance(example, dict):
|
399
399
|
continue
|
400
400
|
# Unwrapping example from `{"item": [{...}]}`
|
401
|
-
if isinstance(example, dict)
|
402
|
-
inner =
|
403
|
-
if
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
401
|
+
if isinstance(example, dict):
|
402
|
+
inner = next((value for key, value in example.items() if key.lower() == schema_name.lower()), None)
|
403
|
+
if inner is not None:
|
404
|
+
if isinstance(inner, list):
|
405
|
+
for sub_example in inner:
|
406
|
+
if isinstance(sub_example, dict):
|
407
|
+
for found in _find_matching_in_responses(
|
408
|
+
sub_example, schema_name, param, normalized, is_id_param
|
409
|
+
):
|
410
|
+
if found is not NOT_FOUND:
|
411
|
+
yield found
|
412
|
+
continue
|
413
|
+
if isinstance(inner, dict):
|
414
|
+
example = inner
|
415
|
+
for found in _find_matching_in_responses(example, schema_name, param, normalized, is_id_param):
|
416
|
+
if found is not NOT_FOUND:
|
417
|
+
yield found
|
414
418
|
|
415
419
|
|
416
420
|
def _find_matching_in_responses(
|
417
421
|
example: dict[str, Any], schema_name: str, param: str, normalized: str, is_id_param: bool
|
418
|
-
) -> Any:
|
422
|
+
) -> Iterator[Any]:
|
419
423
|
# Check for exact match
|
420
424
|
if param in example:
|
421
|
-
|
425
|
+
yield example[param]
|
426
|
+
return
|
422
427
|
if is_id_param and param[:-2] in example:
|
423
|
-
|
428
|
+
value = example[param[:-2]]
|
429
|
+
if isinstance(value, list):
|
430
|
+
for sub_example in value:
|
431
|
+
for found in _find_matching_in_responses(sub_example, schema_name, param, normalized, is_id_param):
|
432
|
+
if found is not NOT_FOUND:
|
433
|
+
yield found
|
434
|
+
return
|
435
|
+
else:
|
436
|
+
yield value
|
437
|
+
return
|
424
438
|
|
425
439
|
# Check for case-insensitive match
|
426
440
|
for key in example:
|
427
441
|
if key.lower() == normalized:
|
428
|
-
|
442
|
+
yield example[key]
|
443
|
+
return
|
429
444
|
else:
|
430
445
|
# If no match found and it's an ID parameter, try additional checks
|
431
446
|
if is_id_param:
|
432
447
|
# Check for 'id' if parameter is '{something}Id'
|
433
448
|
if "id" in example:
|
434
|
-
|
449
|
+
yield example["id"]
|
450
|
+
return
|
435
451
|
# Check for '{schemaName}Id' or '{schemaName}_id'
|
436
452
|
if normalized == "id" or normalized.startswith(schema_name.lower()):
|
437
453
|
for key in (schema_name, schema_name.lower()):
|
438
454
|
for suffix in ("_id", "Id"):
|
439
455
|
with_suffix = f"{key}{suffix}"
|
440
456
|
if with_suffix in example:
|
441
|
-
|
442
|
-
|
457
|
+
yield example[with_suffix]
|
458
|
+
return
|
459
|
+
yield NOT_FOUND
|
@@ -132,6 +132,7 @@ class OpenAPI20Parameter(OpenAPIParameter):
|
|
132
132
|
"multipleOf",
|
133
133
|
"example",
|
134
134
|
"examples",
|
135
|
+
"default",
|
135
136
|
)
|
136
137
|
|
137
138
|
|
@@ -176,6 +177,7 @@ class OpenAPI30Parameter(OpenAPIParameter):
|
|
176
177
|
"format",
|
177
178
|
"example",
|
178
179
|
"examples",
|
180
|
+
"default",
|
179
181
|
)
|
180
182
|
|
181
183
|
def from_open_api_to_json_schema(self, operation: APIOperation, open_api_schema: dict[str, Any]) -> dict[str, Any]:
|
@@ -228,6 +230,7 @@ class OpenAPI20Body(OpenAPIBody, OpenAPI20Parameter):
|
|
228
230
|
"additionalProperties",
|
229
231
|
"example",
|
230
232
|
"examples",
|
233
|
+
"default",
|
231
234
|
)
|
232
235
|
# NOTE. For Open API 2.0 bodies, we still give `x-example` precedence over the schema-level `example` field to keep
|
233
236
|
# the precedence rules consistent.
|
@@ -141,7 +141,10 @@ class ConvertingResolver(InliningResolver):
|
|
141
141
|
def resolve(self, ref: str) -> tuple[str, Any]:
|
142
142
|
url, document = super().resolve(ref)
|
143
143
|
document = to_json_schema_recursive(
|
144
|
-
document,
|
144
|
+
document,
|
145
|
+
nullable_name=self.nullable_name,
|
146
|
+
is_response_schema=self.is_response_schema,
|
147
|
+
update_quantifiers=False,
|
145
148
|
)
|
146
149
|
return url, document
|
147
150
|
|
@@ -1013,24 +1013,30 @@ class SwaggerV20(BaseOpenAPISchema):
|
|
1013
1013
|
content_types = self.get_request_payload_content_types(operation)
|
1014
1014
|
is_multipart = "multipart/form-data" in content_types
|
1015
1015
|
|
1016
|
-
|
1017
|
-
if isinstance(file_value, list):
|
1018
|
-
for item in file_value:
|
1019
|
-
files.append((name, (None, item)))
|
1020
|
-
else:
|
1021
|
-
files.append((name, file_value))
|
1016
|
+
known_fields: dict[str, dict] = {}
|
1022
1017
|
|
1023
1018
|
for parameter in operation.body:
|
1024
1019
|
if isinstance(parameter, OpenAPI20CompositeBody):
|
1025
1020
|
for form_parameter in parameter.definition:
|
1026
|
-
name = form_parameter.
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1021
|
+
known_fields[form_parameter.name] = form_parameter.definition
|
1022
|
+
|
1023
|
+
def add_file(name: str, value: Any) -> None:
|
1024
|
+
if isinstance(value, list):
|
1025
|
+
for item in value:
|
1026
|
+
files.append((name, (None, item)))
|
1027
|
+
else:
|
1028
|
+
files.append((name, value))
|
1029
|
+
|
1030
|
+
for name, value in form_data.items():
|
1031
|
+
param_def = known_fields.get(name)
|
1032
|
+
if param_def:
|
1033
|
+
if param_def.get("type") == "file" or is_multipart:
|
1034
|
+
add_file(name, value)
|
1035
|
+
else:
|
1036
|
+
data[name] = value
|
1037
|
+
else:
|
1038
|
+
# Unknown field — treat it as a file (safe default under multipart/form-data)
|
1039
|
+
add_file(name, value)
|
1034
1040
|
# `None` is the default value for `files` and `data` arguments in `requests.request`
|
1035
1041
|
return files or None, data or None
|
1036
1042
|
|
@@ -1188,14 +1194,19 @@ class OpenApi30(SwaggerV20):
|
|
1188
1194
|
break
|
1189
1195
|
else:
|
1190
1196
|
raise InternalError("No 'multipart/form-data' media type found in the schema")
|
1191
|
-
for name,
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1197
|
+
for name, value in form_data.items():
|
1198
|
+
property_schema = (schema or {}).get("properties", {}).get(name)
|
1199
|
+
if property_schema:
|
1200
|
+
if isinstance(value, list):
|
1201
|
+
files.extend([(name, item) for item in value])
|
1195
1202
|
elif property_schema.get("format") in ("binary", "base64"):
|
1196
|
-
files.append((name,
|
1203
|
+
files.append((name, value))
|
1197
1204
|
else:
|
1198
|
-
files.append((name, (None,
|
1205
|
+
files.append((name, (None, value)))
|
1206
|
+
elif isinstance(value, list):
|
1207
|
+
files.extend([(name, item) for item in value])
|
1208
|
+
else:
|
1209
|
+
files.append((name, (None, value)))
|
1199
1210
|
# `None` is the default value for `files` and `data` arguments in `requests.request`
|
1200
1211
|
return files or None, None
|
1201
1212
|
|
@@ -14,6 +14,7 @@ from .. import failures
|
|
14
14
|
from .._dependency_versions import IS_WERKZEUG_ABOVE_3
|
15
15
|
from ..constants import DEFAULT_RESPONSE_TIMEOUT, NOT_SET
|
16
16
|
from ..exceptions import get_timeout_error
|
17
|
+
from ..internal.copy import fast_deepcopy
|
17
18
|
from ..serializers import SerializerContext
|
18
19
|
from ..types import Cookies, NotSet, RequestCert
|
19
20
|
|
@@ -126,12 +127,21 @@ class RequestsTransport:
|
|
126
127
|
# Additional headers, needed for the serializer
|
127
128
|
for key, value in additional_headers.items():
|
128
129
|
final_headers.setdefault(key, value)
|
130
|
+
|
131
|
+
p = case.query
|
132
|
+
|
133
|
+
# Replace empty dictionaries with empty strings, so the parameters actually present in the query string
|
134
|
+
if any(value == {} for value in (p or {}).values()):
|
135
|
+
p = fast_deepcopy(p)
|
136
|
+
for k, v in p.items():
|
137
|
+
if v == {}:
|
138
|
+
p[k] = ""
|
129
139
|
data = {
|
130
140
|
"method": case.method,
|
131
141
|
"url": url,
|
132
142
|
"cookies": case.cookies,
|
133
143
|
"headers": final_headers,
|
134
|
-
"params":
|
144
|
+
"params": p,
|
135
145
|
**extra,
|
136
146
|
}
|
137
147
|
if params is not None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemathesis
|
3
|
-
Version: 3.39.
|
3
|
+
Version: 3.39.15
|
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://schemathesis.readthedocs.io/en/stable/changelog.html
|
@@ -1,4 +1,4 @@
|
|
1
|
-
schemathesis/__init__.py,sha256=
|
1
|
+
schemathesis/__init__.py,sha256=AGpMI329waZiHwU05CW7cB1cxqQsXvRPB5vs4N7ktB4,2090
|
2
2
|
schemathesis/_compat.py,sha256=y4RZd59i2NCnZ91VQhnKeMn_8t3SgvLOk2Xm8nymUHY,1837
|
3
3
|
schemathesis/_dependency_versions.py,sha256=pjEkkGAfOQJYNb-9UOo84V8nj_lKHr_TGDVdFwY2UU0,816
|
4
4
|
schemathesis/_hypothesis.py,sha256=CEfWX38CsPy-RzwMGdKuJD9mY_AV8fIq_ZhabGp4tW0,30759
|
@@ -61,7 +61,7 @@ schemathesis/fixups/utf8_bom.py,sha256=lWT9RNmJG8i-l5AXIpaCT3qCPUwRgzXPW3eoOjmZE
|
|
61
61
|
schemathesis/generation/__init__.py,sha256=PClFLK3bu-8Gsy71rgdD0ULMqySrzX-Um8Tan77x_5A,1628
|
62
62
|
schemathesis/generation/_hypothesis.py,sha256=74fzLPHugZgMQXerWYFAMqCAjtAXz5E4gek7Gnkhli4,1756
|
63
63
|
schemathesis/generation/_methods.py,sha256=r8oVlJ71_gXcnEhU-byw2E0R2RswQQFm8U7yGErSqbw,1204
|
64
|
-
schemathesis/generation/coverage.py,sha256=
|
64
|
+
schemathesis/generation/coverage.py,sha256=bE93UnTpp8FsILzbupgqcc-1pQL5Y2DykuIlwWUweM4,41279
|
65
65
|
schemathesis/internal/__init__.py,sha256=93HcdG3LF0BbQKbCteOsFMa1w6nXl8yTmx87QLNJOik,161
|
66
66
|
schemathesis/internal/checks.py,sha256=ZPvsPJ7gWwK0IpzBFgOMaq4L2e0yfeC8qxPrnpauVFA,2741
|
67
67
|
schemathesis/internal/copy.py,sha256=DcL56z-d69kKR_5u8mlHvjSL1UTyUKNMAwexrwHFY1s,1031
|
@@ -111,15 +111,15 @@ schemathesis/specs/openapi/checks.py,sha256=cuHTZsoHV2fdUz23_F99-mLelT1xtvaiS9Ec
|
|
111
111
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
112
112
|
schemathesis/specs/openapi/converter.py,sha256=Yxw9lS_JKEyi-oJuACT07fm04bqQDlAu-iHwzkeDvE4,3546
|
113
113
|
schemathesis/specs/openapi/definitions.py,sha256=WTkWwCgTc3OMxfKsqh6YDoGfZMTThSYrHGp8h0vLAK0,93935
|
114
|
-
schemathesis/specs/openapi/examples.py,sha256=
|
114
|
+
schemathesis/specs/openapi/examples.py,sha256=fpZ8gzc1wxgpcUeOpIYaS_WOC-O0oPCLI4tcqmgNpog,21082
|
115
115
|
schemathesis/specs/openapi/formats.py,sha256=3KtEC-8nQRwMErS-WpMadXsr8R0O-NzYwFisZqMuc-8,2761
|
116
116
|
schemathesis/specs/openapi/links.py,sha256=C4Uir2P_EcpqME8ee_a1vdUM8Tm3ZcKNn2YsGjZiMUQ,17935
|
117
117
|
schemathesis/specs/openapi/loaders.py,sha256=jlTYLoG5sVRh8xycIF2M2VDCZ44M80Sct07a_ycg1Po,25698
|
118
118
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
119
|
-
schemathesis/specs/openapi/parameters.py,sha256=
|
119
|
+
schemathesis/specs/openapi/parameters.py,sha256=fP4BupY_1wFbjL9n0lTtpQZY0YBt2mCjrG598LF8ZOI,14712
|
120
120
|
schemathesis/specs/openapi/patterns.py,sha256=L99UtslPvwObCVf5ndq3vL2YjQ7H1nMb-ZNMcyz_Qvk,12677
|
121
|
-
schemathesis/specs/openapi/references.py,sha256=
|
122
|
-
schemathesis/specs/openapi/schemas.py,sha256=
|
121
|
+
schemathesis/specs/openapi/references.py,sha256=0-gqbAxvBfrvFXA7YqmcNh7zRY7V_pKEnak0ncwQljI,9894
|
122
|
+
schemathesis/specs/openapi/schemas.py,sha256=bgtDwlHcXtw27bYx4mCj1GVnjO2ZazdMaWAxXESyo2I,54024
|
123
123
|
schemathesis/specs/openapi/security.py,sha256=Z-6pk2Ga1PTUtBe298KunjVHsNh5A-teegeso7zcPIE,7138
|
124
124
|
schemathesis/specs/openapi/serialization.py,sha256=rcZfqQbWer_RELedu4Sh5h_RhKYPWTfUjnmLwpP2R_A,11842
|
125
125
|
schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
|
@@ -147,14 +147,14 @@ schemathesis/stateful/sink.py,sha256=bHYlgh-fMwg1Srxk_XGs0-WV34YccotwH9PGrxCK57A
|
|
147
147
|
schemathesis/stateful/state_machine.py,sha256=EE1T0L21vBU0UHGiCmfPfIfnhU1WptB16h0t1iNVro0,13037
|
148
148
|
schemathesis/stateful/statistic.py,sha256=2-uU5xpT9CbMulKgJWLZN6MUpC0Fskf5yXTt4ef4NFA,542
|
149
149
|
schemathesis/stateful/validation.py,sha256=23qSZjC1_xRmtCX4OqsyG6pGxdlo6IZYid695ZpDQyU,3747
|
150
|
-
schemathesis/transports/__init__.py,sha256=
|
150
|
+
schemathesis/transports/__init__.py,sha256=9ahByCU8HNCWX8zc6ngGFrkW4kAHJZKGc1fpajKGYJU,13308
|
151
151
|
schemathesis/transports/asgi.py,sha256=bwW9vMd1h89Jh7I4jHJVwSNUQzHvc7-JOD5u4hSHZd8,212
|
152
152
|
schemathesis/transports/auth.py,sha256=urSTO9zgFO1qU69xvnKHPFQV0SlJL3d7_Ojl0tLnZwo,1143
|
153
153
|
schemathesis/transports/content_types.py,sha256=MiKOm-Hy5i75hrROPdpiBZPOTDzOwlCdnthJD12AJzI,2187
|
154
154
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
155
155
|
schemathesis/transports/responses.py,sha256=OFD4ZLqwEFpo7F9vaP_SVgjhxAqatxIj38FS4XVq8Qs,1680
|
156
|
-
schemathesis-3.39.
|
157
|
-
schemathesis-3.39.
|
158
|
-
schemathesis-3.39.
|
159
|
-
schemathesis-3.39.
|
160
|
-
schemathesis-3.39.
|
156
|
+
schemathesis-3.39.15.dist-info/METADATA,sha256=_4VLl1QXjO3WQhtKtojjaD68tz6wGveA2s1CMwWteUE,11901
|
157
|
+
schemathesis-3.39.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
158
|
+
schemathesis-3.39.15.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
159
|
+
schemathesis-3.39.15.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
160
|
+
schemathesis-3.39.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|