schemathesis 4.1.0__py3-none-any.whl → 4.1.2__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.
@@ -7,6 +7,7 @@ SCHEMATHESIS_TEST_CASE_HEADER = "X-Schemathesis-TestCaseId"
7
7
  HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER = ":memory:"
8
8
  INTERNAL_BUFFER_SIZE = 32 * 1024
9
9
  DEFAULT_STATEFUL_STEP_COUNT = 6
10
+ INJECTED_PATH_PARAMETER_KEY = "x-schemathesis-injected"
10
11
 
11
12
 
12
13
  class NotSet: ...
@@ -310,6 +310,7 @@ def format_failures(
310
310
  output += "\n <NO RESPONSE>"
311
311
 
312
312
  # cURL
313
- output += "\n" + formatter(MessageBlock.CURL, f"\nReproduce with: \n\n {curl}")
313
+ _curl = "\n".join(f" {line}" for line in curl.splitlines())
314
+ output += "\n" + formatter(MessageBlock.CURL, f"\nReproduce with: \n\n{_curl}")
314
315
 
315
316
  return output
@@ -330,10 +330,18 @@ def validate_response(
330
330
  if stateful_ctx.is_seen_in_suite(failure) or stateful_ctx.is_seen_in_run(failure):
331
331
  return
332
332
  failure_data = recorder.find_failure_data(parent_id=case.id, failure=failure)
333
+
334
+ # Collect the whole chain of cURL commands
335
+ commands = []
336
+ parent = recorder.find_parent(case_id=failure_data.case.id)
337
+ while parent is not None:
338
+ commands.append(parent.as_curl_command(headers=failure_data.headers, verify=failure_data.verify))
339
+ parent = recorder.find_parent(case_id=parent.id)
340
+ commands.append(failure_data.case.as_curl_command(headers=failure_data.headers, verify=failure_data.verify))
333
341
  recorder.record_check_failure(
334
342
  name=name,
335
343
  case_id=failure_data.case.id,
336
- code_sample=failure_data.case.as_curl_command(headers=failure_data.headers, verify=failure_data.verify),
344
+ code_sample="\n".join(commands),
337
345
  failure=failure,
338
346
  )
339
347
  control.count_failure()
@@ -49,6 +49,7 @@ from schemathesis.generation.case import Case
49
49
  from schemathesis.generation.hypothesis.builder import (
50
50
  InvalidHeadersExampleMark,
51
51
  InvalidRegexMark,
52
+ MissingPathParameters,
52
53
  NonSerializableMark,
53
54
  UnsatisfiableExampleMark,
54
55
  )
@@ -73,6 +74,7 @@ def run_test(
73
74
  yield scenario_started
74
75
  errors: list[Exception] = []
75
76
  skip_reason = None
77
+ error: Exception
76
78
  test_start_time = time.monotonic()
77
79
  recorder = ScenarioRecorder(label=operation.label)
78
80
  state = TestingState()
@@ -227,11 +229,15 @@ def run_test(
227
229
  and any(check.status == Status.FAILURE for checks in recorder.checks.values() for check in checks)
228
230
  ):
229
231
  status = Status.FAILURE
232
+
233
+ # Check for various errors during generation (tests may still have been generated)
234
+
230
235
  if UnsatisfiableExampleMark.is_set(test_function):
231
236
  status = Status.ERROR
232
237
  yield non_fatal_error(
233
238
  hypothesis.errors.Unsatisfiable("Failed to generate test cases from examples for this API operation")
234
239
  )
240
+
235
241
  non_serializable = NonSerializableMark.get(test_function)
236
242
  if non_serializable is not None and status != Status.ERROR:
237
243
  status = Status.ERROR
@@ -248,10 +254,17 @@ def run_test(
248
254
  if invalid_regex is not None and status != Status.ERROR:
249
255
  status = Status.ERROR
250
256
  yield non_fatal_error(InvalidRegexPattern.from_schema_error(invalid_regex, from_examples=True))
257
+
251
258
  invalid_headers = InvalidHeadersExampleMark.get(test_function)
252
259
  if invalid_headers:
253
260
  status = Status.ERROR
254
261
  yield non_fatal_error(InvalidHeadersExample.from_headers(invalid_headers))
262
+
263
+ missing_path_parameters = MissingPathParameters.get(test_function)
264
+ if missing_path_parameters:
265
+ status = Status.ERROR
266
+ yield non_fatal_error(missing_path_parameters)
267
+
255
268
  for error in deduplicate_errors(errors):
256
269
  yield non_fatal_error(error)
257
270
 
schemathesis/filters.py CHANGED
@@ -175,6 +175,10 @@ class FilterSet:
175
175
  """Whether the filter set does not contain any filters."""
176
176
  return not self._includes and not self._excludes
177
177
 
178
+ def clear(self) -> None:
179
+ self._includes.clear()
180
+ self._excludes.clear()
181
+
178
182
  def include(
179
183
  self,
180
184
  func: MatcherFunc | None = None,
@@ -20,7 +20,7 @@ from requests.models import CaseInsensitiveDict
20
20
  from schemathesis import auths
21
21
  from schemathesis.auths import AuthStorage, AuthStorageMark
22
22
  from schemathesis.config import GenerationConfig, ProjectConfig
23
- from schemathesis.core import NOT_SET, NotSet, SpecificationFeature, media_types
23
+ from schemathesis.core import INJECTED_PATH_PARAMETER_KEY, NOT_SET, NotSet, SpecificationFeature, media_types
24
24
  from schemathesis.core.errors import InvalidSchema, SerializationNotPossible
25
25
  from schemathesis.core.marks import Mark
26
26
  from schemathesis.core.transforms import deepclone
@@ -185,6 +185,18 @@ def create_test(
185
185
  generation_config=generation,
186
186
  )
187
187
 
188
+ injected_path_parameter_names = [
189
+ parameter.name
190
+ for parameter in operation.path_parameters
191
+ if parameter.definition.get(INJECTED_PATH_PARAMETER_KEY)
192
+ ]
193
+ if injected_path_parameter_names:
194
+ names = ", ".join(f"'{name}'" for name in injected_path_parameter_names)
195
+ plural = "s" if len(injected_path_parameter_names) > 1 else ""
196
+ verb = "are" if len(injected_path_parameter_names) > 1 else "is"
197
+ error = InvalidSchema(f"Path parameter{plural} {names} {verb} not defined")
198
+ MissingPathParameters.set(hypothesis_test, error)
199
+
188
200
  setattr(hypothesis_test, SETTINGS_ATTRIBUTE_NAME, settings)
189
201
 
190
202
  return hypothesis_test
@@ -902,3 +914,4 @@ UnsatisfiableExampleMark = Mark[Unsatisfiable](attr_name="unsatisfiable_example"
902
914
  NonSerializableMark = Mark[SerializationNotPossible](attr_name="non_serializable")
903
915
  InvalidRegexMark = Mark[SchemaError](attr_name="invalid_regex")
904
916
  InvalidHeadersExampleMark = Mark[dict[str, str]](attr_name="invalid_example_header")
917
+ MissingPathParameters = Mark[InvalidSchema](attr_name="missing_path_parameters")
schemathesis/hooks.py CHANGED
@@ -2,10 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  import inspect
4
4
  from collections import defaultdict
5
+ from contextlib import contextmanager
5
6
  from dataclasses import dataclass, field
6
7
  from enum import Enum, unique
7
8
  from functools import lru_cache, partial
8
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast
9
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generator, cast
9
10
 
10
11
  from schemathesis.core.marks import Mark
11
12
  from schemathesis.core.transport import Response
@@ -54,18 +55,28 @@ def to_filterable_hook(dispatcher: HookDispatcher) -> Callable:
54
55
  filter_used = False
55
56
  filter_set = FilterSet()
56
57
 
58
+ @contextmanager
59
+ def _reset_on_error() -> Generator:
60
+ try:
61
+ yield
62
+ except Exception:
63
+ filter_set.clear()
64
+ raise
65
+
57
66
  def register(hook: str | Callable) -> Callable:
58
67
  nonlocal filter_set
59
68
 
60
69
  if filter_used:
61
- validate_filterable_hook(hook)
70
+ with _reset_on_error():
71
+ validate_filterable_hook(hook)
62
72
 
63
73
  if isinstance(hook, str):
64
74
 
65
75
  def decorator(func: Callable) -> Callable:
66
76
  hook_name = cast(str, hook)
67
77
  if filter_used:
68
- validate_filterable_hook(hook)
78
+ with _reset_on_error():
79
+ validate_filterable_hook(hook)
69
80
  func.filter_set = filter_set # type: ignore[attr-defined]
70
81
  return dispatcher.register_hook_with_name(func, hook_name)
71
82
 
@@ -86,13 +97,15 @@ def to_filterable_hook(dispatcher: HookDispatcher) -> Callable:
86
97
  nonlocal filter_used
87
98
 
88
99
  filter_used = True
89
- filter_set.include(*args, **kwargs)
100
+ with _reset_on_error():
101
+ filter_set.include(*args, **kwargs)
90
102
 
91
103
  def exclude(*args: Any, **kwargs: Any) -> None:
92
104
  nonlocal filter_used
93
105
 
94
106
  filter_used = True
95
- filter_set.exclude(*args, **kwargs)
107
+ with _reset_on_error():
108
+ filter_set.exclude(*args, **kwargs)
96
109
 
97
110
  attach_filter_chain(target, "apply_to", include)
98
111
  attach_filter_chain(target, "skip_for", exclude)
@@ -113,11 +126,9 @@ class HookDispatcher:
113
126
  _hooks: defaultdict[str, list[Callable]] = field(default_factory=lambda: defaultdict(list))
114
127
  _specs: ClassVar[dict[str, RegisteredHook]] = {}
115
128
 
116
- def __post_init__(self) -> None:
117
- self.hook = to_filterable_hook(self) # type: ignore[method-assign]
118
-
119
- def hook(self, hook: str | Callable) -> Callable:
120
- raise NotImplementedError
129
+ @property
130
+ def hook(self) -> Callable:
131
+ return to_filterable_hook(self)
121
132
 
122
133
  def apply(self, hook: Callable, *, name: str | None = None) -> Callable[[Callable], Callable]:
123
134
  """Register hook to run only on one test function.
@@ -201,6 +212,9 @@ class HookDispatcher:
201
212
  """Get a list of hooks registered for a name."""
202
213
  return self._hooks.get(name, [])
203
214
 
215
+ def get_all(self) -> dict[str, list[Callable]]:
216
+ return self._hooks
217
+
204
218
  def apply_to_container(
205
219
  self, strategy: st.SearchStrategy, container: str, context: HookContext
206
220
  ) -> st.SearchStrategy:
@@ -298,6 +298,7 @@ def pytest_pyfunc_call(pyfuncitem): # type:ignore
298
298
  from schemathesis.generation.hypothesis.builder import (
299
299
  InvalidHeadersExampleMark,
300
300
  InvalidRegexMark,
301
+ MissingPathParameters,
301
302
  NonSerializableMark,
302
303
  UnsatisfiableExampleMark,
303
304
  )
@@ -333,8 +334,13 @@ def pytest_pyfunc_call(pyfuncitem): # type:ignore
333
334
  pytest.skip(exc.args[0])
334
335
  except SchemaError as exc:
335
336
  raise InvalidRegexPattern.from_schema_error(exc, from_examples=False) from exc
337
+
336
338
  invalid_headers = InvalidHeadersExampleMark.get(pyfuncitem.obj)
337
339
  if invalid_headers is not None:
338
340
  raise InvalidHeadersExample.from_headers(invalid_headers) from None
341
+
342
+ missing_path_parameters = MissingPathParameters.get(pyfuncitem.obj)
343
+ if missing_path_parameters:
344
+ raise missing_path_parameters from None
339
345
  else:
340
346
  yield
schemathesis/schemas.py CHANGED
@@ -27,7 +27,7 @@ from schemathesis.generation.case import Case
27
27
  from schemathesis.generation.hypothesis import strategies
28
28
  from schemathesis.generation.hypothesis.given import GivenInput, given_proxy
29
29
  from schemathesis.generation.meta import CaseMetadata
30
- from schemathesis.hooks import HookDispatcherMark
30
+ from schemathesis.hooks import HookDispatcherMark, _should_skip_hook
31
31
 
32
32
  from .auths import AuthStorage
33
33
  from .filters import (
@@ -712,14 +712,22 @@ class APIOperation(Generic[P]):
712
712
  def _apply_hooks(dispatcher: HookDispatcher, _strategy: SearchStrategy[Case]) -> SearchStrategy[Case]:
713
713
  context = HookContext(operation=self)
714
714
  for hook in dispatcher.get_all_by_name("before_generate_case"):
715
+ if _should_skip_hook(hook, context):
716
+ continue
715
717
  _strategy = hook(context, _strategy)
716
718
  for hook in dispatcher.get_all_by_name("filter_case"):
719
+ if _should_skip_hook(hook, context):
720
+ continue
717
721
  hook = partial(hook, context)
718
722
  _strategy = _strategy.filter(hook)
719
723
  for hook in dispatcher.get_all_by_name("map_case"):
724
+ if _should_skip_hook(hook, context):
725
+ continue
720
726
  hook = partial(hook, context)
721
727
  _strategy = _strategy.map(hook)
722
728
  for hook in dispatcher.get_all_by_name("flatmap_case"):
729
+ if _should_skip_hook(hook, context):
730
+ continue
723
731
  hook = partial(hook, context)
724
732
  _strategy = _strategy.flatmap(hook)
725
733
  return _strategy
@@ -366,7 +366,10 @@ def use_after_free(ctx: CheckContext, response: Response, case: Case) -> bool |
366
366
 
367
367
  if not isinstance(case.operation.schema, BaseOpenAPISchema) or is_unexpected_http_status_case(case):
368
368
  return True
369
- if response.status_code == 404 or response.status_code >= 500:
369
+
370
+ # Only check for use-after-free on successful responses (2xx) or redirects (3xx)
371
+ # Other status codes indicate request-level issues / server errors, not successful resource access
372
+ if not (200 <= response.status_code < 400):
370
373
  return None
371
374
 
372
375
  for related_case in ctx._find_related(case_id=case.id):
@@ -494,7 +497,11 @@ def ignored_auth(ctx: CheckContext, response: Response, case: Case) -> bool | No
494
497
  """Check if an operation declares authentication as a requirement but does not actually enforce it."""
495
498
  from .schemas import BaseOpenAPISchema
496
499
 
497
- if not isinstance(case.operation.schema, BaseOpenAPISchema) or is_unexpected_http_status_case(case):
500
+ if (
501
+ not isinstance(case.operation.schema, BaseOpenAPISchema)
502
+ or is_unexpected_http_status_case(case)
503
+ or _has_optional_auth(case.operation)
504
+ ):
498
505
  return True
499
506
  security_parameters = _get_security_parameters(case.operation)
500
507
  # Authentication is required for this API operation and response is successful
@@ -582,6 +589,13 @@ def _get_security_parameters(operation: APIOperation) -> list[SecurityParameter]
582
589
  ]
583
590
 
584
591
 
592
+ def _has_optional_auth(operation: APIOperation) -> bool:
593
+ from .schemas import BaseOpenAPISchema
594
+
595
+ schema = cast(BaseOpenAPISchema, operation.schema)
596
+ return schema.security.has_optional_auth(schema.raw_schema, operation)
597
+
598
+
585
599
  def _contains_auth(
586
600
  ctx: CheckContext, case: Case, response: Response, security_parameters: list[SecurityParameter]
587
601
  ) -> AuthKind | None:
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import itertools
4
+ import string
4
5
  from collections import defaultdict
5
6
  from contextlib import ExitStack, contextmanager, suppress
6
7
  from dataclasses import dataclass, field
@@ -29,7 +30,7 @@ from requests.exceptions import InvalidHeader
29
30
  from requests.structures import CaseInsensitiveDict
30
31
  from requests.utils import check_header_validity
31
32
 
32
- from schemathesis.core import NOT_SET, NotSet, Specification, deserialization, media_types
33
+ from schemathesis.core import INJECTED_PATH_PARAMETER_KEY, NOT_SET, NotSet, Specification, deserialization, media_types
33
34
  from schemathesis.core.compat import RefResolutionError
34
35
  from schemathesis.core.errors import InternalError, InvalidSchema, LoaderError, LoaderErrorKind, OperationNotFound
35
36
  from schemathesis.core.failures import Failure, FailureGroup, MalformedJson
@@ -89,6 +90,17 @@ def check_header(parameter: dict[str, Any]) -> None:
89
90
  raise InvalidSchema(f"Invalid header name: {name}")
90
91
 
91
92
 
93
+ def get_template_fields(template: str) -> set[str]:
94
+ """Extract named placeholders from a string template."""
95
+ try:
96
+ parameters = {name for _, name, _, _ in string.Formatter().parse(template) if name is not None}
97
+ # Check for malformed params to avoid injecting them - they will be checked later on in the workflow
98
+ template.format(**dict.fromkeys(parameters, ""))
99
+ return parameters
100
+ except (ValueError, IndexError):
101
+ return set()
102
+
103
+
92
104
  @dataclass(eq=False, repr=False)
93
105
  class BaseOpenAPISchema(BaseSchema):
94
106
  nullable_name: ClassVar[str] = ""
@@ -100,6 +112,7 @@ class BaseOpenAPISchema(BaseSchema):
100
112
  # excessive resolving
101
113
  _inline_reference_cache_lock: RLock = field(default_factory=RLock)
102
114
  component_locations: ClassVar[tuple[tuple[str, ...], ...]] = ()
115
+ _path_parameter_template: ClassVar[dict[str, Any]] = None # type: ignore
103
116
 
104
117
  @property
105
118
  def specification(self) -> Specification:
@@ -391,7 +404,6 @@ class BaseOpenAPISchema(BaseSchema):
391
404
  resolved: dict[str, Any],
392
405
  scope: str,
393
406
  ) -> APIOperation:
394
- """Create JSON schemas for the query, body, etc from Swagger parameters definitions."""
395
407
  __tracebackhide__ = True
396
408
  base_url = self.get_base_url()
397
409
  operation: APIOperation[OpenAPIParameter] = APIOperation(
@@ -404,6 +416,14 @@ class BaseOpenAPISchema(BaseSchema):
404
416
  )
405
417
  for parameter in parameters:
406
418
  operation.add_parameter(parameter)
419
+ # Inject unconstrained path parameters if any is missing
420
+ missing_parameter_names = get_template_fields(operation.path) - {
421
+ parameter.name for parameter in operation.path_parameters
422
+ }
423
+ for name in missing_parameter_names:
424
+ definition = {"name": name, INJECTED_PATH_PARAMETER_KEY: True, **deepclone(self._path_parameter_template)}
425
+ for parameter in self.collect_parameters([definition], resolved):
426
+ operation.add_parameter(parameter)
407
427
  config = self.config.generation_for(operation=operation)
408
428
  if config.with_security_parameters:
409
429
  self.security.process_definitions(self.raw_schema, operation, self.resolver)
@@ -835,6 +855,7 @@ class SwaggerV20(BaseOpenAPISchema):
835
855
  security = SwaggerSecurityProcessor()
836
856
  component_locations: ClassVar[tuple[tuple[str, ...], ...]] = (("definitions",),)
837
857
  links_field = "x-links"
858
+ _path_parameter_template = {"in": "path", "required": True, "type": "string"}
838
859
 
839
860
  @property
840
861
  def specification(self) -> Specification:
@@ -1003,6 +1024,7 @@ class OpenApi30(SwaggerV20):
1003
1024
  security = OpenAPISecurityProcessor()
1004
1025
  component_locations = (("components", "schemas"),)
1005
1026
  links_field = "links"
1027
+ _path_parameter_template = {"in": "path", "required": True, "schema": {"type": "string"}}
1006
1028
 
1007
1029
  @property
1008
1030
  def specification(self) -> Specification:
@@ -32,20 +32,28 @@ class BaseSecurityProcessor:
32
32
  self.process_api_key_security_definition(definition, operation)
33
33
  self.process_http_security_definition(definition, operation)
34
34
 
35
+ @staticmethod
36
+ def _get_security_requirements(schema: dict[str, Any], operation: APIOperation) -> list[dict]:
37
+ global_requirements = schema.get("security", [])
38
+ local_requirements = operation.definition.raw.get("security", None)
39
+ if local_requirements is not None:
40
+ return local_requirements
41
+ return global_requirements
42
+
35
43
  @staticmethod
36
44
  def get_security_requirements(schema: dict[str, Any], operation: APIOperation) -> list[str]:
37
45
  """Get applied security requirements for the given API operation."""
38
46
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object
39
47
  # > This definition overrides any declared top-level security.
40
48
  # > To remove a top-level security declaration, an empty array can be used.
41
- global_requirements = schema.get("security", [])
42
- local_requirements = operation.definition.raw.get("security", None)
43
- if local_requirements is not None:
44
- requirements = local_requirements
45
- else:
46
- requirements = global_requirements
49
+ requirements = BaseSecurityProcessor._get_security_requirements(schema, operation)
47
50
  return [key for requirement in requirements for key in requirement]
48
51
 
52
+ @staticmethod
53
+ def has_optional_auth(schema: dict[str, Any], operation: APIOperation) -> bool:
54
+ requirements = BaseSecurityProcessor._get_security_requirements(schema, operation)
55
+ return {} in requirements
56
+
49
57
  def _get_active_definitions(
50
58
  self, schema: dict[str, Any], operation: APIOperation, resolver: RefResolver
51
59
  ) -> Generator[dict[str, Any], None, None]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.1.0
3
+ Version: 4.1.2
4
4
  Summary: Property-based testing framework for Open API and GraphQL based apps
5
5
  Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
6
6
  Project-URL: Changelog, https://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
@@ -2,10 +2,10 @@ schemathesis/__init__.py,sha256=QqVUCBQr-RDEstgCZLsxzIa9HJslVSeijrm9gES4b_0,1423
2
2
  schemathesis/auths.py,sha256=JdEwPRS9WKmPcxzGXYYz9pjlIUMQYCfif7ZJU0Kde-I,16400
3
3
  schemathesis/checks.py,sha256=GTdejjXDooAOuq66nvCK3i-AMPBuU-_-aNeSeL9JIlc,6561
4
4
  schemathesis/errors.py,sha256=T8nobEi5tQX_SkwaYb8JFoIlF9F_vOQVprZ8EVPAhjA,931
5
- schemathesis/filters.py,sha256=OEub50Ob5sf0Tn3iTeuIaxSMtepF7KVoiEM9wtt5GGA,13433
6
- schemathesis/hooks.py,sha256=2-HNwluP6HWkkAJP4nnVcyqO93TxXl8VwXW-sUGAooc,14488
5
+ schemathesis/filters.py,sha256=Nk1Z6_L0fJBIzFGwjUhx9oDHJiGn-kVxbAXWeN9gRcQ,13525
6
+ schemathesis/hooks.py,sha256=q2wqYNgpMCO8ImSBkbrWDSwN0BSELelqJMgAAgGvv2M,14836
7
7
  schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- schemathesis/schemas.py,sha256=thyBKI50sJsj7PE52GvLy9_ktPMDP-fbLM8ZwvdHuD0,28618
8
+ schemathesis/schemas.py,sha256=i0L6jYulGGASD1RiQsA6sOTNt1v812KHSZKIhnuX34s,28965
9
9
  schemathesis/cli/__init__.py,sha256=U9gjzWWpiFhaqevPjZbwyTNjABdpvXETI4HgwdGKnvs,877
10
10
  schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
11
11
  schemathesis/cli/constants.py,sha256=CVcQNHEiX-joAQmyuEVKWPOSxDHsOw_EXXZsEclzLuY,341
@@ -46,13 +46,13 @@ schemathesis/config/_report.py,sha256=ZECDpaCY4WWHD5UbjvgZoSjLz-rlTvfd5Ivzdgzqf2
46
46
  schemathesis/config/_validator.py,sha256=IcE8geFZ0ZwR18rkIRs25i7pTl7Z84XbjYGUB-mqReU,258
47
47
  schemathesis/config/_warnings.py,sha256=sI0VZcTj3dOnphhBwYwU_KTagxr89HGWTtQ99HcY84k,772
48
48
  schemathesis/config/schema.json,sha256=3CRSBfUK2vLVl1h5PfeK_YDdEoBZSojYl8Q1kT-ITLE,19846
49
- schemathesis/core/__init__.py,sha256=j862XBH5dXhxsrDg9mE7n4cSSfol0EHdY0ru1d27tCc,1917
49
+ schemathesis/core/__init__.py,sha256=h4gmDePIPvPiVuYxnjrpPKytSZPi6fZeVdTG6c822E4,1973
50
50
  schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1058
51
51
  schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
52
52
  schemathesis/core/curl.py,sha256=yuaCe_zHLGwUjEeloQi6W3tOA3cGdnHDNI17-5jia0o,1723
53
53
  schemathesis/core/deserialization.py,sha256=qjXUPaz_mc1OSgXzTUSkC8tuVR8wgVQtb9g3CcAF6D0,2951
54
54
  schemathesis/core/errors.py,sha256=pwiyGhX7tId88Toe2H4ZYsCDc_OvUJtW8Wv-xDv2UD4,16361
55
- schemathesis/core/failures.py,sha256=MYyRnom-XeUEuBmq2ffdz34xhxmpSHWaQunfHtliVsY,8932
55
+ schemathesis/core/failures.py,sha256=yFpAxWdEnm0Ri8z8RqRI9H7vcLH5ztOeSIi4m4SGx5g,8996
56
56
  schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
57
57
  schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
58
58
  schemathesis/core/lazy_import.py,sha256=aMhWYgbU2JOltyWBb32vnWBb6kykOghucEzI_F70yVE,470
@@ -79,10 +79,10 @@ schemathesis/engine/recorder.py,sha256=K3HfMARrT5mPWXPnYebjjcq5CcsBRhMrtZwEL9_Lv
79
79
  schemathesis/engine/phases/__init__.py,sha256=7Yp7dQbd6-K9pavIJeURg6jiNeMpW8UU-Iiikr668ts,3278
80
80
  schemathesis/engine/phases/probes.py,sha256=YogjJcZJcTMS8sMdGnG4oXKmMUj_4r_J7MY-BBJtCRU,5690
81
81
  schemathesis/engine/phases/stateful/__init__.py,sha256=Lz1rgNqCfUSIz173XqCGsiMuUI5bh4L-RIFexU1-c_Q,2461
82
- schemathesis/engine/phases/stateful/_executor.py,sha256=_303Yqflx1iFNTQI2EfjSp_2T21YvzJJgMSazhpv5JQ,15200
82
+ schemathesis/engine/phases/stateful/_executor.py,sha256=6YOgAGynP4UKKTITe7S39SCcnRp5OXQ7fCc8BJIFW3A,15592
83
83
  schemathesis/engine/phases/stateful/context.py,sha256=A7X1SLDOWFpCvFN9IiIeNVZM0emjqatmJL_k9UsO7vM,2946
84
84
  schemathesis/engine/phases/unit/__init__.py,sha256=9dDcxyj887pktnE9YDIPNaR-vc7iqKQWIrFr77SbUTQ,8786
85
- schemathesis/engine/phases/unit/_executor.py,sha256=tRv_QjwtXOuSZCl_j-S_EnhcrKScr9gyGGZItzg16XI,16190
85
+ schemathesis/engine/phases/unit/_executor.py,sha256=pqUISTWvnXb61rgrnzwlFn7PyR-ZsGis2kbRgBMH4EA,16519
86
86
  schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
87
87
  schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
88
88
  schemathesis/generation/case.py,sha256=Qc2_5JrWuUkCzAFTTgnVqNUJ2sioslmINTXiY7nHHgA,12326
@@ -92,7 +92,7 @@ schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz
92
92
  schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
93
93
  schemathesis/generation/overrides.py,sha256=OBWqDQPreiliaf2M-oyXppVKHoJkCRzxtwSJx1b6AFw,3759
94
94
  schemathesis/generation/hypothesis/__init__.py,sha256=jK3G4i0SdcyhqwPQg91RH_yg437lSY-smeIQ-wZLWPc,1959
95
- schemathesis/generation/hypothesis/builder.py,sha256=zOUIp1n6kKkh_M7HiwtCNzDk3wtepwBEjmT2qt6PenM,36078
95
+ schemathesis/generation/hypothesis/builder.py,sha256=HOZLev-b-fkaFwDIxQHlpS59y7anyK9mJ-qw5IeQZqY,36777
96
96
  schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
97
97
  schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
98
98
  schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
@@ -111,7 +111,7 @@ schemathesis/pytest/__init__.py,sha256=7W0q-Thcw03IAQfXE_Mo8JPZpUdHJzfu85fjK1Zdf
111
111
  schemathesis/pytest/control_flow.py,sha256=F8rAPsPeNv_sJiJgbZYtTpwKWjauZmqFUaKroY2GmQI,217
112
112
  schemathesis/pytest/lazy.py,sha256=u58q0orI0zisivLJKJkSo53RaQMPLSMiE0vJ1TQ9_uA,11073
113
113
  schemathesis/pytest/loaders.py,sha256=Sbv8e5F77_x4amLP50iwubfm6kpOhx7LhLFGsVXW5Ys,925
114
- schemathesis/pytest/plugin.py,sha256=MXllorF5SqUgqjLyhHb-P0wlWokvZMg0Rm6OTRoFoas,14266
114
+ schemathesis/pytest/plugin.py,sha256=-c92Qi2aEvW45KLAzHj9EDXbY_mYq5B5ZdkRY9Ks_DY,14463
115
115
  schemathesis/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
116
  schemathesis/python/asgi.py,sha256=5PyvuTBaivvyPUEi3pwJni91K1kX5Zc0u9c6c1D8a1Q,287
117
117
  schemathesis/python/wsgi.py,sha256=uShAgo_NChbfYaV1117e6UHp0MTg7jaR0Sy_to3Jmf8,219
@@ -123,7 +123,7 @@ schemathesis/specs/graphql/schemas.py,sha256=B6FULWIIWoN_gx9OeQOd-6qWsz9yVg5h1Xl
123
123
  schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
124
124
  schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
125
125
  schemathesis/specs/openapi/_hypothesis.py,sha256=XK-g2284wCsqOuPhubpE-PQ5_YotUwNodAnjeHs_-R0,21712
126
- schemathesis/specs/openapi/checks.py,sha256=1_YIcGqZ2xSN_1Ob_CIt_HfARSodMCCYNX4SQXx03-c,30150
126
+ schemathesis/specs/openapi/checks.py,sha256=CVUBhJgdVZZdgCIydsO0OYf3K4gGgnYw6XDSJo3q6OU,30623
127
127
  schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
128
128
  schemathesis/specs/openapi/converter.py,sha256=LkpCCAxZzET4Qa_3YStSNuhGlsm5G6TVwpxYu6lPO4g,4169
129
129
  schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
@@ -133,8 +133,8 @@ schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKR
133
133
  schemathesis/specs/openapi/parameters.py,sha256=XpuZ2sex2aYUzKDK17GXVNWFBmvamuyVtloQ1oGQhLk,14468
134
134
  schemathesis/specs/openapi/patterns.py,sha256=GqPZEXMRdWENQxanWjBOalIZ2MQUjuxk21kmdiI703E,18027
135
135
  schemathesis/specs/openapi/references.py,sha256=40YcDExPLR2B8EOwt-Csw-5MYFi2xj_DXf91J0Pc9d4,8855
136
- schemathesis/specs/openapi/schemas.py,sha256=tEN7kUH6CO-B3hUO-1Bhn42KpKE8_DbTWjksWYByQXo,50188
137
- schemathesis/specs/openapi/security.py,sha256=6UWYMhL-dPtkTineqqBFNKca1i4EuoTduw-EOLeE0aQ,7149
136
+ schemathesis/specs/openapi/schemas.py,sha256=qJ0ChkZuXQafQ-nPwMzckSk3iC32H2O5W9Cbd7DN_2I,51379
137
+ schemathesis/specs/openapi/security.py,sha256=Vv5Y76hM49ZqLlivViSURKQlxDd1F_FQRj5gEUfyBU0,7552
138
138
  schemathesis/specs/openapi/serialization.py,sha256=VdDLmeHqxlWM4cxQQcCkvrU6XurivolwEEaT13ohelA,11972
139
139
  schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
140
140
  schemathesis/specs/openapi/expressions/__init__.py,sha256=hfuRtXD75tQFhzSo6QgDZ3zByyWeZRKevB8edszAVj4,2272
@@ -157,8 +157,8 @@ schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEz
157
157
  schemathesis/transport/requests.py,sha256=XWiQVG4rGnFX0rOhOZAKVIPbrlknLuS7pHYwUcOiEGs,10942
158
158
  schemathesis/transport/serialization.py,sha256=igUXKZ_VJ9gV7P0TUc5PDQBJXl_s0kK9T3ljGWWvo6E,10339
159
159
  schemathesis/transport/wsgi.py,sha256=KoAfvu6RJtzyj24VGB8e-Iaa9smpgXJ3VsM8EgAz2tc,6152
160
- schemathesis-4.1.0.dist-info/METADATA,sha256=kExwPgT08l7__o4qiNKlKsSSHd7yHG_S124ZFe7FiLA,8540
161
- schemathesis-4.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
- schemathesis-4.1.0.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
- schemathesis-4.1.0.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
- schemathesis-4.1.0.dist-info/RECORD,,
160
+ schemathesis-4.1.2.dist-info/METADATA,sha256=29s0l-ISmsOaNtfY14dG-YwUGOv7a0A-1wCma2a__mE,8540
161
+ schemathesis-4.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
+ schemathesis-4.1.2.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
+ schemathesis-4.1.2.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
+ schemathesis-4.1.2.dist-info/RECORD,,