schemathesis 3.30.0__py3-none-any.whl → 3.30.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.
@@ -38,6 +38,7 @@ from ..fixups import ALL_FIXUPS
38
38
  from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod
39
39
  from ..hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, HookScope
40
40
  from ..internal.datetime import current_datetime
41
+ from ..internal.output import OutputConfig
41
42
  from ..internal.validation import file_exists
42
43
  from ..loaders import load_app, load_yaml
43
44
  from ..models import Case, CheckFunction
@@ -694,6 +695,14 @@ The report data, consisting of a tar gz file with multiple JSON files, is subjec
694
695
  callback=callbacks.convert_experimental,
695
696
  multiple=True,
696
697
  )
698
+ @click.option(
699
+ "--output-truncate",
700
+ help="Specifies whether to truncate schemas and responses in error messages.",
701
+ type=str,
702
+ default="true",
703
+ show_default=True,
704
+ callback=callbacks.convert_boolean_string,
705
+ )
697
706
  @click.option(
698
707
  "--generation-allow-x00",
699
708
  help="Determines whether to allow the generation of `\x00` bytes within strings.",
@@ -783,6 +792,7 @@ def run(
783
792
  stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
784
793
  force_schema_version: str | None = None,
785
794
  sanitize_output: bool = True,
795
+ output_truncate: bool = True,
786
796
  contrib_unique_data: bool = False,
787
797
  contrib_openapi_formats_uuid: bool = False,
788
798
  contrib_openapi_fill_missing_examples: bool = False,
@@ -863,6 +873,8 @@ def run(
863
873
  click.secho(DEPRECATED_CASSETTE_PATH_OPTION_WARNING, fg="yellow")
864
874
  cassette_path = store_network_log
865
875
 
876
+ output_config = OutputConfig(truncate=output_truncate)
877
+
866
878
  schemathesis_io_hostname = urlparse(schemathesis_io_url).netloc
867
879
  token = schemathesis_io_token or service.hosts.get_token(hostname=schemathesis_io_hostname, hosts_file=hosts_file)
868
880
  schema_kind = callbacks.parse_schema_kind(schema, app)
@@ -982,6 +994,7 @@ def run(
982
994
  stateful_recursion_limit=stateful_recursion_limit,
983
995
  hypothesis_settings=hypothesis_settings,
984
996
  generation_config=generation_config,
997
+ output_config=output_config,
985
998
  service_client=client,
986
999
  )
987
1000
  execute(
@@ -1008,6 +1021,7 @@ def run(
1008
1021
  location=schema,
1009
1022
  base_url=base_url,
1010
1023
  started_at=started_at,
1024
+ output_config=output_config,
1011
1025
  )
1012
1026
 
1013
1027
 
@@ -1036,6 +1050,7 @@ class LoaderConfig:
1036
1050
  request_cert: RequestCert | None
1037
1051
  wait_for_schema: float | None
1038
1052
  rate_limit: str | None
1053
+ output_config: OutputConfig
1039
1054
  # Network request parameters
1040
1055
  auth: tuple[str, str] | None
1041
1056
  auth_type: str | None
@@ -1079,6 +1094,7 @@ def into_event_stream(
1079
1094
  workers_num: int,
1080
1095
  hypothesis_settings: hypothesis.settings | None,
1081
1096
  generation_config: generation.GenerationConfig,
1097
+ output_config: OutputConfig,
1082
1098
  seed: int | None,
1083
1099
  exit_first: bool,
1084
1100
  max_failures: int | None,
@@ -1112,6 +1128,7 @@ def into_event_stream(
1112
1128
  method=method or None,
1113
1129
  tag=tag or None,
1114
1130
  operation_id=operation_id or None,
1131
+ output_config=output_config,
1115
1132
  )
1116
1133
  schema = load_schema(config)
1117
1134
  yield from runner.from_schema(
@@ -1257,6 +1274,7 @@ def get_loader_kwargs(loader: Callable, config: LoaderConfig) -> dict[str, Any]:
1257
1274
  "force_schema_version": config.force_schema_version,
1258
1275
  "data_generation_methods": config.data_generation_methods,
1259
1276
  "rate_limit": config.rate_limit,
1277
+ "output_config": config.output_config,
1260
1278
  }
1261
1279
  if loader not in (oas_loaders.from_path, oas_loaders.from_dict):
1262
1280
  kwargs["headers"] = config.headers
@@ -1384,6 +1402,7 @@ def execute(
1384
1402
  location: str,
1385
1403
  base_url: str | None,
1386
1404
  started_at: str,
1405
+ output_config: OutputConfig,
1387
1406
  ) -> None:
1388
1407
  """Execute a prepared runner by drawing events from it and passing to a proper handler."""
1389
1408
  handlers: list[EventHandler] = []
@@ -1447,6 +1466,7 @@ def execute(
1447
1466
  verbosity=verbosity,
1448
1467
  code_sample_style=code_sample_style,
1449
1468
  report=report_context,
1469
+ output_config=output_config,
1450
1470
  )
1451
1471
 
1452
1472
  def shutdown() -> None:
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
8
8
 
9
9
  from ..code_samples import CodeSampleStyle
10
10
  from ..internal.deprecation import deprecated_property
11
+ from ..internal.output import OutputConfig
11
12
  from ..internal.result import Result
12
13
  from ..runner.probes import ProbeRun
13
14
  from ..runner.serialization import SerializedTestResult
@@ -57,6 +58,7 @@ class ExecutionContext:
57
58
  report: ServiceReportContext | FileReportContext | None = None
58
59
  probes: list[ProbeRun] | None = None
59
60
  analysis: Result[AnalysisResult, Exception] | None = None
61
+ output_config: OutputConfig = field(default_factory=OutputConfig)
60
62
  # Special flag to display a warning about Windows-specific encoding issue
61
63
  encountered_windows_encoding_issue: bool = False
62
64
  state_machine_sink: StateMachineSink | None = None
@@ -7,7 +7,8 @@ from typing import TYPE_CHECKING, cast
7
7
 
8
8
  from junit_xml import TestCase, TestSuite, to_xml_report_file
9
9
 
10
- from ..exceptions import RuntimeErrorType, prepare_response_payload
10
+ from ..exceptions import RuntimeErrorType
11
+ from ..internal.output import prepare_response_payload
11
12
  from ..models import Status
12
13
  from ..runner import events
13
14
  from ..runner.serialization import SerializedCheck, SerializedError
@@ -37,7 +38,7 @@ class JunitXMLHandler(EventHandler):
37
38
  group_by_case(event.result.checks, context.code_sample_style), 1
38
39
  ):
39
40
  checks = sorted(group, key=lambda c: c.name != "not_a_server_error")
40
- test_case.add_failure_info(message=build_failure_message(idx, code_sample, checks))
41
+ test_case.add_failure_info(message=build_failure_message(context, idx, code_sample, checks))
41
42
  elif event.status == Status.error:
42
43
  test_case.add_error_info(message=build_error_message(context, event.result.errors[-1]))
43
44
  elif event.status == Status.skip:
@@ -48,7 +49,7 @@ class JunitXMLHandler(EventHandler):
48
49
  to_xml_report_file(file_descriptor=self.file_handle, test_suites=test_suites, prettyprint=True)
49
50
 
50
51
 
51
- def build_failure_message(idx: int, code_sample: str, checks: list[SerializedCheck]) -> str:
52
+ def build_failure_message(context: ExecutionContext, idx: int, code_sample: str, checks: list[SerializedCheck]) -> str:
52
53
  from ..transports.responses import get_reason
53
54
 
54
55
  message = ""
@@ -73,7 +74,7 @@ def build_failure_message(idx: int, code_sample: str, checks: list[SerializedChe
73
74
  # Checked that is not None
74
75
  body = cast(bytes, check.response.deserialize_body())
75
76
  payload = body.decode(encoding)
76
- payload = prepare_response_payload(payload)
77
+ payload = prepare_response_payload(payload, config=context.output_config)
77
78
  payload = textwrap.indent(f"\n`{payload}`\n", prefix=" ")
78
79
  message += payload
79
80
  except UnicodeDecodeError:
@@ -26,9 +26,9 @@ from ...exceptions import (
26
26
  RuntimeErrorType,
27
27
  extract_requests_exception_details,
28
28
  format_exception,
29
- prepare_response_payload,
30
29
  )
31
30
  from ...experimental import GLOBAL_EXPERIMENTS
31
+ from ...internal.output import prepare_response_payload
32
32
  from ...internal.result import Ok
33
33
  from ...models import Status
34
34
  from ...runner import events
@@ -314,7 +314,7 @@ def display_failures_for_single_test(context: ExecutionContext, result: Serializ
314
314
  # Checked that is not None
315
315
  body = cast(bytes, check.response.deserialize_body())
316
316
  payload = body.decode(encoding)
317
- payload = prepare_response_payload(payload)
317
+ payload = prepare_response_payload(payload, config=context.output_config)
318
318
  payload = textwrap.indent(f"\n`{payload}`", prefix=" ")
319
319
  click.echo(payload)
320
320
  except UnicodeDecodeError:
@@ -878,7 +878,11 @@ def handle_stateful_event(context: ExecutionContext, event: events.StatefulEvent
878
878
  if not experimental.STATEFUL_ONLY.is_enabled:
879
879
  click.echo()
880
880
  click.secho("Stateful tests\n", bold=True)
881
- elif isinstance(event.data, stateful_events.ScenarioFinished) and not event.data.is_final:
881
+ elif (
882
+ isinstance(event.data, stateful_events.ScenarioFinished)
883
+ and not event.data.is_final
884
+ and event.data.status != stateful_events.ScenarioStatus.REJECTED
885
+ ):
882
886
  display_execution_result(context, event.data.status.value)
883
887
  elif isinstance(event.data, stateful_events.RunFinished):
884
888
  click.echo()
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import enum
4
- import json
5
4
  import re
6
5
  import traceback
7
6
  from dataclasses import dataclass, field
@@ -12,6 +11,7 @@ from typing import TYPE_CHECKING, Any, Callable, Generator, NoReturn
12
11
 
13
12
  from .constants import SERIALIZERS_SUGGESTION_MESSAGE
14
13
  from .failures import FailureContext
14
+ from .internal.output import truncate_json
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  import hypothesis.errors
@@ -207,7 +207,7 @@ class OperationSchemaError(Exception):
207
207
  message = "Invalid schema definition"
208
208
  error_path = " -> ".join(str(entry) for entry in error.path) or "[root]"
209
209
  message += f"\n\nLocation:\n {error_path}"
210
- instance = truncated_json(error.instance)
210
+ instance = truncate_json(error.instance)
211
211
  message += f"\n\nProblematic definition:\n{instance}"
212
212
  message += "\n\nError details:\n "
213
213
  # This default message contains the instance which we already printed
@@ -300,39 +300,6 @@ class InvalidHeadersExample(OperationSchemaError):
300
300
  return cls(message)
301
301
 
302
302
 
303
- def truncated_json(data: Any, max_lines: int = 10, max_width: int = 80) -> str:
304
- # Convert JSON to string with indentation
305
- indent = 4
306
- serialized = json.dumps(data, indent=indent)
307
-
308
- # Split string by lines
309
-
310
- lines = [line[: max_width - 3] + "..." if len(line) > max_width else line for line in serialized.split("\n")]
311
-
312
- if len(lines) <= max_lines:
313
- return "\n".join(lines)
314
-
315
- truncated_lines = lines[: max_lines - 1]
316
- indentation = " " * indent
317
- truncated_lines.append(f"{indentation}// Output truncated...")
318
- truncated_lines.append(lines[-1])
319
-
320
- return "\n".join(truncated_lines)
321
-
322
-
323
- MAX_PAYLOAD_SIZE = 512
324
-
325
-
326
- def prepare_response_payload(payload: str, max_size: int = MAX_PAYLOAD_SIZE) -> str:
327
- if payload.endswith("\r\n"):
328
- payload = payload[:-2]
329
- elif payload.endswith("\n"):
330
- payload = payload[:-1]
331
- if len(payload) > max_size:
332
- payload = payload[:max_size] + " // Output truncated..."
333
- return payload
334
-
335
-
336
303
  class DeadlineExceeded(Exception):
337
304
  """Test took too long to run."""
338
305
 
schemathesis/failures.py CHANGED
@@ -5,6 +5,8 @@ from dataclasses import dataclass
5
5
  from json import JSONDecodeError
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
+ from schemathesis.internal.output import OutputConfig
9
+
8
10
  if TYPE_CHECKING:
9
11
  from graphql.error import GraphQLFormattedError
10
12
  from jsonschema import ValidationError
@@ -42,11 +44,14 @@ class ValidationErrorContext(FailureContext):
42
44
  return ("/".join(map(str, self.schema_path)),)
43
45
 
44
46
  @classmethod
45
- def from_exception(cls, exc: ValidationError) -> ValidationErrorContext:
46
- from .exceptions import truncated_json
47
-
48
- schema = textwrap.indent(truncated_json(exc.schema, max_lines=20), prefix=" ")
49
- value = textwrap.indent(truncated_json(exc.instance, max_lines=20), prefix=" ")
47
+ def from_exception(
48
+ cls, exc: ValidationError, *, output_config: OutputConfig | None = None
49
+ ) -> ValidationErrorContext:
50
+ from .internal.output import truncate_json
51
+
52
+ output_config = OutputConfig.from_parent(output_config, max_lines=20)
53
+ schema = textwrap.indent(truncate_json(exc.schema, config=output_config), prefix=" ")
54
+ value = textwrap.indent(truncate_json(exc.instance, config=output_config), prefix=" ")
50
55
  schema_path = list(exc.absolute_schema_path)
51
56
  if len(schema_path) > 1:
52
57
  # Exclude the last segment, which is already in the schema
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass, replace
5
+ from typing import Any
6
+
7
+ TRUNCATED = "// Output truncated..."
8
+ MAX_PAYLOAD_SIZE = 512
9
+ MAX_LINES = 10
10
+ MAX_WIDTH = 80
11
+
12
+
13
+ @dataclass
14
+ class OutputConfig:
15
+ """Options for configuring various aspects of Schemathesis output."""
16
+
17
+ truncate: bool = True
18
+ max_payload_size: int = MAX_PAYLOAD_SIZE
19
+ max_lines: int = MAX_LINES
20
+ max_width: int = MAX_WIDTH
21
+
22
+ @classmethod
23
+ def from_parent(cls, parent: OutputConfig | None = None, **changes: Any) -> OutputConfig:
24
+ parent = parent or OutputConfig()
25
+ return parent.replace(**changes)
26
+
27
+ def replace(self, **changes: Any) -> OutputConfig:
28
+ """Create a new instance with updated values."""
29
+ return replace(self, **changes)
30
+
31
+
32
+ def truncate_json(data: Any, *, config: OutputConfig | None = None) -> str:
33
+ config = config or OutputConfig()
34
+ # Convert JSON to string with indentation
35
+ indent = 4
36
+ serialized = json.dumps(data, indent=indent)
37
+ if not config.truncate:
38
+ return serialized
39
+
40
+ # Split string by lines
41
+
42
+ lines = [
43
+ line[: config.max_width - 3] + "..." if len(line) > config.max_width else line
44
+ for line in serialized.split("\n")
45
+ ]
46
+
47
+ if len(lines) <= config.max_lines:
48
+ return "\n".join(lines)
49
+
50
+ truncated_lines = lines[: config.max_lines - 1]
51
+ indentation = " " * indent
52
+ truncated_lines.append(f"{indentation}{TRUNCATED}")
53
+ truncated_lines.append(lines[-1])
54
+
55
+ return "\n".join(truncated_lines)
56
+
57
+
58
+ def prepare_response_payload(payload: str, *, config: OutputConfig | None = None) -> str:
59
+ if payload.endswith("\r\n"):
60
+ payload = payload[:-2]
61
+ elif payload.endswith("\n"):
62
+ payload = payload[:-1]
63
+ config = config or OutputConfig()
64
+ if not config.truncate:
65
+ return payload
66
+ if len(payload) > config.max_payload_size:
67
+ payload = payload[: config.max_payload_size] + f" {TRUNCATED}"
68
+ return payload
schemathesis/lazy.py CHANGED
@@ -21,6 +21,7 @@ from .constants import FLAKY_FAILURE_MESSAGE, NOT_SET
21
21
  from .exceptions import CheckFailed, OperationSchemaError, SkipTest, get_grouped_exception
22
22
  from .generation import DataGenerationMethodInput, GenerationConfig
23
23
  from .hooks import HookDispatcher, HookScope
24
+ from .internal.output import OutputConfig
24
25
  from .internal.result import Ok
25
26
  from .models import APIOperation
26
27
  from .schemas import BaseSchema
@@ -52,6 +53,7 @@ class LazySchema:
52
53
  skip_deprecated_operations: bool = False
53
54
  data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET
54
55
  generation_config: GenerationConfig | NotSet = NOT_SET
56
+ output_config: OutputConfig | NotSet = NOT_SET
55
57
  code_sample_style: CodeSampleStyle = CodeSampleStyle.default()
56
58
  rate_limiter: Limiter | None = None
57
59
  sanitize_output: bool = True
@@ -69,6 +71,7 @@ class LazySchema:
69
71
  skip_deprecated_operations: bool | NotSet = NOT_SET,
70
72
  data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
71
73
  generation_config: GenerationConfig | NotSet = NOT_SET,
74
+ output_config: OutputConfig | NotSet = NOT_SET,
72
75
  code_sample_style: str | NotSet = NOT_SET,
73
76
  ) -> Callable:
74
77
  if method is NOT_SET:
@@ -83,6 +86,8 @@ class LazySchema:
83
86
  data_generation_methods = self.data_generation_methods
84
87
  if generation_config is NOT_SET:
85
88
  generation_config = self.generation_config
89
+ if output_config is NOT_SET:
90
+ output_config = self.output_config
86
91
  if isinstance(code_sample_style, str):
87
92
  _code_sample_style = CodeSampleStyle.from_str(code_sample_style)
88
93
  else:
@@ -122,6 +127,7 @@ class LazySchema:
122
127
  skip_deprecated_operations=skip_deprecated_operations,
123
128
  data_generation_methods=data_generation_methods,
124
129
  generation_config=generation_config,
130
+ output_config=output_config,
125
131
  code_sample_style=_code_sample_style,
126
132
  app=self.app,
127
133
  rate_limiter=self.rate_limiter,
@@ -326,6 +332,7 @@ def get_schema(
326
332
  skip_deprecated_operations: bool | NotSet = NOT_SET,
327
333
  data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
328
334
  generation_config: GenerationConfig | NotSet = NOT_SET,
335
+ output_config: OutputConfig | NotSet = NOT_SET,
329
336
  code_sample_style: CodeSampleStyle,
330
337
  rate_limiter: Limiter | None,
331
338
  sanitize_output: bool,
@@ -348,6 +355,7 @@ def get_schema(
348
355
  skip_deprecated_operations=skip_deprecated_operations,
349
356
  data_generation_methods=data_generation_methods,
350
357
  generation_config=generation_config,
358
+ output_config=output_config,
351
359
  code_sample_style=code_sample_style,
352
360
  rate_limiter=rate_limiter,
353
361
  sanitize_output=sanitize_output,
schemathesis/models.py CHANGED
@@ -46,12 +46,12 @@ from .exceptions import (
46
46
  deduplicate_failed_checks,
47
47
  get_grouped_exception,
48
48
  maybe_set_assertion_message,
49
- prepare_response_payload,
50
49
  )
51
50
  from .generation import DataGenerationMethod, GenerationConfig, generate_random_case_id
52
51
  from .hooks import GLOBAL_HOOK_DISPATCHER, HookContext, HookDispatcher, dispatch
53
52
  from .internal.copy import fast_deepcopy
54
53
  from .internal.deprecation import deprecated_function, deprecated_property
54
+ from .internal.output import prepare_response_payload
55
55
  from .parameters import Parameter, ParameterSet, PayloadAlternatives
56
56
  from .sanitization import sanitize_request, sanitize_response
57
57
  from .serializers import Serializer
@@ -420,7 +420,7 @@ class Case:
420
420
  if not payload:
421
421
  formatted += "\n\n <EMPTY>"
422
422
  else:
423
- payload = prepare_response_payload(payload)
423
+ payload = prepare_response_payload(payload, config=self.operation.schema.output_config)
424
424
  payload = textwrap.indent(f"\n`{payload}`", prefix=" ")
425
425
  formatted += f"\n{payload}"
426
426
  code_sample_style = (
@@ -301,6 +301,7 @@ class StatefulEvent(ExecutionEvent):
301
301
  class AfterStatefulExecution(ExecutionEvent):
302
302
  """Happens after the stateful test run."""
303
303
 
304
+ status: Status
304
305
  result: SerializedTestResult
305
306
 
306
307
 
@@ -216,7 +216,7 @@ class BaseRunner:
216
216
 
217
217
  def _run_stateful_tests(self, results: TestResultSet) -> Generator[events.ExecutionEvent, None, None]:
218
218
  # Run new-style stateful tests
219
- if self.stateful is not None and experimental.STATEFUL_TEST_RUNNER.is_enabled:
219
+ if self.stateful is not None and experimental.STATEFUL_TEST_RUNNER.is_enabled and self.schema.links_count > 0:
220
220
  result = TestResult(
221
221
  method="",
222
222
  path="",
@@ -232,13 +232,21 @@ class BaseRunner:
232
232
  )
233
233
  state_machine = self.schema.as_state_machine()
234
234
  runner = state_machine.runner(config=config)
235
+ status = Status.success
235
236
  for stateful_event in runner.execute():
236
237
  if isinstance(stateful_event, stateful_events.SuiteFinished):
238
+ if stateful_event.failures and status != Status.error:
239
+ status = Status.failure
237
240
  for failure in stateful_event.failures:
238
241
  result.checks.append(failure)
242
+ elif isinstance(stateful_event, stateful_events.Errored):
243
+ status = Status.error
239
244
  yield events.StatefulEvent(data=stateful_event)
240
245
  results.append(result)
241
- yield events.AfterStatefulExecution(result=SerializedTestResult.from_test_result(result))
246
+ yield events.AfterStatefulExecution(
247
+ status=status,
248
+ result=SerializedTestResult.from_test_result(result),
249
+ )
242
250
 
243
251
  def _run_tests(
244
252
  self,
schemathesis/schemas.py CHANGED
@@ -44,6 +44,7 @@ from .generation import (
44
44
  GenerationConfig,
45
45
  )
46
46
  from .hooks import HookContext, HookDispatcher, HookScope, dispatch
47
+ from .internal.output import OutputConfig
47
48
  from .internal.result import Ok, Result
48
49
  from .models import APIOperation, Case
49
50
  from .stateful import Stateful, StatefulTest
@@ -94,6 +95,7 @@ class BaseSchema(Mapping):
94
95
  default_factory=lambda: list(DEFAULT_DATA_GENERATION_METHODS)
95
96
  )
96
97
  generation_config: GenerationConfig = field(default_factory=GenerationConfig)
98
+ output_config: OutputConfig = field(default_factory=OutputConfig)
97
99
  code_sample_style: CodeSampleStyle = CodeSampleStyle.default()
98
100
  rate_limiter: Limiter | None = None
99
101
  sanitize_output: bool = True
@@ -286,6 +288,7 @@ class BaseSchema(Mapping):
286
288
  skip_deprecated_operations: bool | NotSet = NOT_SET,
287
289
  data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
288
290
  generation_config: GenerationConfig | NotSet = NOT_SET,
291
+ output_config: OutputConfig | NotSet = NOT_SET,
289
292
  code_sample_style: CodeSampleStyle | NotSet = NOT_SET,
290
293
  rate_limiter: Limiter | None = NOT_SET,
291
294
  sanitize_output: bool | NotSet | None = NOT_SET,
@@ -314,6 +317,8 @@ class BaseSchema(Mapping):
314
317
  data_generation_methods = self.data_generation_methods
315
318
  if generation_config is NOT_SET:
316
319
  generation_config = self.generation_config
320
+ if output_config is NOT_SET:
321
+ output_config = self.output_config
317
322
  if code_sample_style is NOT_SET:
318
323
  code_sample_style = self.code_sample_style
319
324
  if rate_limiter is NOT_SET:
@@ -337,6 +342,7 @@ class BaseSchema(Mapping):
337
342
  skip_deprecated_operations=skip_deprecated_operations, # type: ignore
338
343
  data_generation_methods=data_generation_methods, # type: ignore
339
344
  generation_config=generation_config, # type: ignore
345
+ output_config=output_config, # type: ignore
340
346
  code_sample_style=code_sample_style, # type: ignore
341
347
  rate_limiter=rate_limiter, # type: ignore
342
348
  sanitize_output=sanitize_output, # type: ignore
@@ -150,22 +150,30 @@ def serialize_finished(event: events.Finished) -> dict[str, Any] | None:
150
150
 
151
151
 
152
152
  def serialize_stateful_event(event: events.StatefulEvent) -> dict[str, Any] | None:
153
- if isinstance(event.data, stateful_events.RunStarted):
154
- return {
155
- "data": {
156
- "timestamp": event.data.timestamp,
157
- "started_at": event.data.started_at,
158
- }
153
+ return _serialize_stateful_event(event.data)
154
+
155
+
156
+ def _serialize_stateful_event(event: stateful_events.StatefulEvent) -> dict[str, Any] | None:
157
+ data: dict[str, Any]
158
+ if isinstance(event, stateful_events.RunStarted):
159
+ data = {
160
+ "timestamp": event.timestamp,
161
+ "started_at": event.started_at,
159
162
  }
160
- elif isinstance(event.data, stateful_events.SuiteFinished):
161
- return {
162
- "data": {
163
- "timestamp": event.data.timestamp,
164
- "status": event.data.status,
165
- "failures": [_serialize_check(SerializedCheck.from_check(failure)) for failure in event.data.failures],
166
- }
163
+ elif isinstance(event, stateful_events.SuiteFinished):
164
+ data = {
165
+ "timestamp": event.timestamp,
166
+ "status": event.status,
167
+ "failures": [_serialize_check(SerializedCheck.from_check(failure)) for failure in event.failures],
168
+ }
169
+ elif isinstance(event, stateful_events.Errored):
170
+ data = {
171
+ "timestamp": event.timestamp,
172
+ "exception": format_exception(event.exception, True),
167
173
  }
168
- return {"data": asdict(event.data)}
174
+ else:
175
+ data = asdict(event)
176
+ return {"data": {event.__class__.__name__: data}}
169
177
 
170
178
 
171
179
  def serialize_after_stateful_execution(event: events.AfterStatefulExecution) -> dict[str, Any] | None:
@@ -16,6 +16,7 @@ from ...generation import (
16
16
  GenerationConfig,
17
17
  )
18
18
  from ...hooks import HookContext, dispatch
19
+ from ...internal.output import OutputConfig
19
20
  from ...internal.validation import require_relative_url
20
21
  from ...loaders import load_schema_from_url
21
22
  from ...throttling import build_limiter
@@ -52,6 +53,7 @@ def from_path(
52
53
  base_url: str | None = None,
53
54
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
54
55
  generation_config: GenerationConfig | None = None,
56
+ output_config: OutputConfig | None = None,
55
57
  code_sample_style: str = CodeSampleStyle.default().name,
56
58
  rate_limit: str | None = None,
57
59
  encoding: str = "utf8",
@@ -69,6 +71,8 @@ def from_path(
69
71
  base_url=base_url,
70
72
  data_generation_methods=data_generation_methods,
71
73
  code_sample_style=code_sample_style,
74
+ generation_config=generation_config,
75
+ output_config=output_config,
72
76
  location=pathlib.Path(path).absolute().as_uri(),
73
77
  rate_limit=rate_limit,
74
78
  sanitize_output=sanitize_output,
@@ -161,6 +165,7 @@ def from_file(
161
165
  base_url: str | None = None,
162
166
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
163
167
  generation_config: GenerationConfig | None = None,
168
+ output_config: OutputConfig | None = None,
164
169
  code_sample_style: str = CodeSampleStyle.default().name,
165
170
  location: str | None = None,
166
171
  rate_limit: str | None = None,
@@ -198,6 +203,8 @@ def from_file(
198
203
  app=app,
199
204
  base_url=base_url,
200
205
  data_generation_methods=data_generation_methods,
206
+ generation_config=generation_config,
207
+ output_config=output_config,
201
208
  code_sample_style=code_sample_style,
202
209
  location=location,
203
210
  rate_limit=rate_limit,
@@ -221,6 +228,7 @@ def from_dict(
221
228
  location: str | None = None,
222
229
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
223
230
  generation_config: GenerationConfig | None = None,
231
+ output_config: OutputConfig | None = None,
224
232
  code_sample_style: str = CodeSampleStyle.default().name,
225
233
  rate_limit: str | None = None,
226
234
  sanitize_output: bool = True,
@@ -250,6 +258,8 @@ def from_dict(
250
258
  base_url=base_url,
251
259
  app=app,
252
260
  data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
261
+ generation_config=generation_config or GenerationConfig(),
262
+ output_config=output_config or OutputConfig(),
253
263
  code_sample_style=_code_sample_style,
254
264
  rate_limiter=rate_limiter,
255
265
  sanitize_output=sanitize_output,
@@ -266,6 +276,7 @@ def from_wsgi(
266
276
  base_url: str | None = None,
267
277
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
268
278
  generation_config: GenerationConfig | None = None,
279
+ output_config: OutputConfig | None = None,
269
280
  code_sample_style: str = CodeSampleStyle.default().name,
270
281
  rate_limit: str | None = None,
271
282
  sanitize_output: bool = True,
@@ -294,6 +305,8 @@ def from_wsgi(
294
305
  base_url=base_url,
295
306
  app=app,
296
307
  data_generation_methods=data_generation_methods,
308
+ generation_config=generation_config,
309
+ output_config=output_config,
297
310
  code_sample_style=code_sample_style,
298
311
  rate_limit=rate_limit,
299
312
  sanitize_output=sanitize_output,
@@ -307,6 +320,7 @@ def from_asgi(
307
320
  base_url: str | None = None,
308
321
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
309
322
  generation_config: GenerationConfig | None = None,
323
+ output_config: OutputConfig | None = None,
310
324
  code_sample_style: str = CodeSampleStyle.default().name,
311
325
  rate_limit: str | None = None,
312
326
  sanitize_output: bool = True,
@@ -332,6 +346,8 @@ def from_asgi(
332
346
  base_url=base_url,
333
347
  app=app,
334
348
  data_generation_methods=data_generation_methods,
349
+ generation_config=generation_config,
350
+ output_config=output_config,
335
351
  code_sample_style=code_sample_style,
336
352
  rate_limit=rate_limit,
337
353
  sanitize_output=sanitize_output,
@@ -18,6 +18,7 @@ from ...generation import (
18
18
  GenerationConfig,
19
19
  )
20
20
  from ...hooks import HookContext, dispatch
21
+ from ...internal.output import OutputConfig
21
22
  from ...internal.validation import require_relative_url
22
23
  from ...loaders import load_schema_from_url, load_yaml
23
24
  from ...throttling import build_limiter
@@ -79,6 +80,7 @@ def from_path(
79
80
  force_schema_version: str | None = None,
80
81
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
81
82
  generation_config: GenerationConfig | None = None,
83
+ output_config: OutputConfig | None = None,
82
84
  code_sample_style: str = CodeSampleStyle.default().name,
83
85
  rate_limit: str | None = None,
84
86
  encoding: str = "utf8",
@@ -103,6 +105,7 @@ def from_path(
103
105
  force_schema_version=force_schema_version,
104
106
  data_generation_methods=data_generation_methods,
105
107
  generation_config=generation_config,
108
+ output_config=output_config,
106
109
  code_sample_style=code_sample_style,
107
110
  location=pathlib.Path(path).absolute().as_uri(),
108
111
  rate_limit=rate_limit,
@@ -127,6 +130,7 @@ def from_uri(
127
130
  force_schema_version: str | None = None,
128
131
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
129
132
  generation_config: GenerationConfig | None = None,
133
+ output_config: OutputConfig | None = None,
130
134
  code_sample_style: str = CodeSampleStyle.default().name,
131
135
  wait_for_schema: float | None = None,
132
136
  rate_limit: str | None = None,
@@ -176,6 +180,7 @@ def from_uri(
176
180
  force_schema_version=force_schema_version,
177
181
  data_generation_methods=data_generation_methods,
178
182
  generation_config=generation_config,
183
+ output_config=output_config,
179
184
  code_sample_style=code_sample_style,
180
185
  location=uri,
181
186
  rate_limit=rate_limit,
@@ -221,6 +226,7 @@ def from_file(
221
226
  force_schema_version: str | None = None,
222
227
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
223
228
  generation_config: GenerationConfig | None = None,
229
+ output_config: OutputConfig | None = None,
224
230
  code_sample_style: str = CodeSampleStyle.default().name,
225
231
  location: str | None = None,
226
232
  rate_limit: str | None = None,
@@ -267,6 +273,7 @@ def from_file(
267
273
  force_schema_version=force_schema_version,
268
274
  data_generation_methods=data_generation_methods,
269
275
  generation_config=generation_config,
276
+ output_config=output_config,
270
277
  code_sample_style=code_sample_style,
271
278
  location=location,
272
279
  rate_limit=rate_limit,
@@ -295,6 +302,7 @@ def from_dict(
295
302
  force_schema_version: str | None = None,
296
303
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
297
304
  generation_config: GenerationConfig | None = None,
305
+ output_config: OutputConfig | None = None,
298
306
  code_sample_style: str = CodeSampleStyle.default().name,
299
307
  location: str | None = None,
300
308
  rate_limit: str | None = None,
@@ -336,6 +344,7 @@ def from_dict(
336
344
  validate_schema=validate_schema,
337
345
  data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
338
346
  generation_config=generation_config or GenerationConfig(),
347
+ output_config=output_config or OutputConfig(),
339
348
  code_sample_style=_code_sample_style,
340
349
  location=location,
341
350
  rate_limiter=rate_limiter,
@@ -378,6 +387,7 @@ def from_dict(
378
387
  validate_schema=validate_schema,
379
388
  data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
380
389
  generation_config=generation_config or GenerationConfig(),
390
+ output_config=output_config or OutputConfig(),
381
391
  code_sample_style=_code_sample_style,
382
392
  location=location,
383
393
  rate_limiter=rate_limiter,
@@ -467,6 +477,7 @@ def from_pytest_fixture(
467
477
  validate_schema: bool = False,
468
478
  data_generation_methods: DataGenerationMethodInput | NotSet = NOT_SET,
469
479
  generation_config: GenerationConfig | NotSet = NOT_SET,
480
+ output_config: OutputConfig | NotSet = NOT_SET,
470
481
  code_sample_style: str = CodeSampleStyle.default().name,
471
482
  rate_limit: str | None = None,
472
483
  sanitize_output: bool = True,
@@ -504,6 +515,7 @@ def from_pytest_fixture(
504
515
  validate_schema=validate_schema,
505
516
  data_generation_methods=_data_generation_methods,
506
517
  generation_config=generation_config,
518
+ output_config=output_config,
507
519
  code_sample_style=_code_sample_style,
508
520
  rate_limiter=rate_limiter,
509
521
  sanitize_output=sanitize_output,
@@ -524,6 +536,7 @@ def from_wsgi(
524
536
  force_schema_version: str | None = None,
525
537
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
526
538
  generation_config: GenerationConfig | None = None,
539
+ output_config: OutputConfig | None = None,
527
540
  code_sample_style: str = CodeSampleStyle.default().name,
528
541
  rate_limit: str | None = None,
529
542
  sanitize_output: bool = True,
@@ -555,6 +568,7 @@ def from_wsgi(
555
568
  force_schema_version=force_schema_version,
556
569
  data_generation_methods=data_generation_methods,
557
570
  generation_config=generation_config,
571
+ output_config=output_config,
558
572
  code_sample_style=code_sample_style,
559
573
  location=schema_path,
560
574
  rate_limit=rate_limit,
@@ -587,6 +601,7 @@ def from_aiohttp(
587
601
  force_schema_version: str | None = None,
588
602
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
589
603
  generation_config: GenerationConfig | None = None,
604
+ output_config: OutputConfig | None = None,
590
605
  code_sample_style: str = CodeSampleStyle.default().name,
591
606
  rate_limit: str | None = None,
592
607
  sanitize_output: bool = True,
@@ -614,6 +629,7 @@ def from_aiohttp(
614
629
  force_schema_version=force_schema_version,
615
630
  data_generation_methods=data_generation_methods,
616
631
  generation_config=generation_config,
632
+ output_config=output_config,
617
633
  code_sample_style=code_sample_style,
618
634
  rate_limit=rate_limit,
619
635
  sanitize_output=sanitize_output,
@@ -635,6 +651,7 @@ def from_asgi(
635
651
  force_schema_version: str | None = None,
636
652
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
637
653
  generation_config: GenerationConfig | None = None,
654
+ output_config: OutputConfig | None = None,
638
655
  code_sample_style: str = CodeSampleStyle.default().name,
639
656
  rate_limit: str | None = None,
640
657
  sanitize_output: bool = True,
@@ -664,6 +681,7 @@ def from_asgi(
664
681
  force_schema_version=force_schema_version,
665
682
  data_generation_methods=data_generation_methods,
666
683
  generation_config=generation_config,
684
+ output_config=output_config,
667
685
  code_sample_style=code_sample_style,
668
686
  location=schema_path,
669
687
  rate_limit=rate_limit,
@@ -657,7 +657,7 @@ class BaseOpenAPISchema(BaseSchema):
657
657
  jsonschema.validate(data, schema, cls=cls, resolver=resolver)
658
658
  except jsonschema.ValidationError as exc:
659
659
  exc_class = get_schema_validation_error(operation.verbose_name, exc)
660
- ctx = failures.ValidationErrorContext.from_exception(exc)
660
+ ctx = failures.ValidationErrorContext.from_exception(exc, output_config=operation.schema.output_config)
661
661
  try:
662
662
  raise exc_class(ctx.title, context=ctx) from exc
663
663
  except Exception as exc:
@@ -40,7 +40,7 @@ class RunnerContext:
40
40
  # Unique failures collected in the current suite
41
41
  failures_for_suite: list[Check] = field(default_factory=list)
42
42
  # Status of the current step
43
- current_step_status: events.StepStatus = events.StepStatus.SUCCESS
43
+ current_step_status: events.StepStatus | None = None
44
44
  current_response: GenericResponse | None = None
45
45
 
46
46
  @property
@@ -49,18 +49,28 @@ class RunnerContext:
49
49
  return events.ScenarioStatus.SUCCESS
50
50
  elif self.current_step_status == events.StepStatus.FAILURE:
51
51
  return events.ScenarioStatus.FAILURE
52
- return events.ScenarioStatus.ERROR
52
+ elif self.current_step_status == events.StepStatus.ERROR:
53
+ return events.ScenarioStatus.ERROR
54
+ elif self.current_step_status == events.StepStatus.INTERRUPTED:
55
+ return events.ScenarioStatus.INTERRUPTED
56
+ return events.ScenarioStatus.REJECTED
53
57
 
54
58
  def reset_step(self) -> None:
55
- self.current_step_status = events.StepStatus.SUCCESS
59
+ self.current_step_status = None
56
60
  self.current_response = None
57
61
 
62
+ def step_succeeded(self) -> None:
63
+ self.current_step_status = events.StepStatus.SUCCESS
64
+
58
65
  def step_failed(self) -> None:
59
66
  self.current_step_status = events.StepStatus.FAILURE
60
67
 
61
68
  def step_errored(self) -> None:
62
69
  self.current_step_status = events.StepStatus.ERROR
63
70
 
71
+ def step_interrupted(self) -> None:
72
+ self.current_step_status = events.StepStatus.INTERRUPTED
73
+
64
74
  def mark_as_seen_in_run(self, exc: CheckFailed) -> None:
65
75
  key = _failure_cache_key(exc)
66
76
  self.seen_in_run.add(key)
@@ -23,6 +23,8 @@ class RunStatus(str, Enum):
23
23
  class StatefulEvent:
24
24
  """Basic stateful test event."""
25
25
 
26
+ timestamp: float
27
+
26
28
  __slots__ = ("timestamp",)
27
29
 
28
30
 
@@ -30,6 +32,7 @@ class StatefulEvent:
30
32
  class RunStarted(StatefulEvent):
31
33
  """Before executing all scenarios."""
32
34
 
35
+ started_at: float
33
36
  state_machine: Type[APIStateMachine]
34
37
 
35
38
  __slots__ = ("state_machine", "timestamp", "started_at")
@@ -92,8 +95,10 @@ class ScenarioStatus(str, Enum):
92
95
 
93
96
  SUCCESS = "success"
94
97
  FAILURE = "failure"
95
- # TODO: Count for Hypothesis' rejected?
96
98
  ERROR = "error"
99
+ # Rejected by Hypothesis
100
+ REJECTED = "rejected"
101
+ INTERRUPTED = "interrupted"
97
102
 
98
103
 
99
104
  @dataclass
@@ -132,6 +137,7 @@ class StepStatus(str, Enum):
132
137
  SUCCESS = "success"
133
138
  FAILURE = "failure"
134
139
  ERROR = "error"
140
+ INTERRUPTED = "interrupted"
135
141
 
136
142
 
137
143
  @dataclass
@@ -4,7 +4,7 @@ import queue
4
4
  import threading
5
5
  from contextlib import contextmanager
6
6
  from dataclasses import dataclass, field
7
- from typing import TYPE_CHECKING, Any, Generator, Iterator, Type
7
+ from typing import TYPE_CHECKING, Any, Generator, Iterator, Type, cast
8
8
 
9
9
  from hypothesis.control import current_build_context
10
10
  from hypothesis.errors import Flaky
@@ -134,15 +134,18 @@ def _execute_state_machine_loop(
134
134
  if stop_event.is_set():
135
135
  raise KeyboardInterrupt
136
136
  event_queue.put(events.StepStarted())
137
- ctx.reset_step()
138
137
  try:
139
138
  result = super().step(case, previous)
139
+ ctx.step_succeeded()
140
140
  except CheckFailed:
141
141
  ctx.step_failed()
142
142
  raise
143
143
  except Exception:
144
144
  ctx.step_errored()
145
145
  raise
146
+ except KeyboardInterrupt:
147
+ ctx.step_interrupted()
148
+ raise
146
149
  finally:
147
150
  transition_id: events.TransitionId | None
148
151
  if previous is not None:
@@ -162,9 +165,10 @@ def _execute_state_machine_loop(
162
165
  )
163
166
  else:
164
167
  response = None
168
+ status = cast(events.StepStatus, ctx.current_step_status)
165
169
  event_queue.put(
166
170
  events.StepFinished(
167
- status=ctx.current_step_status,
171
+ status=status,
168
172
  transition_id=transition_id,
169
173
  target=case.operation.verbose_name,
170
174
  response=response,
@@ -186,6 +190,7 @@ def _execute_state_machine_loop(
186
190
  is_final=build_ctx.is_final,
187
191
  )
188
192
  )
193
+ ctx.reset_step()
189
194
  super().teardown()
190
195
 
191
196
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: schemathesis
3
- Version: 3.30.0
3
+ Version: 3.30.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
@@ -34,8 +34,8 @@ Requires-Dist: colorama<1.0,>=0.4
34
34
  Requires-Dist: httpx<1.0,>=0.22.0
35
35
  Requires-Dist: hypothesis-graphql<1,>=0.11.0
36
36
  Requires-Dist: hypothesis-jsonschema<0.24,>=0.23.1
37
- Requires-Dist: hypothesis<7,>=6.84.3; python_version > '3.8'
38
- Requires-Dist: hypothesis[zoneinfo]<7,>=6.84.3; python_version == '3.8'
37
+ Requires-Dist: hypothesis<7,>=6.103.4; python_version > '3.8'
38
+ Requires-Dist: hypothesis[zoneinfo]<7,>=6.103.4; python_version == '3.8'
39
39
  Requires-Dist: jsonschema<5.0,>=4.18.0
40
40
  Requires-Dist: junit-xml<2.0,>=1.9
41
41
  Requires-Dist: pyrate-limiter<4.0,>=2.10
@@ -10,37 +10,37 @@ schemathesis/auths.py,sha256=NeJqsjtDgJtHMyrHc6V1NTpkAh1K8ZKLalpB3v80cT4,14734
10
10
  schemathesis/checks.py,sha256=XplKduiRbRrxZx-wDMGaW91aIitr5RL9vjMP6bvCFRg,2272
11
11
  schemathesis/code_samples.py,sha256=xk1-1jnXg5hS40VzIZp8PEtZwGaazNlVKMT7_X-zG-M,4123
12
12
  schemathesis/constants.py,sha256=l1YQ7PXhEj9dyf9CTESVUpPOaFCH7iz-Fe8o4v6Th_s,2673
13
- schemathesis/exceptions.py,sha256=-OgYikO5aEJ_43aYOql4xNdBf__4Vi-YNs9_TuYe8lk,20524
14
- schemathesis/failures.py,sha256=2gp6JpZLVzM9juU4i7TfwOCaT9RDbiEhUOTP3KE5xNE,6792
13
+ schemathesis/exceptions.py,sha256=ZX_KqnkQ80GACsBgm6lJV43aFBJws-2vGtnFu72n-O4,19576
14
+ schemathesis/failures.py,sha256=wXz5Kr5i-ojcYc-BdzFlNbNGOfoVXHZM6kd4iULdHK4,7003
15
15
  schemathesis/filters.py,sha256=0fYzn9sJ35k3Znx1P8FrbSdoUcdslcibtGh-IOTRwB8,10251
16
16
  schemathesis/graphql.py,sha256=YkoKWY5K8lxp7H3ikAs-IsoDbiPwJvChG7O8p3DgwtI,229
17
17
  schemathesis/hooks.py,sha256=dveqMmThIvt4fDahUXhU2nCq5pFvYjzzd1Ys_MhrJZA,12398
18
- schemathesis/lazy.py,sha256=sK81rXgBuRzG0oMbfbnMedrAR3McBB47gA0C49Nc8l0,14847
18
+ schemathesis/lazy.py,sha256=eVdGkTZK0fWvUlFUCFGGlViH2NWEtYIjxiNkF4fBWhI,15218
19
19
  schemathesis/loaders.py,sha256=OtCD1o0TVmSNAUF7dgHpouoAXtY6w9vEtsRVGv4lE0g,4588
20
- schemathesis/models.py,sha256=-L7g2rWFrTi2T-2LBpT807yzGUH8-iLMz2F-moJqi4w,43957
20
+ schemathesis/models.py,sha256=byTGJD6zyXFvSvQCe-Mxu3lOdvU3EQ9vmadv6Jv6f5I,44025
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=S7EzCdiXlNDnYpjhFKcuucE24XmuCutU9Bk1o86wz_k,17958
24
+ schemathesis/schemas.py,sha256=aa84Kmin4lu68vU4y3DpPGo4Z1Pt7LFCqe9kzW35Asc,18267
25
25
  schemathesis/serializers.py,sha256=kxXZ-UGa1v_vOm0sC4QYcrNv4rfvI7tHGT2elRVbCbc,11649
26
26
  schemathesis/targets.py,sha256=N1Zzgqa1PNycWIeGpra7q-6ASn2x4r9Jompn35bmlsE,1163
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=5KaQb3XCXd-gksoXd6yIQYmo1MjrLeTpo-K8NtR5vrI,65017
30
+ schemathesis/cli/__init__.py,sha256=EmgM2nTU81Q9BdAxwFl6hBsGZl1SYTr9tvCqHDyWu2U,65680
31
31
  schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
32
32
  schemathesis/cli/callbacks.py,sha256=tZSe6EIyc1fdKZ8xGaE0w0LuGCMtT7m9cW41Rd0Nah0,14949
33
33
  schemathesis/cli/cassettes.py,sha256=WLt2svZm23vZX5buQWmWO94QwQEC8OM2g8yH50rfatE,12990
34
34
  schemathesis/cli/constants.py,sha256=ogSuZs68KvzHNKF0yaiBkxLcGbo8IVR3xaIfsy1H1IQ,1546
35
- schemathesis/cli/context.py,sha256=sI1GH41CehHMqDE_Vvo4DOGtSNvHUQvskgeVMDbv_pE,2110
35
+ schemathesis/cli/context.py,sha256=BsaKvfmh9LmiDU3D7wixSzBjk9KWNRWJwYA3Xa3d35I,2223
36
36
  schemathesis/cli/debug.py,sha256=_YA-bX1ujHl4bqQDEum7M-I2XHBTEGbvgkhvcvKhmgU,658
37
37
  schemathesis/cli/handlers.py,sha256=RjvogPCqqFTiyVYWrGG6euw9-6h-7uSeFvS-ouU8eWs,389
38
- schemathesis/cli/junitxml.py,sha256=WdxtBKBi6V5Ow840Nde21MvUlDJQyJu2M6DXx1VDuyo,4648
38
+ schemathesis/cli/junitxml.py,sha256=yWacOIrTC9UI-IlgZnu8nfSmpkxfMT22NqJ3L1SZa3w,4743
39
39
  schemathesis/cli/options.py,sha256=DY5PUzpUNyYgpcqqFTeZjmVUykpbaI9fbN44QIWNOfA,2559
40
40
  schemathesis/cli/reporting.py,sha256=_Jh8sJMfOf_CUHdbTpG5VRsfW8INtXQjtk1njmGWEnY,3298
41
41
  schemathesis/cli/sanitization.py,sha256=pVlQnVDC1_Ugp0oe1LEkRorFdBRDHqr_NWWKaOaNdY0,728
42
42
  schemathesis/cli/output/__init__.py,sha256=AXaUzQ1nhQ-vXhW4-X-91vE2VQtEcCOrGtQXXNN55iQ,29
43
- schemathesis/cli/output/default.py,sha256=1FzwzDw_5tHnXs8bNG2HyNAONu4CWQWKnH29swlbvIE,39602
43
+ schemathesis/cli/output/default.py,sha256=0zFolpo2HS2NH-IzRqEIZqfa_6Vk4jEHpfykLQ7apAM,39755
44
44
  schemathesis/cli/output/short.py,sha256=CL6-Apxr5tuZ3BL1vecV1MiRY1wDt21g0wiUwZu6mLM,2607
45
45
  schemathesis/contrib/__init__.py,sha256=FH8NL8NXgSKBFOF8Jy_EB6T4CJEaiM-tmDhz16B2o4k,187
46
46
  schemathesis/contrib/unique_data.py,sha256=_ElPRLNp0XhdETiZ-aplKln4hgU04jxR17kmjAEWf1I,1318
@@ -63,15 +63,16 @@ schemathesis/internal/copy.py,sha256=lcK01CODz6jogXyH0tsKkPv1PBEX8jeBPxI9MzQ6LN4
63
63
  schemathesis/internal/datetime.py,sha256=zPLBL0XXLNfP-KYel3H2m8pnsxjsA_4d-zTOhJg2EPQ,136
64
64
  schemathesis/internal/deprecation.py,sha256=cF3FYy5syihneSIY7M7aVJW2KTzcd88WOItHFCJtADg,1107
65
65
  schemathesis/internal/jsonschema.py,sha256=-7tF15cXo1ZdhiRFYYfEClXihX2Svc5Loi_Dz1x201k,1157
66
+ schemathesis/internal/output.py,sha256=zMaG5knIuBieVH8CrcmPJgbmQukDs2xdekX0BrK7BZs,1989
66
67
  schemathesis/internal/result.py,sha256=d449YvyONjqjDs-A5DAPgtAI96iT753K8sU6_1HLo2Q,461
67
68
  schemathesis/internal/transformation.py,sha256=3S6AzAqdsEsB5iobFgSuvL0UMUqH0JHC7hGxKwcpqPw,450
68
69
  schemathesis/internal/validation.py,sha256=G7i8jIMUpAeOnDsDF_eWYvRZe_yMprRswx0QAtMPyEw,966
69
70
  schemathesis/runner/__init__.py,sha256=n-4CanmlfMiGnir6xbZH-fM5a0pe2GloBFj2NTtet-M,21419
70
- schemathesis/runner/events.py,sha256=1h5EDPhM3t76PSPZo0IToDAidAS8eGDhKuMGtJbX-WU,10556
71
+ schemathesis/runner/events.py,sha256=PJceb_jC-2BKEdMIrH8mSCtt8lbPPq6Gs5qjPicwxmU,10575
71
72
  schemathesis/runner/probes.py,sha256=J-TT0hOKu9j4htWKBcYKmsomcRxmvOl4WpmnKLVXu8M,5546
72
73
  schemathesis/runner/serialization.py,sha256=J8fuG8MSJq3rE3IJs73U1YXWFrNa05k7PGd5Bvq1uec,17356
73
74
  schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
74
- schemathesis/runner/impl/core.py,sha256=45-4914E51z3qtMU53-WrckTaoAm9NMCNIkeiKo2J70,41527
75
+ schemathesis/runner/impl/core.py,sha256=vlq4TWf-s5As49wSmiATTK8N7vf2PFjJtsDoH_rrTrc,41896
75
76
  schemathesis/runner/impl/solo.py,sha256=MatxThgqKsY2tX_hVwjy78oKFeKejb6dFJoX3kGzW4U,3359
76
77
  schemathesis/runner/impl/threadpool.py,sha256=fj2QYoWxIJIxpTCcJQyM_VCRO1YDnW9XQJJnNVFVQxY,15253
77
78
  schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
@@ -85,12 +86,12 @@ schemathesis/service/hosts.py,sha256=ad2Lxq9Zcc9PP-1eFLQnxen4ImglcGOH8n7CGG72NNg
85
86
  schemathesis/service/metadata.py,sha256=x2LeCED1mdPf-YQJmjY8xtcIKHfD1ap5V0BGl-UgqNo,2087
86
87
  schemathesis/service/models.py,sha256=ihItUJ9CvH4TvmdfJY3W88NR82OODF8a3RD7WRXn6RM,6578
87
88
  schemathesis/service/report.py,sha256=4A8nf6_KOjDW3x1VXF8gSf_WY2xXp1Cbz-Owl_GeR7o,8294
88
- schemathesis/service/serialization.py,sha256=WxFVVnAgwE0RIIedzDfIOR1UArdYLxcwE0ghh-_wNB0,9492
89
+ schemathesis/service/serialization.py,sha256=LbEUhcDI9hsFDrxjjH-79QhRd71pKHHc9odsVsUuC7s,9776
89
90
  schemathesis/service/usage.py,sha256=UbXqxeDq5mAjKkfV4hApZsReZmQHXiqoXUYn_Z6YuZk,2438
90
91
  schemathesis/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
92
  schemathesis/specs/graphql/__init__.py,sha256=fgyHtvWNUVWismBTOqxQtgLoTighTfvMv6v6QCD_Oyc,85
92
93
  schemathesis/specs/graphql/_cache.py,sha256=7ras3q_InDJBPykgHroerl9f2jFamC8xJD35376Zs6I,795
93
- schemathesis/specs/graphql/loaders.py,sha256=uOtg0rjCh7e2j6RGMBbWjVXEPHrLKvSFTXFvpXYlIZs,11461
94
+ schemathesis/specs/graphql/loaders.py,sha256=qxNGL67_AfhoRh0hIxlnJVe6do26vqwWS_TrJtB-Lro,12198
94
95
  schemathesis/specs/graphql/nodes.py,sha256=bE3G1kNmqJ8OV4igBvIK-UORrkQA6Nofduf87O3TD9I,541
95
96
  schemathesis/specs/graphql/scalars.py,sha256=9tvLTiYVe8A_E8ASA0czz3Z0Mp9lyak7R4wHpAE_jKo,1805
96
97
  schemathesis/specs/graphql/schemas.py,sha256=ICIejnv05Q9uxljQU06i9RSVa10mAdTESBfM17wMkaI,13491
@@ -106,11 +107,11 @@ schemathesis/specs/openapi/examples.py,sha256=OxzcYWlNQzAjkNf8IAGxEGnQ4DelY4mtPw
106
107
  schemathesis/specs/openapi/filters.py,sha256=6Q9eNQ6zCR-NQkUxgnkSDWxfk3hsZuxemBv7v1rhwb4,1437
107
108
  schemathesis/specs/openapi/formats.py,sha256=JmmkQWNAj5XreXb7Edgj4LADAf4m86YulR_Ec8evpJ4,1220
108
109
  schemathesis/specs/openapi/links.py,sha256=2ucOLs50OhCqu0PEdbT_BGUM3fKnHBl97YGISLpAxLY,16023
109
- schemathesis/specs/openapi/loaders.py,sha256=hroaVnZkYvUn19M8fwgdOiBUSSVkOUR3wXnkgPjoGYU,24110
110
+ schemathesis/specs/openapi/loaders.py,sha256=JJdIz1aT03J9WmUWTLOz6Yhuu69IqmhobQ9_vL6XJ6U,24916
110
111
  schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
111
112
  schemathesis/specs/openapi/parameters.py,sha256=_6vNCnPXcdxjfAQbykCRLHjvmTpu_02xDJghxDrGYr8,13611
112
113
  schemathesis/specs/openapi/references.py,sha256=i1Jd8EtEpSpauFW29DQsYM-JvB4jtYI7dpJAJcdjYe0,9276
113
- schemathesis/specs/openapi/schemas.py,sha256=x3exxkghtkSdmdiN3tnYHHGAT6AA0y-WFerwiOC_Ajo,51715
114
+ schemathesis/specs/openapi/schemas.py,sha256=rEsvuRM4mFbUEfaNfANadyfWhA-iaXbaBMZpjq1anBk,51761
114
115
  schemathesis/specs/openapi/security.py,sha256=sTOCYEsJmxGY2dglrlZRYmJzNZjEZAHm5bZtbYFHLmI,6612
115
116
  schemathesis/specs/openapi/serialization.py,sha256=5qGdFHZ3n80UlbSXrO_bkr4Al_7ci_Z3aSUjZczNDQY,11384
116
117
  schemathesis/specs/openapi/utils.py,sha256=-TCu0hTrlwp2x5qHNp-TxiHRMeIZC9OBmlhLssjRIiQ,742
@@ -131,9 +132,9 @@ schemathesis/specs/openapi/stateful/statistic.py,sha256=EJK4NqeAYRYl1FtU9YEuTLyh
131
132
  schemathesis/specs/openapi/stateful/types.py,sha256=UuGcCTFvaHsqeLN9ZeUNcbjsEwmthoT3UcHfDHchOYo,419
132
133
  schemathesis/stateful/__init__.py,sha256=qyQJ-9Ect-AWZiAsK63F3BTGu-jZnPCOp1q46YAonkQ,4911
133
134
  schemathesis/stateful/config.py,sha256=kuKGLNNk0YrR6G0IzcZh6v0oysHHTPfHi9nHUsfQsF0,2298
134
- schemathesis/stateful/context.py,sha256=eLlUvmR7QqQyXvm6QVyo_JKdpR64GgwoOyfW387M_HA,3335
135
- schemathesis/stateful/events.py,sha256=5GfcKBIo3JyJIBbG-nzE4vGOLbDhRM7fypDBgoh-Shc,4828
136
- schemathesis/stateful/runner.py,sha256=oVcprfG5PLCtD1_VQa86EA3qy3PvmUJLrAF3u5oAhUU,9434
135
+ schemathesis/stateful/context.py,sha256=P2kVeMOMsHOpQ0wyeZ2OfTU5fSCgehKJxzai1_tXiwc,3747
136
+ schemathesis/stateful/events.py,sha256=wXh4O9FELmNWAUnupAFpUFOokjesvQOE8joBUAqjbrU,4947
137
+ schemathesis/stateful/runner.py,sha256=ZcnU0lERjenpG0XnQUSeHH0MOesCg7K97fE53eIxvvc,9633
137
138
  schemathesis/stateful/sink.py,sha256=V1ReRTpN3VfAXtcnSEke4UHhu7IxrYQs0s_gSDMaqWw,2425
138
139
  schemathesis/stateful/state_machine.py,sha256=H-AzMPTKuCKnoCv0b7XPFDsHkzRftNfbvh5xb2H5Hfk,12156
139
140
  schemathesis/stateful/statistic.py,sha256=xPLiCw61ofNXQicqcK_sZyLHiqiGcgQARpwd8AiRubM,487
@@ -143,8 +144,8 @@ schemathesis/transports/auth.py,sha256=4z7c-K7lfyyVqgR6X1v4yiE8ewR_ViAznWFTAsCL0
143
144
  schemathesis/transports/content_types.py,sha256=VrcRQvF5T_TUjrCyrZcYF2LOwKfs3IrLcMtkVSp1ImI,2189
144
145
  schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
145
146
  schemathesis/transports/responses.py,sha256=6-gvVcRK0Ho_lSydUysBNFWoJwZEiEgf6Iv-GWkQGd8,1675
146
- schemathesis-3.30.0.dist-info/METADATA,sha256=i5DX3xGxuYwOKjMct9LrvjpORr7ZbyO9vU4yElLzzZ0,17669
147
- schemathesis-3.30.0.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
148
- schemathesis-3.30.0.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
149
- schemathesis-3.30.0.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
150
- schemathesis-3.30.0.dist-info/RECORD,,
147
+ schemathesis-3.30.1.dist-info/METADATA,sha256=zpOkWoX0pEVKQ1smDJgZMR5cg29onY26c5P8t4ukbnE,17671
148
+ schemathesis-3.30.1.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
149
+ schemathesis-3.30.1.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
150
+ schemathesis-3.30.1.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
151
+ schemathesis-3.30.1.dist-info/RECORD,,