schemathesis 3.32.1__py3-none-any.whl → 3.33.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- schemathesis/cli/__init__.py +209 -33
- schemathesis/filters.py +88 -12
- schemathesis/hooks.py +1 -1
- schemathesis/internal/deprecation.py +5 -1
- schemathesis/lazy.py +108 -15
- schemathesis/models.py +1 -1
- schemathesis/runner/events.py +2 -1
- schemathesis/runner/impl/core.py +5 -6
- schemathesis/schemas.py +77 -1
- schemathesis/specs/graphql/schemas.py +13 -1
- schemathesis/specs/openapi/loaders.py +25 -12
- schemathesis/stateful/events.py +3 -2
- {schemathesis-3.32.1.dist-info → schemathesis-3.33.0.dist-info}/METADATA +1 -1
- {schemathesis-3.32.1.dist-info → schemathesis-3.33.0.dist-info}/RECORD +17 -17
- {schemathesis-3.32.1.dist-info → schemathesis-3.33.0.dist-info}/WHEEL +0 -0
- {schemathesis-3.32.1.dist-info → schemathesis-3.33.0.dist-info}/entry_points.txt +0 -0
- {schemathesis-3.32.1.dist-info → schemathesis-3.33.0.dist-info}/licenses/LICENSE +0 -0
schemathesis/cli/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ from collections import defaultdict
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from enum import Enum
|
|
13
13
|
from queue import Queue
|
|
14
|
-
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, NoReturn, cast
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Literal, NoReturn, Sequence, cast
|
|
15
15
|
from urllib.parse import urlparse
|
|
16
16
|
|
|
17
17
|
import click
|
|
@@ -34,6 +34,7 @@ from ..constants import (
|
|
|
34
34
|
WAIT_FOR_SCHEMA_ENV_VAR,
|
|
35
35
|
)
|
|
36
36
|
from ..exceptions import SchemaError, SchemaErrorType, extract_nth_traceback
|
|
37
|
+
from ..filters import FilterSet, expression_to_filter_function, is_deprecated
|
|
37
38
|
from ..fixups import ALL_FIXUPS
|
|
38
39
|
from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod
|
|
39
40
|
from ..hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, HookScope
|
|
@@ -41,7 +42,7 @@ from ..internal.datetime import current_datetime
|
|
|
41
42
|
from ..internal.output import OutputConfig
|
|
42
43
|
from ..internal.validation import file_exists
|
|
43
44
|
from ..loaders import load_app, load_yaml
|
|
44
|
-
from ..models import Case, CheckFunction
|
|
45
|
+
from ..models import APIOperation, Case, CheckFunction
|
|
45
46
|
from ..runner import events, prepare_hypothesis_settings, probes
|
|
46
47
|
from ..specs.graphql import loaders as gql_loaders
|
|
47
48
|
from ..specs.openapi import loaders as oas_loaders
|
|
@@ -216,6 +217,39 @@ with_hosts_file = click.option(
|
|
|
216
217
|
)
|
|
217
218
|
|
|
218
219
|
|
|
220
|
+
def _with_filter(*, by: str, mode: Literal["include", "exclude"], modifier: Literal["regex"] | None = None) -> Callable:
|
|
221
|
+
"""Generate a CLI option for filtering API operations."""
|
|
222
|
+
param = f"--{mode}-{by}"
|
|
223
|
+
action = "include in" if mode == "include" else "exclude from"
|
|
224
|
+
prop = {
|
|
225
|
+
"operation-id": "ID",
|
|
226
|
+
"name": "Operation name",
|
|
227
|
+
}.get(by, by.capitalize())
|
|
228
|
+
if modifier:
|
|
229
|
+
param += f"-{modifier}"
|
|
230
|
+
prop += " pattern"
|
|
231
|
+
help_text = f"{prop} to {action} testing."
|
|
232
|
+
return click.option(
|
|
233
|
+
param,
|
|
234
|
+
help=help_text,
|
|
235
|
+
type=str,
|
|
236
|
+
multiple=modifier is None,
|
|
237
|
+
cls=GroupedOption,
|
|
238
|
+
group=ParameterGroup.filtering,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
_BY_VALUES = ("operation-id", "tag", "name", "method", "path")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def with_filters(command: Callable) -> Callable:
|
|
246
|
+
for by in _BY_VALUES:
|
|
247
|
+
for mode in ("exclude", "include"):
|
|
248
|
+
for modifier in ("regex", None):
|
|
249
|
+
command = _with_filter(by=by, mode=mode, modifier=modifier)(command) # type: ignore[arg-type]
|
|
250
|
+
return command
|
|
251
|
+
|
|
252
|
+
|
|
219
253
|
class ReportToService:
|
|
220
254
|
pass
|
|
221
255
|
|
|
@@ -362,6 +396,33 @@ REPORT_TO_SERVICE = ReportToService()
|
|
|
362
396
|
type=str,
|
|
363
397
|
callback=callbacks.validate_headers,
|
|
364
398
|
)
|
|
399
|
+
@with_filters
|
|
400
|
+
@click.option(
|
|
401
|
+
"--include-by",
|
|
402
|
+
"include_by",
|
|
403
|
+
type=str,
|
|
404
|
+
help="Include API operations by expression",
|
|
405
|
+
cls=GroupedOption,
|
|
406
|
+
group=ParameterGroup.filtering,
|
|
407
|
+
)
|
|
408
|
+
@click.option(
|
|
409
|
+
"--exclude-by",
|
|
410
|
+
"exclude_by",
|
|
411
|
+
type=str,
|
|
412
|
+
help="Exclude API operations by expression",
|
|
413
|
+
cls=GroupedOption,
|
|
414
|
+
group=ParameterGroup.filtering,
|
|
415
|
+
)
|
|
416
|
+
@click.option(
|
|
417
|
+
"--exclude-deprecated",
|
|
418
|
+
help="Exclude deprecated API operations from testing.",
|
|
419
|
+
is_flag=True,
|
|
420
|
+
is_eager=True,
|
|
421
|
+
default=False,
|
|
422
|
+
show_default=True,
|
|
423
|
+
cls=GroupedOption,
|
|
424
|
+
group=ParameterGroup.filtering,
|
|
425
|
+
)
|
|
365
426
|
@click.option(
|
|
366
427
|
"--endpoint",
|
|
367
428
|
"-E",
|
|
@@ -779,10 +840,33 @@ def run(
|
|
|
779
840
|
exit_first: bool = False,
|
|
780
841
|
max_failures: int | None = None,
|
|
781
842
|
dry_run: bool = False,
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
843
|
+
include_path: Sequence[str] = (),
|
|
844
|
+
include_path_regex: str | None = None,
|
|
845
|
+
include_method: Sequence[str] = (),
|
|
846
|
+
include_method_regex: str | None = None,
|
|
847
|
+
include_name: Sequence[str] = (),
|
|
848
|
+
include_name_regex: str | None = None,
|
|
849
|
+
include_tag: Sequence[str] = (),
|
|
850
|
+
include_tag_regex: str | None = None,
|
|
851
|
+
include_operation_id: Sequence[str] = (),
|
|
852
|
+
include_operation_id_regex: str | None = None,
|
|
853
|
+
exclude_path: Sequence[str] = (),
|
|
854
|
+
exclude_path_regex: str | None = None,
|
|
855
|
+
exclude_method: Sequence[str] = (),
|
|
856
|
+
exclude_method_regex: str | None = None,
|
|
857
|
+
exclude_name: Sequence[str] = (),
|
|
858
|
+
exclude_name_regex: str | None = None,
|
|
859
|
+
exclude_tag: Sequence[str] = (),
|
|
860
|
+
exclude_tag_regex: str | None = None,
|
|
861
|
+
exclude_operation_id: Sequence[str] = (),
|
|
862
|
+
exclude_operation_id_regex: str | None = None,
|
|
863
|
+
include_by: str | None = None,
|
|
864
|
+
exclude_by: str | None = None,
|
|
865
|
+
exclude_deprecated: bool = False,
|
|
866
|
+
endpoints: tuple[str, ...] = (),
|
|
867
|
+
methods: tuple[str, ...] = (),
|
|
868
|
+
tags: tuple[str, ...] = (),
|
|
869
|
+
operation_ids: tuple[str, ...] = (),
|
|
786
870
|
workers_num: int = DEFAULT_WORKERS,
|
|
787
871
|
base_url: str | None = None,
|
|
788
872
|
app: str | None = None,
|
|
@@ -897,6 +981,107 @@ def run(
|
|
|
897
981
|
|
|
898
982
|
output_config = OutputConfig(truncate=output_truncate)
|
|
899
983
|
|
|
984
|
+
deprecated_filters = {
|
|
985
|
+
"--method": "--include-method",
|
|
986
|
+
"--endpoint": "--include-path",
|
|
987
|
+
"--tag": "--include-tag",
|
|
988
|
+
"--operation-id": "--include-operation-id",
|
|
989
|
+
}
|
|
990
|
+
for values, arg_name in (
|
|
991
|
+
(include_path, "--include-path"),
|
|
992
|
+
(include_method, "--include-method"),
|
|
993
|
+
(include_name, "--include-name"),
|
|
994
|
+
(include_tag, "--include-tag"),
|
|
995
|
+
(include_operation_id, "--include-operation-id"),
|
|
996
|
+
(exclude_path, "--exclude-path"),
|
|
997
|
+
(exclude_method, "--exclude-method"),
|
|
998
|
+
(exclude_name, "--exclude-name"),
|
|
999
|
+
(exclude_tag, "--exclude-tag"),
|
|
1000
|
+
(exclude_operation_id, "--exclude-operation-id"),
|
|
1001
|
+
(methods, "--method"),
|
|
1002
|
+
(endpoints, "--endpoint"),
|
|
1003
|
+
(tags, "--tag"),
|
|
1004
|
+
(operation_ids, "--operation-id"),
|
|
1005
|
+
):
|
|
1006
|
+
if values and arg_name in deprecated_filters:
|
|
1007
|
+
replacement = deprecated_filters[arg_name]
|
|
1008
|
+
click.secho(
|
|
1009
|
+
f"Warning: Option `{arg_name}` is deprecated and will be removed in Schemathesis 4.0. "
|
|
1010
|
+
f"Use `{replacement}` instead.",
|
|
1011
|
+
fg="yellow",
|
|
1012
|
+
)
|
|
1013
|
+
_ensure_unique_filter(values, arg_name)
|
|
1014
|
+
include_by_function = _filter_by_expression_to_func(include_by, "--include-by")
|
|
1015
|
+
exclude_by_function = _filter_by_expression_to_func(exclude_by, "--exclude-by")
|
|
1016
|
+
|
|
1017
|
+
filter_set = FilterSet()
|
|
1018
|
+
if include_by_function:
|
|
1019
|
+
filter_set.include(include_by_function)
|
|
1020
|
+
for name_ in include_name:
|
|
1021
|
+
filter_set.include(name=name_)
|
|
1022
|
+
for method in include_method:
|
|
1023
|
+
filter_set.include(method=method)
|
|
1024
|
+
if methods:
|
|
1025
|
+
for method in methods:
|
|
1026
|
+
filter_set.include(method_regex=method)
|
|
1027
|
+
for path in include_path:
|
|
1028
|
+
filter_set.include(path=path)
|
|
1029
|
+
if endpoints:
|
|
1030
|
+
for endpoint in endpoints:
|
|
1031
|
+
filter_set.include(path_regex=endpoint)
|
|
1032
|
+
for tag in include_tag:
|
|
1033
|
+
filter_set.include(tag=tag)
|
|
1034
|
+
if tags:
|
|
1035
|
+
for tag in tags:
|
|
1036
|
+
filter_set.include(tag_regex=tag)
|
|
1037
|
+
for operation_id in include_operation_id:
|
|
1038
|
+
filter_set.include(operation_id=operation_id)
|
|
1039
|
+
if operation_ids:
|
|
1040
|
+
for operation_id in operation_ids:
|
|
1041
|
+
filter_set.include(operation_id_regex=operation_id)
|
|
1042
|
+
if (
|
|
1043
|
+
include_name_regex
|
|
1044
|
+
or include_method_regex
|
|
1045
|
+
or include_path_regex
|
|
1046
|
+
or include_tag_regex
|
|
1047
|
+
or include_operation_id_regex
|
|
1048
|
+
):
|
|
1049
|
+
filter_set.include(
|
|
1050
|
+
name_regex=include_name_regex,
|
|
1051
|
+
method_regex=include_method_regex,
|
|
1052
|
+
path_regex=include_path_regex,
|
|
1053
|
+
tag_regex=include_tag_regex,
|
|
1054
|
+
operation_id_regex=include_operation_id_regex,
|
|
1055
|
+
)
|
|
1056
|
+
if exclude_by_function:
|
|
1057
|
+
filter_set.exclude(exclude_by_function)
|
|
1058
|
+
for name_ in exclude_name:
|
|
1059
|
+
filter_set.exclude(name=name_)
|
|
1060
|
+
for method in exclude_method:
|
|
1061
|
+
filter_set.exclude(method=method)
|
|
1062
|
+
for path in exclude_path:
|
|
1063
|
+
filter_set.exclude(path=path)
|
|
1064
|
+
for tag in exclude_tag:
|
|
1065
|
+
filter_set.exclude(tag=tag)
|
|
1066
|
+
for operation_id in exclude_operation_id:
|
|
1067
|
+
filter_set.exclude(operation_id=operation_id)
|
|
1068
|
+
if (
|
|
1069
|
+
exclude_name_regex
|
|
1070
|
+
or exclude_method_regex
|
|
1071
|
+
or exclude_path_regex
|
|
1072
|
+
or exclude_tag_regex
|
|
1073
|
+
or exclude_operation_id_regex
|
|
1074
|
+
):
|
|
1075
|
+
filter_set.exclude(
|
|
1076
|
+
name_regex=exclude_name_regex,
|
|
1077
|
+
method_regex=exclude_method_regex,
|
|
1078
|
+
path_regex=exclude_path_regex,
|
|
1079
|
+
tag_regex=exclude_tag_regex,
|
|
1080
|
+
operation_id_regex=exclude_operation_id_regex,
|
|
1081
|
+
)
|
|
1082
|
+
if exclude_deprecated or skip_deprecated_operations:
|
|
1083
|
+
filter_set.exclude(is_deprecated)
|
|
1084
|
+
|
|
900
1085
|
schemathesis_io_hostname = urlparse(schemathesis_io_url).netloc
|
|
901
1086
|
token = schemathesis_io_token or service.hosts.get_token(hostname=schemathesis_io_hostname, hosts_file=hosts_file)
|
|
902
1087
|
schema_kind = callbacks.parse_schema_kind(schema, app)
|
|
@@ -986,7 +1171,6 @@ def run(
|
|
|
986
1171
|
base_url=base_url,
|
|
987
1172
|
started_at=started_at,
|
|
988
1173
|
validate_schema=validate_schema,
|
|
989
|
-
skip_deprecated_operations=skip_deprecated_operations,
|
|
990
1174
|
data_generation_methods=data_generation_methods,
|
|
991
1175
|
force_schema_version=force_schema_version,
|
|
992
1176
|
request_tls_verify=request_tls_verify,
|
|
@@ -997,10 +1181,6 @@ def run(
|
|
|
997
1181
|
auth_type=auth_type,
|
|
998
1182
|
override=override,
|
|
999
1183
|
headers=headers,
|
|
1000
|
-
endpoint=endpoints or None,
|
|
1001
|
-
method=methods or None,
|
|
1002
|
-
tag=tags or None,
|
|
1003
|
-
operation_id=operation_ids or None,
|
|
1004
1184
|
request_timeout=request_timeout,
|
|
1005
1185
|
seed=hypothesis_seed,
|
|
1006
1186
|
exit_first=exit_first,
|
|
@@ -1018,6 +1198,7 @@ def run(
|
|
|
1018
1198
|
generation_config=generation_config,
|
|
1019
1199
|
output_config=output_config,
|
|
1020
1200
|
service_client=client,
|
|
1201
|
+
filter_set=filter_set,
|
|
1021
1202
|
)
|
|
1022
1203
|
execute(
|
|
1023
1204
|
event_stream,
|
|
@@ -1048,6 +1229,21 @@ def run(
|
|
|
1048
1229
|
)
|
|
1049
1230
|
|
|
1050
1231
|
|
|
1232
|
+
def _ensure_unique_filter(values: Sequence[str], arg_name: str) -> None:
|
|
1233
|
+
if len(values) != len(set(values)):
|
|
1234
|
+
duplicates = ",".join(sorted({value for value in values if values.count(value) > 1}))
|
|
1235
|
+
raise click.UsageError(f"Duplicate values are not allowed for `{arg_name}`: {duplicates}")
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def _filter_by_expression_to_func(value: str | None, arg_name: str) -> Callable | None:
|
|
1239
|
+
if value:
|
|
1240
|
+
try:
|
|
1241
|
+
return expression_to_filter_function(value)
|
|
1242
|
+
except ValueError:
|
|
1243
|
+
raise click.UsageError(f"Invalid expression for {arg_name}: {value}") from None
|
|
1244
|
+
return None
|
|
1245
|
+
|
|
1246
|
+
|
|
1051
1247
|
def prepare_request_cert(cert: str | None, key: str | None) -> RequestCert | None:
|
|
1052
1248
|
if cert is not None and key is not None:
|
|
1053
1249
|
return cert, key
|
|
@@ -1065,7 +1261,6 @@ class LoaderConfig:
|
|
|
1065
1261
|
app: Any
|
|
1066
1262
|
base_url: str | None
|
|
1067
1263
|
validate_schema: bool
|
|
1068
|
-
skip_deprecated_operations: bool
|
|
1069
1264
|
data_generation_methods: tuple[DataGenerationMethod, ...]
|
|
1070
1265
|
force_schema_version: str | None
|
|
1071
1266
|
request_tls_verify: bool | str
|
|
@@ -1079,11 +1274,6 @@ class LoaderConfig:
|
|
|
1079
1274
|
auth: tuple[str, str] | None
|
|
1080
1275
|
auth_type: str | None
|
|
1081
1276
|
headers: dict[str, str] | None
|
|
1082
|
-
# Schema filters
|
|
1083
|
-
endpoint: Filter | None
|
|
1084
|
-
method: Filter | None
|
|
1085
|
-
tag: Filter | None
|
|
1086
|
-
operation_id: Filter | None
|
|
1087
1277
|
|
|
1088
1278
|
|
|
1089
1279
|
def into_event_stream(
|
|
@@ -1093,7 +1283,6 @@ def into_event_stream(
|
|
|
1093
1283
|
base_url: str | None,
|
|
1094
1284
|
started_at: str,
|
|
1095
1285
|
validate_schema: bool,
|
|
1096
|
-
skip_deprecated_operations: bool,
|
|
1097
1286
|
data_generation_methods: tuple[DataGenerationMethod, ...],
|
|
1098
1287
|
force_schema_version: str | None,
|
|
1099
1288
|
request_tls_verify: bool | str,
|
|
@@ -1106,11 +1295,7 @@ def into_event_stream(
|
|
|
1106
1295
|
headers: dict[str, str] | None,
|
|
1107
1296
|
request_timeout: int | None,
|
|
1108
1297
|
wait_for_schema: float | None,
|
|
1109
|
-
|
|
1110
|
-
endpoint: Filter | None,
|
|
1111
|
-
method: Filter | None,
|
|
1112
|
-
tag: Filter | None,
|
|
1113
|
-
operation_id: Filter | None,
|
|
1298
|
+
filter_set: FilterSet,
|
|
1114
1299
|
# Runtime behavior
|
|
1115
1300
|
checks: Iterable[CheckFunction],
|
|
1116
1301
|
max_response_time: int | None,
|
|
@@ -1137,7 +1322,6 @@ def into_event_stream(
|
|
|
1137
1322
|
app=app,
|
|
1138
1323
|
base_url=base_url,
|
|
1139
1324
|
validate_schema=validate_schema,
|
|
1140
|
-
skip_deprecated_operations=skip_deprecated_operations,
|
|
1141
1325
|
data_generation_methods=data_generation_methods,
|
|
1142
1326
|
force_schema_version=force_schema_version,
|
|
1143
1327
|
request_proxy=request_proxy,
|
|
@@ -1148,14 +1332,11 @@ def into_event_stream(
|
|
|
1148
1332
|
auth=auth,
|
|
1149
1333
|
auth_type=auth_type,
|
|
1150
1334
|
headers=headers,
|
|
1151
|
-
endpoint=endpoint or None,
|
|
1152
|
-
method=method or None,
|
|
1153
|
-
tag=tag or None,
|
|
1154
|
-
operation_id=operation_id or None,
|
|
1155
1335
|
output_config=output_config,
|
|
1156
1336
|
generation_config=generation_config,
|
|
1157
1337
|
)
|
|
1158
1338
|
schema = load_schema(config)
|
|
1339
|
+
schema.filter_set = filter_set
|
|
1159
1340
|
yield from runner.from_schema(
|
|
1160
1341
|
schema,
|
|
1161
1342
|
auth=auth,
|
|
@@ -1293,11 +1474,6 @@ def get_loader_kwargs(loader: Callable, config: LoaderConfig) -> dict[str, Any]:
|
|
|
1293
1474
|
kwargs = {
|
|
1294
1475
|
"app": config.app,
|
|
1295
1476
|
"base_url": config.base_url,
|
|
1296
|
-
"method": config.method,
|
|
1297
|
-
"endpoint": config.endpoint,
|
|
1298
|
-
"tag": config.tag,
|
|
1299
|
-
"operation_id": config.operation_id,
|
|
1300
|
-
"skip_deprecated_operations": config.skip_deprecated_operations,
|
|
1301
1477
|
"validate_schema": config.validate_schema,
|
|
1302
1478
|
"force_schema_version": config.force_schema_version,
|
|
1303
1479
|
"data_generation_methods": config.data_generation_methods,
|
schemathesis/filters.py
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import re
|
|
6
7
|
from dataclasses import dataclass, field
|
|
7
8
|
from functools import partial
|
|
8
9
|
from types import SimpleNamespace
|
|
9
|
-
from typing import TYPE_CHECKING, Callable, List, Protocol, Union
|
|
10
|
-
|
|
11
|
-
from .types import NotSet, Filter as FilterType
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Protocol, Union
|
|
12
11
|
|
|
13
12
|
from .exceptions import UsageError
|
|
13
|
+
from .types import Filter as FilterType
|
|
14
|
+
from .types import NotSet
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
16
17
|
from .models import APIOperation
|
|
@@ -143,9 +144,29 @@ class FilterSet:
|
|
|
143
144
|
|
|
144
145
|
__slots__ = ("_includes", "_excludes")
|
|
145
146
|
|
|
146
|
-
def __init__(self) -> None:
|
|
147
|
-
self._includes = set()
|
|
148
|
-
self._excludes = set()
|
|
147
|
+
def __init__(self, _includes: set[Filter] | None = None, _excludes: set[Filter] | None = None) -> None:
|
|
148
|
+
self._includes = _includes or set()
|
|
149
|
+
self._excludes = _excludes or set()
|
|
150
|
+
|
|
151
|
+
def clone(self) -> FilterSet:
|
|
152
|
+
return FilterSet(_includes=self._includes.copy(), _excludes=self._excludes.copy())
|
|
153
|
+
|
|
154
|
+
def merge(self, other: FilterSet) -> FilterSet:
|
|
155
|
+
def _merge(lhs: set[Filter], rhs: set[Filter]) -> set[Filter]:
|
|
156
|
+
result = lhs.copy()
|
|
157
|
+
for new in rhs:
|
|
158
|
+
for old in lhs:
|
|
159
|
+
for new_matcher in new.matchers:
|
|
160
|
+
for old_matcher in old.matchers:
|
|
161
|
+
if "=" in new_matcher.label and "=" in old_matcher.label:
|
|
162
|
+
if new_matcher.label.split("=")[0] == old_matcher.label.split("=")[0]:
|
|
163
|
+
result.remove(old)
|
|
164
|
+
result.add(new)
|
|
165
|
+
return result
|
|
166
|
+
|
|
167
|
+
return FilterSet(
|
|
168
|
+
_includes=_merge(self._includes, other._includes), _excludes=_merge(self._excludes, other._excludes)
|
|
169
|
+
)
|
|
149
170
|
|
|
150
171
|
def apply_to(self, operations: list[APIOperation]) -> list[APIOperation]:
|
|
151
172
|
"""Get a filtered list of the given operations that match the filters."""
|
|
@@ -304,8 +325,12 @@ def attach_filter_chain(
|
|
|
304
325
|
name_regex: str | None = None,
|
|
305
326
|
method: FilterValue | None = None,
|
|
306
327
|
method_regex: str | None = None,
|
|
328
|
+
tag: FilterValue | None = None,
|
|
329
|
+
tag_regex: RegexValue | None = None,
|
|
307
330
|
path: FilterValue | None = None,
|
|
308
331
|
path_regex: str | None = None,
|
|
332
|
+
operation_id: FilterValue | None = None,
|
|
333
|
+
operation_id_regex: RegexValue | None = None,
|
|
309
334
|
) -> Callable:
|
|
310
335
|
__tracebackhide__ = True
|
|
311
336
|
filter_func(
|
|
@@ -314,8 +339,12 @@ def attach_filter_chain(
|
|
|
314
339
|
name_regex=name_regex,
|
|
315
340
|
method=method,
|
|
316
341
|
method_regex=method_regex,
|
|
342
|
+
tag=tag,
|
|
343
|
+
tag_regex=tag_regex,
|
|
317
344
|
path=path,
|
|
318
345
|
path_regex=path_regex,
|
|
346
|
+
operation_id=operation_id,
|
|
347
|
+
operation_id_regex=operation_id_regex,
|
|
319
348
|
)
|
|
320
349
|
return target
|
|
321
350
|
|
|
@@ -325,6 +354,10 @@ def attach_filter_chain(
|
|
|
325
354
|
setattr(target, attribute, proxy)
|
|
326
355
|
|
|
327
356
|
|
|
357
|
+
def is_deprecated(ctx: HasAPIOperation) -> bool:
|
|
358
|
+
return ctx.operation.definition.raw.get("deprecated") is True
|
|
359
|
+
|
|
360
|
+
|
|
328
361
|
def filter_set_from_components(
|
|
329
362
|
*,
|
|
330
363
|
include: bool,
|
|
@@ -338,9 +371,6 @@ def filter_set_from_components(
|
|
|
338
371
|
def _is_defined(x: FilterType | None) -> bool:
|
|
339
372
|
return x is not None and not isinstance(x, NotSet)
|
|
340
373
|
|
|
341
|
-
def _is_deprecated(ctx: HasAPIOperation) -> bool:
|
|
342
|
-
return ctx.operation.definition.raw.get("deprecated") is True
|
|
343
|
-
|
|
344
374
|
def _prepare_filter(filter_: FilterType | None) -> RegexValue | None:
|
|
345
375
|
if filter_ is None or isinstance(filter_, NotSet):
|
|
346
376
|
return None
|
|
@@ -359,9 +389,9 @@ def filter_set_from_components(
|
|
|
359
389
|
operation_id_regex=_prepare_filter(operation_id),
|
|
360
390
|
)
|
|
361
391
|
if skip_deprecated_operations is True and not any(
|
|
362
|
-
matcher.label ==
|
|
392
|
+
matcher.label == is_deprecated.__name__ for exclude_ in new._excludes for matcher in exclude_.matchers
|
|
363
393
|
):
|
|
364
|
-
new.exclude(func=
|
|
394
|
+
new.exclude(func=is_deprecated)
|
|
365
395
|
# Merge with the parent filter set
|
|
366
396
|
if parent is not None:
|
|
367
397
|
for include_ in parent._includes:
|
|
@@ -387,10 +417,56 @@ def filter_set_from_components(
|
|
|
387
417
|
matchers = exclude_.matchers
|
|
388
418
|
ids = []
|
|
389
419
|
for idx, matcher in enumerate(exclude_.matchers):
|
|
390
|
-
if skip_deprecated_operations is False and matcher.label ==
|
|
420
|
+
if skip_deprecated_operations is False and matcher.label == is_deprecated.__name__:
|
|
391
421
|
ids.append(idx)
|
|
392
422
|
if ids:
|
|
393
423
|
matchers = tuple(matcher for idx, matcher in enumerate(matchers) if idx not in ids)
|
|
394
424
|
if matchers:
|
|
395
425
|
new._excludes.add(exclude_)
|
|
396
426
|
return new
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def parse_expression(expression: str) -> tuple[str, str, Any]:
|
|
430
|
+
expression = expression.strip()
|
|
431
|
+
|
|
432
|
+
# Find the operator
|
|
433
|
+
for op in ("==", "!="):
|
|
434
|
+
try:
|
|
435
|
+
pointer, value = expression.split(op, 1)
|
|
436
|
+
break
|
|
437
|
+
except ValueError:
|
|
438
|
+
continue
|
|
439
|
+
else:
|
|
440
|
+
raise ValueError(f"Invalid expression: {expression}")
|
|
441
|
+
|
|
442
|
+
pointer = pointer.strip()
|
|
443
|
+
value = value.strip()
|
|
444
|
+
if not pointer or not value:
|
|
445
|
+
raise ValueError(f"Invalid expression: {expression}")
|
|
446
|
+
# Parse the JSON value
|
|
447
|
+
try:
|
|
448
|
+
return pointer, op, json.loads(value)
|
|
449
|
+
except json.JSONDecodeError:
|
|
450
|
+
# If it's not valid JSON, treat it as a string
|
|
451
|
+
return pointer, op, value
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def expression_to_filter_function(expression: str) -> Callable[[HasAPIOperation], bool]:
|
|
455
|
+
from .specs.openapi.references import resolve_pointer
|
|
456
|
+
|
|
457
|
+
pointer, op, value = parse_expression(expression)
|
|
458
|
+
|
|
459
|
+
if op == "==":
|
|
460
|
+
|
|
461
|
+
def filter_function(ctx: HasAPIOperation) -> bool:
|
|
462
|
+
definition = ctx.operation.definition.resolved
|
|
463
|
+
resolved = resolve_pointer(definition, pointer)
|
|
464
|
+
return resolved == value
|
|
465
|
+
else:
|
|
466
|
+
|
|
467
|
+
def filter_function(ctx: HasAPIOperation) -> bool:
|
|
468
|
+
definition = ctx.operation.definition.resolved
|
|
469
|
+
resolved = resolve_pointer(definition, pointer)
|
|
470
|
+
return resolved != value
|
|
471
|
+
|
|
472
|
+
return filter_function
|
schemathesis/hooks.py
CHANGED
|
@@ -42,7 +42,7 @@ class HookContext:
|
|
|
42
42
|
|
|
43
43
|
operation: APIOperation | None = None
|
|
44
44
|
|
|
45
|
-
@deprecated_property(removed_in="4.0", replacement="operation")
|
|
45
|
+
@deprecated_property(removed_in="4.0", replacement="`operation`")
|
|
46
46
|
def endpoint(self) -> APIOperation | None:
|
|
47
47
|
return self.operation
|
|
48
48
|
|
|
@@ -5,7 +5,7 @@ from typing import Any, Callable
|
|
|
5
5
|
def _warn_deprecation(*, kind: str, thing: str, removed_in: str, replacement: str) -> None:
|
|
6
6
|
warnings.warn(
|
|
7
7
|
f"{kind} `{thing}` is deprecated and will be removed in Schemathesis {removed_in}. "
|
|
8
|
-
f"Use
|
|
8
|
+
f"Use {replacement} instead.",
|
|
9
9
|
DeprecationWarning,
|
|
10
10
|
stacklevel=1,
|
|
11
11
|
)
|
|
@@ -23,6 +23,10 @@ def deprecated_property(*, removed_in: str, replacement: str) -> Callable:
|
|
|
23
23
|
return wrapper
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def warn_filtration_arguments(name: str) -> None:
|
|
27
|
+
_warn_deprecation(kind="Argument", thing=name, removed_in="4.0", replacement="`include` and `exclude` methods")
|
|
28
|
+
|
|
29
|
+
|
|
26
30
|
def deprecated_function(*, removed_in: str, replacement: str) -> Callable:
|
|
27
31
|
def wrapper(func: Callable) -> Callable:
|
|
28
32
|
def inner(*args: Any, **kwargs: Any) -> Any:
|
schemathesis/lazy.py
CHANGED
|
@@ -19,9 +19,10 @@ from .auths import AuthStorage
|
|
|
19
19
|
from .code_samples import CodeSampleStyle
|
|
20
20
|
from .constants import FLAKY_FAILURE_MESSAGE, NOT_SET
|
|
21
21
|
from .exceptions import CheckFailed, OperationSchemaError, SkipTest, get_grouped_exception
|
|
22
|
-
from .filters import filter_set_from_components
|
|
22
|
+
from .filters import FilterSet, FilterValue, MatcherFunc, RegexValue, filter_set_from_components, is_deprecated
|
|
23
23
|
from .generation import DataGenerationMethodInput, GenerationConfig
|
|
24
24
|
from .hooks import HookDispatcher, HookScope
|
|
25
|
+
from .internal.deprecation import warn_filtration_arguments
|
|
25
26
|
from .internal.output import OutputConfig
|
|
26
27
|
from .internal.result import Ok
|
|
27
28
|
from .models import APIOperation
|
|
@@ -43,15 +44,11 @@ from .utils import (
|
|
|
43
44
|
class LazySchema:
|
|
44
45
|
fixture_name: str
|
|
45
46
|
base_url: str | None | NotSet = NOT_SET
|
|
46
|
-
method: Filter | None = NOT_SET
|
|
47
|
-
endpoint: Filter | None = NOT_SET
|
|
48
|
-
tag: Filter | None = NOT_SET
|
|
49
|
-
operation_id: Filter | None = NOT_SET
|
|
50
47
|
app: Any = NOT_SET
|
|
48
|
+
filter_set: FilterSet = field(default_factory=FilterSet)
|
|
51
49
|
hooks: HookDispatcher = field(default_factory=lambda: HookDispatcher(scope=HookScope.SCHEMA))
|
|
52
50
|
auth: AuthStorage = field(default_factory=AuthStorage)
|
|
53
51
|
validate_schema: bool = True
|
|
54
|
-
skip_deprecated_operations: bool = False
|
|
55
52
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET
|
|
56
53
|
generation_config: GenerationConfig | NotSet = NOT_SET
|
|
57
54
|
output_config: OutputConfig | NotSet = NOT_SET
|
|
@@ -59,6 +56,104 @@ class LazySchema:
|
|
|
59
56
|
rate_limiter: Limiter | None = None
|
|
60
57
|
sanitize_output: bool = True
|
|
61
58
|
|
|
59
|
+
def include(
|
|
60
|
+
self,
|
|
61
|
+
func: MatcherFunc | None = None,
|
|
62
|
+
*,
|
|
63
|
+
name: FilterValue | None = None,
|
|
64
|
+
name_regex: str | None = None,
|
|
65
|
+
method: FilterValue | None = None,
|
|
66
|
+
method_regex: str | None = None,
|
|
67
|
+
path: FilterValue | None = None,
|
|
68
|
+
path_regex: str | None = None,
|
|
69
|
+
tag: FilterValue | None = None,
|
|
70
|
+
tag_regex: RegexValue | None = None,
|
|
71
|
+
operation_id: FilterValue | None = None,
|
|
72
|
+
operation_id_regex: RegexValue | None = None,
|
|
73
|
+
) -> LazySchema:
|
|
74
|
+
"""Include only operations that match the given filters."""
|
|
75
|
+
filter_set = self.filter_set.clone()
|
|
76
|
+
filter_set.include(
|
|
77
|
+
func,
|
|
78
|
+
name=name,
|
|
79
|
+
name_regex=name_regex,
|
|
80
|
+
method=method,
|
|
81
|
+
method_regex=method_regex,
|
|
82
|
+
path=path,
|
|
83
|
+
path_regex=path_regex,
|
|
84
|
+
tag=tag,
|
|
85
|
+
tag_regex=tag_regex,
|
|
86
|
+
operation_id=operation_id,
|
|
87
|
+
operation_id_regex=operation_id_regex,
|
|
88
|
+
)
|
|
89
|
+
return self.__class__(
|
|
90
|
+
fixture_name=self.fixture_name,
|
|
91
|
+
base_url=self.base_url,
|
|
92
|
+
app=self.app,
|
|
93
|
+
hooks=self.hooks,
|
|
94
|
+
auth=self.auth,
|
|
95
|
+
validate_schema=self.validate_schema,
|
|
96
|
+
data_generation_methods=self.data_generation_methods,
|
|
97
|
+
generation_config=self.generation_config,
|
|
98
|
+
output_config=self.output_config,
|
|
99
|
+
code_sample_style=self.code_sample_style,
|
|
100
|
+
rate_limiter=self.rate_limiter,
|
|
101
|
+
sanitize_output=self.sanitize_output,
|
|
102
|
+
filter_set=filter_set,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def exclude(
|
|
106
|
+
self,
|
|
107
|
+
func: MatcherFunc | None = None,
|
|
108
|
+
*,
|
|
109
|
+
name: FilterValue | None = None,
|
|
110
|
+
name_regex: str | None = None,
|
|
111
|
+
method: FilterValue | None = None,
|
|
112
|
+
method_regex: str | None = None,
|
|
113
|
+
path: FilterValue | None = None,
|
|
114
|
+
path_regex: str | None = None,
|
|
115
|
+
tag: FilterValue | None = None,
|
|
116
|
+
tag_regex: RegexValue | None = None,
|
|
117
|
+
operation_id: FilterValue | None = None,
|
|
118
|
+
operation_id_regex: RegexValue | None = None,
|
|
119
|
+
deprecated: bool = False,
|
|
120
|
+
) -> LazySchema:
|
|
121
|
+
"""Exclude operations that match the given filters."""
|
|
122
|
+
filter_set = self.filter_set.clone()
|
|
123
|
+
if deprecated:
|
|
124
|
+
if func is None:
|
|
125
|
+
func = is_deprecated
|
|
126
|
+
else:
|
|
127
|
+
filter_set.exclude(is_deprecated)
|
|
128
|
+
filter_set.exclude(
|
|
129
|
+
func,
|
|
130
|
+
name=name,
|
|
131
|
+
name_regex=name_regex,
|
|
132
|
+
method=method,
|
|
133
|
+
method_regex=method_regex,
|
|
134
|
+
path=path,
|
|
135
|
+
path_regex=path_regex,
|
|
136
|
+
tag=tag,
|
|
137
|
+
tag_regex=tag_regex,
|
|
138
|
+
operation_id=operation_id,
|
|
139
|
+
operation_id_regex=operation_id_regex,
|
|
140
|
+
)
|
|
141
|
+
return self.__class__(
|
|
142
|
+
fixture_name=self.fixture_name,
|
|
143
|
+
base_url=self.base_url,
|
|
144
|
+
app=self.app,
|
|
145
|
+
hooks=self.hooks,
|
|
146
|
+
auth=self.auth,
|
|
147
|
+
validate_schema=self.validate_schema,
|
|
148
|
+
data_generation_methods=self.data_generation_methods,
|
|
149
|
+
generation_config=self.generation_config,
|
|
150
|
+
output_config=self.output_config,
|
|
151
|
+
code_sample_style=self.code_sample_style,
|
|
152
|
+
rate_limiter=self.rate_limiter,
|
|
153
|
+
sanitize_output=self.sanitize_output,
|
|
154
|
+
filter_set=filter_set,
|
|
155
|
+
)
|
|
156
|
+
|
|
62
157
|
def hook(self, hook: str | Callable) -> Callable:
|
|
63
158
|
return self.hooks.register(hook)
|
|
64
159
|
|
|
@@ -75,14 +170,10 @@ class LazySchema:
|
|
|
75
170
|
output_config: OutputConfig | NotSet = NOT_SET,
|
|
76
171
|
code_sample_style: str | NotSet = NOT_SET,
|
|
77
172
|
) -> Callable:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if tag is NOT_SET:
|
|
83
|
-
tag = self.tag
|
|
84
|
-
if operation_id is NOT_SET:
|
|
85
|
-
operation_id = self.operation_id
|
|
173
|
+
for name in ("method", "endpoint", "tag", "operation_id", "skip_deprecated_operations"):
|
|
174
|
+
value = locals()[name]
|
|
175
|
+
if value is not NOT_SET:
|
|
176
|
+
warn_filtration_arguments(name)
|
|
86
177
|
if data_generation_methods is NOT_SET:
|
|
87
178
|
data_generation_methods = self.data_generation_methods
|
|
88
179
|
if generation_config is NOT_SET:
|
|
@@ -133,6 +224,7 @@ class LazySchema:
|
|
|
133
224
|
app=self.app,
|
|
134
225
|
rate_limiter=self.rate_limiter,
|
|
135
226
|
sanitize_output=self.sanitize_output,
|
|
227
|
+
filter_set=self.filter_set,
|
|
136
228
|
)
|
|
137
229
|
fixtures = get_fixtures(test, request, given_kwargs)
|
|
138
230
|
# Changing the node id is required for better reporting - the method and path will appear there
|
|
@@ -325,6 +417,7 @@ def get_schema(
|
|
|
325
417
|
endpoint: Filter | None = None,
|
|
326
418
|
tag: Filter | None = None,
|
|
327
419
|
operation_id: Filter | None = None,
|
|
420
|
+
filter_set: FilterSet,
|
|
328
421
|
app: Any = None,
|
|
329
422
|
test_function: GenericTest,
|
|
330
423
|
hooks: HookDispatcher,
|
|
@@ -350,7 +443,7 @@ def get_schema(
|
|
|
350
443
|
tag=tag,
|
|
351
444
|
operation_id=operation_id,
|
|
352
445
|
skip_deprecated_operations=skip_deprecated_operations,
|
|
353
|
-
parent=schema.filter_set,
|
|
446
|
+
parent=schema.filter_set.merge(filter_set),
|
|
354
447
|
)
|
|
355
448
|
return schema.clone(
|
|
356
449
|
base_url=base_url,
|
schemathesis/models.py
CHANGED
|
@@ -174,7 +174,7 @@ class Case:
|
|
|
174
174
|
def __hash__(self) -> int:
|
|
175
175
|
return hash(self.as_curl_command({SCHEMATHESIS_TEST_CASE_HEADER: "0"}))
|
|
176
176
|
|
|
177
|
-
@deprecated_property(removed_in="4.0", replacement="operation")
|
|
177
|
+
@deprecated_property(removed_in="4.0", replacement="`operation`")
|
|
178
178
|
def endpoint(self) -> APIOperation:
|
|
179
179
|
return self.operation
|
|
180
180
|
|
schemathesis/runner/events.py
CHANGED
|
@@ -10,7 +10,6 @@ from ..exceptions import RuntimeErrorType, SchemaError, SchemaErrorType, format_
|
|
|
10
10
|
from ..generation import DataGenerationMethod
|
|
11
11
|
from ..internal.datetime import current_datetime
|
|
12
12
|
from ..internal.result import Err, Ok, Result
|
|
13
|
-
from ..service.models import AnalysisSuccess
|
|
14
13
|
from .serialization import SerializedError, SerializedTestResult
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
@@ -107,6 +106,8 @@ class AfterAnalysis(ExecutionEvent):
|
|
|
107
106
|
analysis: Result[AnalysisResult, Exception] | None
|
|
108
107
|
|
|
109
108
|
def _serialize(self) -> dict[str, Any]:
|
|
109
|
+
from ..service.models import AnalysisSuccess
|
|
110
|
+
|
|
110
111
|
data = {}
|
|
111
112
|
if isinstance(self.analysis, Ok):
|
|
112
113
|
result = self.analysis.ok()
|
schemathesis/runner/impl/core.py
CHANGED
|
@@ -535,8 +535,10 @@ def run_test(
|
|
|
535
535
|
if isinstance(exc.__cause__, hypothesis.errors.DeadlineExceeded):
|
|
536
536
|
status = Status.error
|
|
537
537
|
result.add_error(DeadlineExceeded.from_exc(exc.__cause__))
|
|
538
|
-
elif
|
|
539
|
-
|
|
538
|
+
elif (
|
|
539
|
+
hasattr(hypothesis.errors, "FlakyFailure")
|
|
540
|
+
and isinstance(exc, hypothesis.errors.FlakyFailure)
|
|
541
|
+
and any(isinstance(subexc, hypothesis.errors.DeadlineExceeded) for subexc in exc.exceptions)
|
|
540
542
|
):
|
|
541
543
|
for sub_exc in exc.exceptions:
|
|
542
544
|
if isinstance(sub_exc, hypothesis.errors.DeadlineExceeded):
|
|
@@ -579,8 +581,7 @@ def run_test(
|
|
|
579
581
|
result.mark_errored()
|
|
580
582
|
for error in deduplicate_errors(errors):
|
|
581
583
|
result.add_error(error)
|
|
582
|
-
except hypothesis.errors.
|
|
583
|
-
# Hypothesis >= 6.108.0
|
|
584
|
+
except hypothesis.errors.Flaky as exc:
|
|
584
585
|
status = _on_flaky(exc)
|
|
585
586
|
except MultipleFailures:
|
|
586
587
|
# Schemathesis may detect multiple errors that come from different check results
|
|
@@ -590,8 +591,6 @@ def run_test(
|
|
|
590
591
|
add_errors(result, errors)
|
|
591
592
|
else:
|
|
592
593
|
status = Status.failure
|
|
593
|
-
except hypothesis.errors.Flaky as exc:
|
|
594
|
-
status = _on_flaky(exc)
|
|
595
594
|
except hypothesis.errors.Unsatisfiable:
|
|
596
595
|
# We need more clear error message here
|
|
597
596
|
status = Status.error
|
schemathesis/schemas.py
CHANGED
|
@@ -37,7 +37,7 @@ from .auths import AuthStorage
|
|
|
37
37
|
from .code_samples import CodeSampleStyle
|
|
38
38
|
from .constants import NOT_SET
|
|
39
39
|
from .exceptions import OperationSchemaError, UsageError
|
|
40
|
-
from .filters import FilterSet, filter_set_from_components
|
|
40
|
+
from .filters import FilterSet, FilterValue, MatcherFunc, RegexValue, filter_set_from_components, is_deprecated
|
|
41
41
|
from .generation import (
|
|
42
42
|
DEFAULT_DATA_GENERATION_METHODS,
|
|
43
43
|
DataGenerationMethod,
|
|
@@ -45,6 +45,7 @@ from .generation import (
|
|
|
45
45
|
GenerationConfig,
|
|
46
46
|
)
|
|
47
47
|
from .hooks import HookContext, HookDispatcher, HookScope, dispatch
|
|
48
|
+
from .internal.deprecation import warn_filtration_arguments
|
|
48
49
|
from .internal.output import OutputConfig
|
|
49
50
|
from .internal.result import Ok, Result
|
|
50
51
|
from .models import APIOperation, Case
|
|
@@ -97,6 +98,76 @@ class BaseSchema(Mapping):
|
|
|
97
98
|
rate_limiter: Limiter | None = None
|
|
98
99
|
sanitize_output: bool = True
|
|
99
100
|
|
|
101
|
+
def include(
|
|
102
|
+
self,
|
|
103
|
+
func: MatcherFunc | None = None,
|
|
104
|
+
*,
|
|
105
|
+
name: FilterValue | None = None,
|
|
106
|
+
name_regex: str | None = None,
|
|
107
|
+
method: FilterValue | None = None,
|
|
108
|
+
method_regex: str | None = None,
|
|
109
|
+
path: FilterValue | None = None,
|
|
110
|
+
path_regex: str | None = None,
|
|
111
|
+
tag: FilterValue | None = None,
|
|
112
|
+
tag_regex: RegexValue | None = None,
|
|
113
|
+
operation_id: FilterValue | None = None,
|
|
114
|
+
operation_id_regex: RegexValue | None = None,
|
|
115
|
+
) -> BaseSchema:
|
|
116
|
+
"""Include only operations that match the given filters."""
|
|
117
|
+
filter_set = self.filter_set.clone()
|
|
118
|
+
filter_set.include(
|
|
119
|
+
func,
|
|
120
|
+
name=name,
|
|
121
|
+
name_regex=name_regex,
|
|
122
|
+
method=method,
|
|
123
|
+
method_regex=method_regex,
|
|
124
|
+
path=path,
|
|
125
|
+
path_regex=path_regex,
|
|
126
|
+
tag=tag,
|
|
127
|
+
tag_regex=tag_regex,
|
|
128
|
+
operation_id=operation_id,
|
|
129
|
+
operation_id_regex=operation_id_regex,
|
|
130
|
+
)
|
|
131
|
+
return self.clone(filter_set=filter_set)
|
|
132
|
+
|
|
133
|
+
def exclude(
|
|
134
|
+
self,
|
|
135
|
+
func: MatcherFunc | None = None,
|
|
136
|
+
*,
|
|
137
|
+
name: FilterValue | None = None,
|
|
138
|
+
name_regex: str | None = None,
|
|
139
|
+
method: FilterValue | None = None,
|
|
140
|
+
method_regex: str | None = None,
|
|
141
|
+
path: FilterValue | None = None,
|
|
142
|
+
path_regex: str | None = None,
|
|
143
|
+
tag: FilterValue | None = None,
|
|
144
|
+
tag_regex: RegexValue | None = None,
|
|
145
|
+
operation_id: FilterValue | None = None,
|
|
146
|
+
operation_id_regex: RegexValue | None = None,
|
|
147
|
+
deprecated: bool = False,
|
|
148
|
+
) -> BaseSchema:
|
|
149
|
+
"""Include only operations that match the given filters."""
|
|
150
|
+
filter_set = self.filter_set.clone()
|
|
151
|
+
if deprecated:
|
|
152
|
+
if func is None:
|
|
153
|
+
func = is_deprecated
|
|
154
|
+
else:
|
|
155
|
+
filter_set.exclude(is_deprecated)
|
|
156
|
+
filter_set.exclude(
|
|
157
|
+
func,
|
|
158
|
+
name=name,
|
|
159
|
+
name_regex=name_regex,
|
|
160
|
+
method=method,
|
|
161
|
+
method_regex=method_regex,
|
|
162
|
+
path=path,
|
|
163
|
+
path_regex=path_regex,
|
|
164
|
+
tag=tag,
|
|
165
|
+
tag_regex=tag_regex,
|
|
166
|
+
operation_id=operation_id,
|
|
167
|
+
operation_id_regex=operation_id_regex,
|
|
168
|
+
)
|
|
169
|
+
return self.clone(filter_set=filter_set)
|
|
170
|
+
|
|
100
171
|
def __iter__(self) -> Iterator[str]:
|
|
101
172
|
raise NotImplementedError
|
|
102
173
|
|
|
@@ -239,6 +310,11 @@ class BaseSchema(Mapping):
|
|
|
239
310
|
CodeSampleStyle.from_str(code_sample_style) if isinstance(code_sample_style, str) else code_sample_style
|
|
240
311
|
)
|
|
241
312
|
|
|
313
|
+
for name in ("method", "endpoint", "tag", "operation_id", "skip_deprecated_operations"):
|
|
314
|
+
value = locals()[name]
|
|
315
|
+
if value is not NOT_SET:
|
|
316
|
+
warn_filtration_arguments(name)
|
|
317
|
+
|
|
242
318
|
filter_set = filter_set_from_components(
|
|
243
319
|
include=True,
|
|
244
320
|
method=method,
|
|
@@ -5,6 +5,7 @@ import time
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from difflib import get_close_matches
|
|
7
7
|
from enum import unique
|
|
8
|
+
from types import SimpleNamespace
|
|
8
9
|
from typing import (
|
|
9
10
|
TYPE_CHECKING,
|
|
10
11
|
Any,
|
|
@@ -184,7 +185,8 @@ class GraphQLSchema(BaseSchema):
|
|
|
184
185
|
return 0
|
|
185
186
|
|
|
186
187
|
def get_all_operations(
|
|
187
|
-
self,
|
|
188
|
+
self,
|
|
189
|
+
hooks: HookDispatcher | None = None,
|
|
188
190
|
) -> Generator[Result[APIOperation, OperationSchemaError], None, None]:
|
|
189
191
|
schema = self.client_schema
|
|
190
192
|
for root_type, operation_type in (
|
|
@@ -195,6 +197,8 @@ class GraphQLSchema(BaseSchema):
|
|
|
195
197
|
continue
|
|
196
198
|
for field_name, field_ in operation_type.fields.items():
|
|
197
199
|
operation = self._build_operation(root_type, operation_type, field_name, field_)
|
|
200
|
+
if self._should_skip(operation):
|
|
201
|
+
continue
|
|
198
202
|
context = HookContext(operation=operation)
|
|
199
203
|
if (
|
|
200
204
|
should_skip_operation(GLOBAL_HOOK_DISPATCHER, context)
|
|
@@ -204,6 +208,14 @@ class GraphQLSchema(BaseSchema):
|
|
|
204
208
|
continue
|
|
205
209
|
yield Ok(operation)
|
|
206
210
|
|
|
211
|
+
def _should_skip(
|
|
212
|
+
self,
|
|
213
|
+
operation: APIOperation,
|
|
214
|
+
_ctx_cache: SimpleNamespace = SimpleNamespace(operation=None),
|
|
215
|
+
) -> bool:
|
|
216
|
+
_ctx_cache.operation = operation
|
|
217
|
+
return not self.filter_set.match(_ctx_cache)
|
|
218
|
+
|
|
207
219
|
def _build_operation(
|
|
208
220
|
self,
|
|
209
221
|
root_type: RootType,
|
|
@@ -19,6 +19,7 @@ from ...generation import (
|
|
|
19
19
|
GenerationConfig,
|
|
20
20
|
)
|
|
21
21
|
from ...hooks import HookContext, dispatch
|
|
22
|
+
from ...internal.deprecation import warn_filtration_arguments
|
|
22
23
|
from ...internal.output import OutputConfig
|
|
23
24
|
from ...internal.validation import require_relative_url
|
|
24
25
|
from ...loaders import load_schema_from_url, load_yaml
|
|
@@ -76,7 +77,7 @@ def from_path(
|
|
|
76
77
|
endpoint: Filter | None = None,
|
|
77
78
|
tag: Filter | None = None,
|
|
78
79
|
operation_id: Filter | None = None,
|
|
79
|
-
skip_deprecated_operations: bool =
|
|
80
|
+
skip_deprecated_operations: bool | None = None,
|
|
80
81
|
validate_schema: bool = False,
|
|
81
82
|
force_schema_version: str | None = None,
|
|
82
83
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -126,7 +127,7 @@ def from_uri(
|
|
|
126
127
|
endpoint: Filter | None = None,
|
|
127
128
|
tag: Filter | None = None,
|
|
128
129
|
operation_id: Filter | None = None,
|
|
129
|
-
skip_deprecated_operations: bool =
|
|
130
|
+
skip_deprecated_operations: bool | None = None,
|
|
130
131
|
validate_schema: bool = False,
|
|
131
132
|
force_schema_version: str | None = None,
|
|
132
133
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -222,7 +223,7 @@ def from_file(
|
|
|
222
223
|
endpoint: Filter | None = None,
|
|
223
224
|
tag: Filter | None = None,
|
|
224
225
|
operation_id: Filter | None = None,
|
|
225
|
-
skip_deprecated_operations: bool =
|
|
226
|
+
skip_deprecated_operations: bool | None = None,
|
|
226
227
|
validate_schema: bool = False,
|
|
227
228
|
force_schema_version: str | None = None,
|
|
228
229
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -298,7 +299,7 @@ def from_dict(
|
|
|
298
299
|
endpoint: Filter | None = None,
|
|
299
300
|
tag: Filter | None = None,
|
|
300
301
|
operation_id: Filter | None = None,
|
|
301
|
-
skip_deprecated_operations: bool =
|
|
302
|
+
skip_deprecated_operations: bool | None = None,
|
|
302
303
|
validate_schema: bool = False,
|
|
303
304
|
force_schema_version: str | None = None,
|
|
304
305
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -331,6 +332,10 @@ def from_dict(
|
|
|
331
332
|
if rate_limit is not None:
|
|
332
333
|
rate_limiter = build_limiter(rate_limit)
|
|
333
334
|
|
|
335
|
+
for name in ("method", "endpoint", "tag", "operation_id", "skip_deprecated_operations"):
|
|
336
|
+
value = locals()[name]
|
|
337
|
+
if value is not None:
|
|
338
|
+
warn_filtration_arguments(name)
|
|
334
339
|
filter_set = filter_set_from_components(
|
|
335
340
|
include=True,
|
|
336
341
|
method=method,
|
|
@@ -475,7 +480,7 @@ def from_pytest_fixture(
|
|
|
475
480
|
endpoint: Filter | None = NOT_SET,
|
|
476
481
|
tag: Filter | None = NOT_SET,
|
|
477
482
|
operation_id: Filter | None = NOT_SET,
|
|
478
|
-
skip_deprecated_operations: bool =
|
|
483
|
+
skip_deprecated_operations: bool | None = None,
|
|
479
484
|
validate_schema: bool = False,
|
|
480
485
|
data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
|
|
481
486
|
generation_config: GenerationConfig | NotSet = NOT_SET,
|
|
@@ -505,15 +510,23 @@ def from_pytest_fixture(
|
|
|
505
510
|
rate_limiter: Limiter | None = None
|
|
506
511
|
if rate_limit is not None:
|
|
507
512
|
rate_limiter = build_limiter(rate_limit)
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
513
|
+
for name in ("method", "endpoint", "tag", "operation_id", "skip_deprecated_operations"):
|
|
514
|
+
value = locals()[name]
|
|
515
|
+
if value is not None:
|
|
516
|
+
warn_filtration_arguments(name)
|
|
517
|
+
filter_set = filter_set_from_components(
|
|
518
|
+
include=True,
|
|
512
519
|
method=method,
|
|
513
520
|
endpoint=endpoint,
|
|
514
521
|
tag=tag,
|
|
515
522
|
operation_id=operation_id,
|
|
516
523
|
skip_deprecated_operations=skip_deprecated_operations,
|
|
524
|
+
)
|
|
525
|
+
return LazySchema(
|
|
526
|
+
fixture_name,
|
|
527
|
+
app=app,
|
|
528
|
+
base_url=base_url,
|
|
529
|
+
filter_set=filter_set,
|
|
517
530
|
validate_schema=validate_schema,
|
|
518
531
|
data_generation_methods=_data_generation_methods,
|
|
519
532
|
generation_config=generation_config,
|
|
@@ -533,7 +546,7 @@ def from_wsgi(
|
|
|
533
546
|
endpoint: Filter | None = None,
|
|
534
547
|
tag: Filter | None = None,
|
|
535
548
|
operation_id: Filter | None = None,
|
|
536
|
-
skip_deprecated_operations: bool =
|
|
549
|
+
skip_deprecated_operations: bool | None = None,
|
|
537
550
|
validate_schema: bool = False,
|
|
538
551
|
force_schema_version: str | None = None,
|
|
539
552
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -598,7 +611,7 @@ def from_aiohttp(
|
|
|
598
611
|
endpoint: Filter | None = None,
|
|
599
612
|
tag: Filter | None = None,
|
|
600
613
|
operation_id: Filter | None = None,
|
|
601
|
-
skip_deprecated_operations: bool =
|
|
614
|
+
skip_deprecated_operations: bool | None = None,
|
|
602
615
|
validate_schema: bool = False,
|
|
603
616
|
force_schema_version: str | None = None,
|
|
604
617
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
|
@@ -648,7 +661,7 @@ def from_asgi(
|
|
|
648
661
|
endpoint: Filter | None = None,
|
|
649
662
|
tag: Filter | None = None,
|
|
650
663
|
operation_id: Filter | None = None,
|
|
651
|
-
skip_deprecated_operations: bool =
|
|
664
|
+
skip_deprecated_operations: bool | None = None,
|
|
652
665
|
validate_schema: bool = False,
|
|
653
666
|
force_schema_version: str | None = None,
|
|
654
667
|
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
schemathesis/stateful/events.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
|
-
from dataclasses import asdict as _asdict
|
|
4
|
+
from dataclasses import asdict as _asdict
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from enum import Enum
|
|
6
|
-
from typing import TYPE_CHECKING,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Type
|
|
7
8
|
|
|
8
9
|
from ..exceptions import format_exception
|
|
9
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: schemathesis
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.33.0
|
|
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
|
|
@@ -12,22 +12,22 @@ schemathesis/code_samples.py,sha256=xk1-1jnXg5hS40VzIZp8PEtZwGaazNlVKMT7_X-zG-M,
|
|
|
12
12
|
schemathesis/constants.py,sha256=l1YQ7PXhEj9dyf9CTESVUpPOaFCH7iz-Fe8o4v6Th_s,2673
|
|
13
13
|
schemathesis/exceptions.py,sha256=w-5A-2Yb6EJOSvPycbx79T9Lr1IcMWJ8UFq6DW9kIMc,19975
|
|
14
14
|
schemathesis/failures.py,sha256=wXz5Kr5i-ojcYc-BdzFlNbNGOfoVXHZM6kd4iULdHK4,7003
|
|
15
|
-
schemathesis/filters.py,sha256=
|
|
15
|
+
schemathesis/filters.py,sha256=t1P236DPVaWvhu2Sm6OGy6Oe2x-nnwEVYRau4qqX6C4,17034
|
|
16
16
|
schemathesis/graphql.py,sha256=YkoKWY5K8lxp7H3ikAs-IsoDbiPwJvChG7O8p3DgwtI,229
|
|
17
|
-
schemathesis/hooks.py,sha256=
|
|
18
|
-
schemathesis/lazy.py,sha256=
|
|
17
|
+
schemathesis/hooks.py,sha256=YuPauQfs-xFDiBDoGxMRmWX5bb4IfCf02nw7IH36J9g,12400
|
|
18
|
+
schemathesis/lazy.py,sha256=hGwSuWe5tDaGpjZTV4Mj8zqdrHDYHxR22N2p5h2yh1g,18897
|
|
19
19
|
schemathesis/loaders.py,sha256=OtCD1o0TVmSNAUF7dgHpouoAXtY6w9vEtsRVGv4lE0g,4588
|
|
20
|
-
schemathesis/models.py,sha256=
|
|
20
|
+
schemathesis/models.py,sha256=HiFWcZO1m2DrmM8fGrXm2sW_uSk2tcYbY5ignfqPpJM,44927
|
|
21
21
|
schemathesis/parameters.py,sha256=PndmqQRlEYsCt1kWjSShPsFf6vj7X_7FRdz_-A95eNg,2258
|
|
22
22
|
schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
schemathesis/sanitization.py,sha256=mRR4YvXpzqbmgX8Xu6rume6LBcz9g_oyusvbesZl44I,8958
|
|
24
|
-
schemathesis/schemas.py,sha256=
|
|
24
|
+
schemathesis/schemas.py,sha256=MJv5NQn55o9ezsJTQxdlP-AD0vqJ7NCs62VW_Thhz6o,20441
|
|
25
25
|
schemathesis/serializers.py,sha256=kxXZ-UGa1v_vOm0sC4QYcrNv4rfvI7tHGT2elRVbCbc,11649
|
|
26
26
|
schemathesis/targets.py,sha256=XIGRghvEzbmEJjse9aZgNEj67L3jAbiazm2rxURWgDE,2351
|
|
27
27
|
schemathesis/throttling.py,sha256=aisUc4MJDGIOGUAs9L2DlWWpdd4KyAFuNVKhYoaUC9M,1719
|
|
28
28
|
schemathesis/types.py,sha256=xOzNAeMs6qqeaJnWs5Fpw5JPbvVjyfRfxTJa3G2Ln5I,920
|
|
29
29
|
schemathesis/utils.py,sha256=NX04p9mO-lCAH3DIISXDXPxWZk6lkGNM4-ubRi8vlvY,5234
|
|
30
|
-
schemathesis/cli/__init__.py,sha256=
|
|
30
|
+
schemathesis/cli/__init__.py,sha256=BvV9r2HG5lCclzsfTuydmJrJFq8CFmZrIF0e_YzROBY,73032
|
|
31
31
|
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
|
32
32
|
schemathesis/cli/callbacks.py,sha256=R2noVRu8zDBWmA3dJ2YnmhAjYdkxPDA1zIpA3_2DkFQ,15144
|
|
33
33
|
schemathesis/cli/cassettes.py,sha256=lKKKKLjoVT8VnZXjCOCmDYhvLKn1XSrCXCMF9EeftHA,18498
|
|
@@ -61,7 +61,7 @@ schemathesis/generation/__init__.py,sha256=mC1NVAHyce1_B_wu-GYO9U21Gut8KFrjPXETR
|
|
|
61
61
|
schemathesis/internal/__init__.py,sha256=93HcdG3LF0BbQKbCteOsFMa1w6nXl8yTmx87QLNJOik,161
|
|
62
62
|
schemathesis/internal/copy.py,sha256=DcL56z-d69kKR_5u8mlHvjSL1UTyUKNMAwexrwHFY1s,1031
|
|
63
63
|
schemathesis/internal/datetime.py,sha256=zPLBL0XXLNfP-KYel3H2m8pnsxjsA_4d-zTOhJg2EPQ,136
|
|
64
|
-
schemathesis/internal/deprecation.py,sha256=
|
|
64
|
+
schemathesis/internal/deprecation.py,sha256=Ty5VBFBlufkITpP0WWTPIPbnB7biDi0kQgXVYWZp820,1273
|
|
65
65
|
schemathesis/internal/extensions.py,sha256=h0aHRK_PTKfiAufkeBziegQS8537TL-Gr1hPW48q8Yc,790
|
|
66
66
|
schemathesis/internal/jsonschema.py,sha256=-7tF15cXo1ZdhiRFYYfEClXihX2Svc5Loi_Dz1x201k,1157
|
|
67
67
|
schemathesis/internal/output.py,sha256=zMaG5knIuBieVH8CrcmPJgbmQukDs2xdekX0BrK7BZs,1989
|
|
@@ -69,11 +69,11 @@ schemathesis/internal/result.py,sha256=d449YvyONjqjDs-A5DAPgtAI96iT753K8sU6_1HLo
|
|
|
69
69
|
schemathesis/internal/transformation.py,sha256=3S6AzAqdsEsB5iobFgSuvL0UMUqH0JHC7hGxKwcpqPw,450
|
|
70
70
|
schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QAtMPyEw,966
|
|
71
71
|
schemathesis/runner/__init__.py,sha256=mXFJTAjbjfGImIOB5d1rkpZC5TtVRxSf_SMiBEhKNMI,21350
|
|
72
|
-
schemathesis/runner/events.py,sha256=
|
|
72
|
+
schemathesis/runner/events.py,sha256=2XnZJQN3bx_AjYqCuucDjFSuVr-UrkN0yiOatXWWn8E,11507
|
|
73
73
|
schemathesis/runner/probes.py,sha256=no5AfO3kse25qvHevjeUfB0Q3C860V2AYzschUW3QMQ,5688
|
|
74
74
|
schemathesis/runner/serialization.py,sha256=erbXEHyI8rIlkQ42AwgmlH7aAbh313EPqCEfrGKxUls,20040
|
|
75
75
|
schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
|
|
76
|
-
schemathesis/runner/impl/core.py,sha256=
|
|
76
|
+
schemathesis/runner/impl/core.py,sha256=4HsnVYXcJ3qNj_vPpQJn7uu6nQwobzS0aGwmvtKy3L0,45022
|
|
77
77
|
schemathesis/runner/impl/solo.py,sha256=N7-pUL6nWGiSRUC4Zqy1T4h99vbeQowP6b6cMnobOow,3042
|
|
78
78
|
schemathesis/runner/impl/threadpool.py,sha256=JtTn5X7gqpbkO4q8n5kykGEPQCpJ9hXJrvU3KMF1CXA,14948
|
|
79
79
|
schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
|
|
@@ -95,7 +95,7 @@ schemathesis/specs/graphql/_cache.py,sha256=7ras3q_InDJBPykgHroerl9f2jFamC8xJD35
|
|
|
95
95
|
schemathesis/specs/graphql/loaders.py,sha256=qxNGL67_AfhoRh0hIxlnJVe6do26vqwWS_TrJtB-Lro,12198
|
|
96
96
|
schemathesis/specs/graphql/nodes.py,sha256=bE3G1kNmqJ8OV4igBvIK-UORrkQA6Nofduf87O3TD9I,541
|
|
97
97
|
schemathesis/specs/graphql/scalars.py,sha256=9tvLTiYVe8A_E8ASA0czz3Z0Mp9lyak7R4wHpAE_jKo,1805
|
|
98
|
-
schemathesis/specs/graphql/schemas.py,sha256=
|
|
98
|
+
schemathesis/specs/graphql/schemas.py,sha256=w9EvZZ3KrRAmpfIIWZz4qHPRwCOmKX_eovTiqpqLoKs,13926
|
|
99
99
|
schemathesis/specs/graphql/validation.py,sha256=uINIOt-2E7ZuQV2CxKzwez-7L9tDtqzMSpnVoRWvxy0,1635
|
|
100
100
|
schemathesis/specs/openapi/__init__.py,sha256=HDcx3bqpa6qWPpyMrxAbM3uTo0Lqpg-BUNZhDJSJKnw,279
|
|
101
101
|
schemathesis/specs/openapi/_cache.py,sha256=PAiAu4X_a2PQgD2lG5H3iisXdyg4SaHpU46bRZvfNkM,4320
|
|
@@ -107,7 +107,7 @@ schemathesis/specs/openapi/definitions.py,sha256=Z186F0gNBSCmPg-Kk7Q-n6XxEZHIOzg
|
|
|
107
107
|
schemathesis/specs/openapi/examples.py,sha256=5bjmW3BnJVTiLlWZbimdfOzQQFR6m1P9G0FErr9g3WI,15128
|
|
108
108
|
schemathesis/specs/openapi/formats.py,sha256=JmmkQWNAj5XreXb7Edgj4LADAf4m86YulR_Ec8evpJ4,1220
|
|
109
109
|
schemathesis/specs/openapi/links.py,sha256=2ucOLs50OhCqu0PEdbT_BGUM3fKnHBl97YGISLpAxLY,16023
|
|
110
|
-
schemathesis/specs/openapi/loaders.py,sha256=
|
|
110
|
+
schemathesis/specs/openapi/loaders.py,sha256=AcpvTK8qdirSRcHcinCjQbwfSQSx448LAh_GvFML1C0,25515
|
|
111
111
|
schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
|
|
112
112
|
schemathesis/specs/openapi/parameters.py,sha256=_6vNCnPXcdxjfAQbykCRLHjvmTpu_02xDJghxDrGYr8,13611
|
|
113
113
|
schemathesis/specs/openapi/references.py,sha256=euxM02kQGMHh4Ss1jWjOY_gyw_HazafKITIsvOEiAvI,9831
|
|
@@ -133,7 +133,7 @@ schemathesis/specs/openapi/stateful/types.py,sha256=UuGcCTFvaHsqeLN9ZeUNcbjsEwmt
|
|
|
133
133
|
schemathesis/stateful/__init__.py,sha256=qyQJ-9Ect-AWZiAsK63F3BTGu-jZnPCOp1q46YAonkQ,4911
|
|
134
134
|
schemathesis/stateful/config.py,sha256=rtGl3egoUuPFxrWcl5xZj_6KmKzZyYaC0b_AUotvurs,2930
|
|
135
135
|
schemathesis/stateful/context.py,sha256=MeP3-lKyhtAd-jzApC65AWlDZSOBzQq0IgK-nvagYqs,4519
|
|
136
|
-
schemathesis/stateful/events.py,sha256=
|
|
136
|
+
schemathesis/stateful/events.py,sha256=reGjsmS-y2izRd3bahpEZNe4zxTSpKAkPI-nCQyoUzI,6733
|
|
137
137
|
schemathesis/stateful/runner.py,sha256=vFd8Id3zzSQVHBK9UgInBpBT7oyUNagQ0p7ZJnPyHRk,10969
|
|
138
138
|
schemathesis/stateful/sink.py,sha256=xjsqJYH5WETKh5pDGlchYyjT3HcjzHEotUjvo1p0JsE,2470
|
|
139
139
|
schemathesis/stateful/state_machine.py,sha256=iRbznWxHnUdLhMpiBaHxe6Nh1EacyGnGFz4DCRwV5j4,12228
|
|
@@ -144,8 +144,8 @@ schemathesis/transports/auth.py,sha256=yELjkEkfx4g74hNrd0Db9aFf0xDJDRIwhg2vzKOTZ
|
|
|
144
144
|
schemathesis/transports/content_types.py,sha256=VrcRQvF5T_TUjrCyrZcYF2LOwKfs3IrLcMtkVSp1ImI,2189
|
|
145
145
|
schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
|
|
146
146
|
schemathesis/transports/responses.py,sha256=6-gvVcRK0Ho_lSydUysBNFWoJwZEiEgf6Iv-GWkQGd8,1675
|
|
147
|
-
schemathesis-3.
|
|
148
|
-
schemathesis-3.
|
|
149
|
-
schemathesis-3.
|
|
150
|
-
schemathesis-3.
|
|
151
|
-
schemathesis-3.
|
|
147
|
+
schemathesis-3.33.0.dist-info/METADATA,sha256=hssTV_lu8kmDJgiFDIyKPF7iO7nmsD2NURQh4FlMhaI,17795
|
|
148
|
+
schemathesis-3.33.0.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
|
|
149
|
+
schemathesis-3.33.0.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
|
|
150
|
+
schemathesis-3.33.0.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
|
|
151
|
+
schemathesis-3.33.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|