schemathesis 3.32.2__py3-none-any.whl → 3.33.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.
@@ -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
- endpoints: Filter | None = None,
783
- methods: Filter | None = None,
784
- tags: Filter | None = None,
785
- operation_ids: Filter | None = None,
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
- # Schema filters
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 == _is_deprecated.__name__ for exclude_ in new._excludes for matcher in exclude_.matchers
392
+ matcher.label == is_deprecated.__name__ for exclude_ in new._excludes for matcher in exclude_.matchers
363
393
  ):
364
- new.exclude(func=_is_deprecated)
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 == _is_deprecated.__name__:
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 `{replacement}` instead.",
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
- if method is NOT_SET:
79
- method = self.method
80
- if endpoint is NOT_SET:
81
- endpoint = self.endpoint
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
 
@@ -279,7 +279,7 @@ def load_schema(
279
279
  operation_id: Filter | None = None,
280
280
  ) -> BaseSchema:
281
281
  """Load schema via specified loader and parameters."""
282
- loader_options = {
282
+ loader_options: dict[str, Any] = {
283
283
  key: value
284
284
  for key, value in (
285
285
  ("base_url", base_url),
@@ -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 isinstance(exc, hypothesis.errors.FlakyFailure) and any(
539
- isinstance(subexc, hypothesis.errors.DeadlineExceeded) for subexc in exc.exceptions
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.FlakyFailure as exc:
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, hooks: HookDispatcher | None = None
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,
@@ -241,6 +241,8 @@ def extract_from_schema(
241
241
  if examples_field_name in subsubschema and isinstance(subsubschema[examples_field_name], list):
242
242
  # These are JSON Schema examples, which is an array of values
243
243
  values.extend(subsubschema[examples_field_name])
244
+ # Check nested examples as well
245
+ values.extend(extract_from_schema(operation, subsubschema, example_field_name, examples_field_name))
244
246
  if not values:
245
247
  if name in required:
246
248
  # Defer generation to only generate these variants if at least one property has examples
@@ -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 = False,
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 = False,
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 = False,
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 = False,
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 = False,
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
- return LazySchema(
509
- fixture_name,
510
- app=app,
511
- base_url=base_url,
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 = False,
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 = False,
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 = False,
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,
@@ -1,9 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
- from dataclasses import asdict as _asdict, dataclass
4
+ from dataclasses import asdict as _asdict
5
+ from dataclasses import dataclass
5
6
  from enum import Enum
6
- from typing import TYPE_CHECKING, Type, Any
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.32.2
3
+ Version: 3.33.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://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=02mg5Gn32AkOprfk33wjH8oXmbsYpogN8XGlrHkEo8Q,14265
15
+ schemathesis/filters.py,sha256=t1P236DPVaWvhu2Sm6OGy6Oe2x-nnwEVYRau4qqX6C4,17034
16
16
  schemathesis/graphql.py,sha256=YkoKWY5K8lxp7H3ikAs-IsoDbiPwJvChG7O8p3DgwtI,229
17
- schemathesis/hooks.py,sha256=dveqMmThIvt4fDahUXhU2nCq5pFvYjzzd1Ys_MhrJZA,12398
18
- schemathesis/lazy.py,sha256=yEMQXbve2cB5mX-9Kv9npUECYfXVMc-dK33k82WJVcM,15405
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=nbm9Agqw94RYib2Q4OH7iOiEPDGw64NlQnEB7o5Spio,44925
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=wfcZflxe6ITt6s1-d37RwoRHJaYckWS76T-_PBCC7hI,17757
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=tMlrDVuzKhmS6FC9HwvkEjvmBr0odBuVyM-dZ5x69mE,66839
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,19 +61,19 @@ 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=cF3FYy5syihneSIY7M7aVJW2KTzcd88WOItHFCJtADg,1107
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
68
68
  schemathesis/internal/result.py,sha256=d449YvyONjqjDs-A5DAPgtAI96iT753K8sU6_1HLo2Q,461
69
69
  schemathesis/internal/transformation.py,sha256=3S6AzAqdsEsB5iobFgSuvL0UMUqH0JHC7hGxKwcpqPw,450
70
70
  schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QAtMPyEw,966
71
- schemathesis/runner/__init__.py,sha256=mXFJTAjbjfGImIOB5d1rkpZC5TtVRxSf_SMiBEhKNMI,21350
71
+ schemathesis/runner/__init__.py,sha256=-aedUaRBCiTiaooC0OsBbdi4XP8APFOpj6eOzwt5nQ8,21366
72
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=k1c2QMMoqtXk1bafVRxJwGnIxTQxU0Pr1Am0Xo9WPd4,45063
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=i6fAW9pYcOplQE7BejP6P8GQ9z6Y43Vx4_feVvbd1B4,13555
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
@@ -104,10 +104,10 @@ schemathesis/specs/openapi/checks.py,sha256=eFjbV9-0202qg0s0JjaBNRr7nf8Bd8VMLPHE
104
104
  schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
105
105
  schemathesis/specs/openapi/converter.py,sha256=TaYgc5BBHPdkN-n0lqpbeVgLu3eL3L8Wu3y_Vo3TJaQ,2800
106
106
  schemathesis/specs/openapi/definitions.py,sha256=Z186F0gNBSCmPg-Kk7Q-n6XxEZHIOzgUyeqixlC62XE,94058
107
- schemathesis/specs/openapi/examples.py,sha256=5bjmW3BnJVTiLlWZbimdfOzQQFR6m1P9G0FErr9g3WI,15128
107
+ schemathesis/specs/openapi/examples.py,sha256=t3mYEgUq-akoe3sc9r0Eqb6y2STjIM4m7Lj7kDKG57Y,15293
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=NOVG8Jrl9pv5O64wAgWHrco1okpRzQDamHDnidZdIwY,24905
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=3AANEwTZFuW9Gbcig70NvdT_h4o5r-Esx9Hpm7zphdg,6710
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.32.2.dist-info/METADATA,sha256=yGKQbTb7EOxfJWZ1kVP68QIeiqG6mL7cAVOMdRaaAT4,17795
148
- schemathesis-3.32.2.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
149
- schemathesis-3.32.2.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
150
- schemathesis-3.32.2.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
151
- schemathesis-3.32.2.dist-info/RECORD,,
147
+ schemathesis-3.33.1.dist-info/METADATA,sha256=Fkjkxp4EXTL2_u0nrLQpB_FacY15xAhLcbSdojE8MYA,17795
148
+ schemathesis-3.33.1.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
149
+ schemathesis-3.33.1.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
150
+ schemathesis-3.33.1.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
151
+ schemathesis-3.33.1.dist-info/RECORD,,