schemathesis 3.21.2__py3-none-any.whl → 3.22.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.
Files changed (95) hide show
  1. schemathesis/__init__.py +1 -1
  2. schemathesis/_compat.py +2 -18
  3. schemathesis/_dependency_versions.py +1 -6
  4. schemathesis/_hypothesis.py +15 -12
  5. schemathesis/_lazy_import.py +3 -2
  6. schemathesis/_xml.py +12 -11
  7. schemathesis/auths.py +88 -81
  8. schemathesis/checks.py +4 -4
  9. schemathesis/cli/__init__.py +202 -171
  10. schemathesis/cli/callbacks.py +29 -32
  11. schemathesis/cli/cassettes.py +25 -25
  12. schemathesis/cli/context.py +18 -12
  13. schemathesis/cli/junitxml.py +2 -2
  14. schemathesis/cli/options.py +10 -11
  15. schemathesis/cli/output/default.py +64 -34
  16. schemathesis/code_samples.py +10 -10
  17. schemathesis/constants.py +1 -1
  18. schemathesis/contrib/unique_data.py +2 -2
  19. schemathesis/exceptions.py +55 -42
  20. schemathesis/extra/_aiohttp.py +2 -2
  21. schemathesis/extra/_flask.py +2 -2
  22. schemathesis/extra/_server.py +3 -2
  23. schemathesis/extra/pytest_plugin.py +10 -10
  24. schemathesis/failures.py +16 -16
  25. schemathesis/filters.py +40 -41
  26. schemathesis/fixups/__init__.py +4 -3
  27. schemathesis/fixups/fast_api.py +5 -4
  28. schemathesis/generation/__init__.py +16 -4
  29. schemathesis/hooks.py +25 -25
  30. schemathesis/internal/jsonschema.py +4 -3
  31. schemathesis/internal/transformation.py +3 -2
  32. schemathesis/lazy.py +39 -31
  33. schemathesis/loaders.py +8 -8
  34. schemathesis/models.py +128 -126
  35. schemathesis/parameters.py +6 -5
  36. schemathesis/runner/__init__.py +107 -81
  37. schemathesis/runner/events.py +37 -26
  38. schemathesis/runner/impl/core.py +86 -81
  39. schemathesis/runner/impl/solo.py +19 -15
  40. schemathesis/runner/impl/threadpool.py +40 -22
  41. schemathesis/runner/serialization.py +67 -40
  42. schemathesis/sanitization.py +18 -20
  43. schemathesis/schemas.py +83 -72
  44. schemathesis/serializers.py +39 -30
  45. schemathesis/service/ci.py +20 -21
  46. schemathesis/service/client.py +29 -9
  47. schemathesis/service/constants.py +1 -0
  48. schemathesis/service/events.py +2 -2
  49. schemathesis/service/hosts.py +8 -7
  50. schemathesis/service/metadata.py +5 -0
  51. schemathesis/service/models.py +22 -4
  52. schemathesis/service/report.py +15 -15
  53. schemathesis/service/serialization.py +23 -27
  54. schemathesis/service/usage.py +8 -7
  55. schemathesis/specs/graphql/loaders.py +31 -24
  56. schemathesis/specs/graphql/nodes.py +3 -2
  57. schemathesis/specs/graphql/scalars.py +26 -2
  58. schemathesis/specs/graphql/schemas.py +38 -34
  59. schemathesis/specs/openapi/_hypothesis.py +62 -44
  60. schemathesis/specs/openapi/checks.py +10 -10
  61. schemathesis/specs/openapi/converter.py +10 -9
  62. schemathesis/specs/openapi/definitions.py +2 -2
  63. schemathesis/specs/openapi/examples.py +22 -21
  64. schemathesis/specs/openapi/expressions/nodes.py +5 -4
  65. schemathesis/specs/openapi/expressions/parser.py +7 -6
  66. schemathesis/specs/openapi/filters.py +6 -6
  67. schemathesis/specs/openapi/formats.py +2 -2
  68. schemathesis/specs/openapi/links.py +19 -21
  69. schemathesis/specs/openapi/loaders.py +133 -78
  70. schemathesis/specs/openapi/negative/__init__.py +16 -11
  71. schemathesis/specs/openapi/negative/mutations.py +11 -10
  72. schemathesis/specs/openapi/parameters.py +20 -19
  73. schemathesis/specs/openapi/references.py +21 -20
  74. schemathesis/specs/openapi/schemas.py +97 -84
  75. schemathesis/specs/openapi/security.py +25 -24
  76. schemathesis/specs/openapi/serialization.py +20 -23
  77. schemathesis/specs/openapi/stateful/__init__.py +12 -11
  78. schemathesis/specs/openapi/stateful/links.py +7 -7
  79. schemathesis/specs/openapi/utils.py +4 -3
  80. schemathesis/specs/openapi/validation.py +3 -2
  81. schemathesis/stateful/__init__.py +15 -16
  82. schemathesis/stateful/state_machine.py +9 -9
  83. schemathesis/targets.py +3 -3
  84. schemathesis/throttling.py +2 -2
  85. schemathesis/transports/auth.py +2 -2
  86. schemathesis/transports/content_types.py +5 -0
  87. schemathesis/transports/headers.py +3 -2
  88. schemathesis/transports/responses.py +1 -1
  89. schemathesis/utils.py +7 -10
  90. {schemathesis-3.21.2.dist-info → schemathesis-3.22.1.dist-info}/METADATA +12 -13
  91. schemathesis-3.22.1.dist-info/RECORD +130 -0
  92. schemathesis-3.21.2.dist-info/RECORD +0 -130
  93. {schemathesis-3.21.2.dist-info → schemathesis-3.22.1.dist-info}/WHEEL +0 -0
  94. {schemathesis-3.21.2.dist-info → schemathesis-3.22.1.dist-info}/entry_points.txt +0 -0
  95. {schemathesis-3.21.2.dist-info → schemathesis-3.22.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,8 +2,9 @@
2
2
 
3
3
  These are basic entities that describe what data could be sent to the API.
4
4
  """
5
+ from __future__ import annotations
5
6
  from dataclasses import dataclass, field
6
- from typing import TYPE_CHECKING, Any, Dict, Generator, Generic, List, Optional, TypeVar
7
+ from typing import TYPE_CHECKING, Any, Generator, Generic, TypeVar
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from .models import APIOperation
@@ -43,7 +44,7 @@ class Parameter:
43
44
  """Parameter example."""
44
45
  raise NotImplementedError
45
46
 
46
- def serialize(self, operation: "APIOperation") -> str:
47
+ def serialize(self, operation: APIOperation) -> str:
47
48
  """Get parameter's string representation."""
48
49
  raise NotImplementedError
49
50
 
@@ -55,13 +56,13 @@ P = TypeVar("P", bound=Parameter)
55
56
  class ParameterSet(Generic[P]):
56
57
  """A set of parameters for the same location."""
57
58
 
58
- items: List[P] = field(default_factory=list)
59
+ items: list[P] = field(default_factory=list)
59
60
 
60
61
  def add(self, parameter: P) -> None:
61
62
  """Add a new parameter."""
62
63
  self.items.append(parameter)
63
64
 
64
- def get(self, name: str) -> Optional[P]:
65
+ def get(self, name: str) -> P | None:
65
66
  for parameter in self:
66
67
  if parameter.name == name:
67
68
  return parameter
@@ -71,7 +72,7 @@ class ParameterSet(Generic[P]):
71
72
  return self.get(name) is not None
72
73
 
73
74
  @property
74
- def example(self) -> Dict[str, Any]:
75
+ def example(self) -> dict[str, Any]:
75
76
  """Composite example gathered from individual parameters."""
76
77
  return {item.name: item.example for item in self.items if item.example}
77
78
 
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union, TYPE_CHECKING
2
+
3
+ from random import Random
4
+ from typing import Any, Callable, Generator, Iterable, TYPE_CHECKING
3
5
  from urllib.parse import urlparse
4
6
 
5
- from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod
7
+ from ..generation import DEFAULT_DATA_GENERATION_METHODS, DataGenerationMethod, GenerationConfig
6
8
  from ..constants import (
7
9
  DEFAULT_DEADLINE,
8
10
  DEFAULT_STATEFUL_RECURSION_LIMIT,
@@ -30,46 +32,47 @@ if TYPE_CHECKING:
30
32
 
31
33
  @deprecated_function(removed_in="4.0", replacement="schemathesis.runner.from_schema")
32
34
  def prepare(
33
- schema_uri: Union[str, Dict[str, Any]],
35
+ schema_uri: str | dict[str, Any],
34
36
  *,
35
37
  # Runtime behavior
36
- checks: Optional[Iterable[CheckFunction]] = None,
37
- data_generation_methods: Tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
38
- max_response_time: Optional[int] = None,
38
+ checks: Iterable[CheckFunction] | None = None,
39
+ data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
40
+ max_response_time: int | None = None,
39
41
  targets: Iterable[Target] = DEFAULT_TARGETS,
40
42
  workers_num: int = 1,
41
- seed: Optional[int] = None,
43
+ seed: int | None = None,
42
44
  exit_first: bool = False,
43
45
  dry_run: bool = False,
44
46
  store_interactions: bool = False,
45
- stateful: Optional[Stateful] = None,
47
+ stateful: Stateful | None = None,
46
48
  stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
47
49
  # Schema loading
48
50
  loader: Callable = oas_loaders.from_uri,
49
- base_url: Optional[str] = None,
50
- auth: Optional[Tuple[str, str]] = None,
51
- auth_type: Optional[str] = None,
52
- headers: Optional[Dict[str, str]] = None,
53
- request_timeout: Optional[int] = None,
54
- request_tls_verify: Union[bool, str] = True,
55
- request_cert: Optional[RequestCert] = None,
56
- endpoint: Optional[Filter] = None,
57
- method: Optional[Filter] = None,
58
- tag: Optional[Filter] = None,
59
- operation_id: Optional[Filter] = None,
60
- app: Optional[str] = None,
51
+ base_url: str | None = None,
52
+ auth: tuple[str, str] | None = None,
53
+ auth_type: str | None = None,
54
+ headers: dict[str, str] | None = None,
55
+ request_timeout: int | None = None,
56
+ request_tls_verify: bool | str = True,
57
+ request_cert: RequestCert | None = None,
58
+ endpoint: Filter | None = None,
59
+ method: Filter | None = None,
60
+ tag: Filter | None = None,
61
+ operation_id: Filter | None = None,
62
+ app: str | None = None,
61
63
  validate_schema: bool = True,
62
64
  skip_deprecated_operations: bool = False,
63
- force_schema_version: Optional[str] = None,
65
+ force_schema_version: str | None = None,
64
66
  count_operations: bool = True,
67
+ count_links: bool = True,
65
68
  # Hypothesis-specific configuration
66
- hypothesis_deadline: Optional[Union[int, NotSet]] = None,
67
- hypothesis_derandomize: Optional[bool] = None,
68
- hypothesis_max_examples: Optional[int] = None,
69
- hypothesis_phases: Optional[List[hypothesis.Phase]] = None,
70
- hypothesis_report_multiple_bugs: Optional[bool] = None,
71
- hypothesis_suppress_health_check: Optional[List[hypothesis.HealthCheck]] = None,
72
- hypothesis_verbosity: Optional[hypothesis.Verbosity] = None,
69
+ hypothesis_deadline: int | NotSet | None = None,
70
+ hypothesis_derandomize: bool | None = None,
71
+ hypothesis_max_examples: int | None = None,
72
+ hypothesis_phases: list[hypothesis.Phase] | None = None,
73
+ hypothesis_report_multiple_bugs: bool | None = None,
74
+ hypothesis_suppress_health_check: list[hypothesis.HealthCheck] | None = None,
75
+ hypothesis_verbosity: hypothesis.Verbosity | None = None,
73
76
  ) -> Generator[events.ExecutionEvent, None, None]:
74
77
  """Prepare a generator that will run test cases against the given API definition."""
75
78
  from ..checks import DEFAULT_CHECKS
@@ -121,10 +124,11 @@ def prepare(
121
124
  stateful=stateful,
122
125
  stateful_recursion_limit=stateful_recursion_limit,
123
126
  count_operations=count_operations,
127
+ count_links=count_links,
124
128
  )
125
129
 
126
130
 
127
- def validate_loader(loader: Callable, schema_uri: Union[str, Dict[str, Any]]) -> None:
131
+ def validate_loader(loader: Callable, schema_uri: str | dict[str, Any]) -> None:
128
132
  """Sanity checking for input schema & loader."""
129
133
  if loader not in (
130
134
  oas_loaders.from_uri,
@@ -149,36 +153,37 @@ def validate_loader(loader: Callable, schema_uri: Union[str, Dict[str, Any]]) ->
149
153
 
150
154
  def execute_from_schema(
151
155
  *,
152
- schema_uri: Union[str, Dict[str, Any]],
156
+ schema_uri: str | dict[str, Any],
153
157
  loader: Callable = oas_loaders.from_uri,
154
- base_url: Optional[str] = None,
155
- endpoint: Optional[Filter] = None,
156
- method: Optional[Filter] = None,
157
- tag: Optional[Filter] = None,
158
- operation_id: Optional[Filter] = None,
159
- app: Optional[str] = None,
158
+ base_url: str | None = None,
159
+ endpoint: Filter | None = None,
160
+ method: Filter | None = None,
161
+ tag: Filter | None = None,
162
+ operation_id: Filter | None = None,
163
+ app: str | None = None,
160
164
  validate_schema: bool = True,
161
165
  skip_deprecated_operations: bool = False,
162
- force_schema_version: Optional[str] = None,
166
+ force_schema_version: str | None = None,
163
167
  checks: Iterable[CheckFunction],
164
- data_generation_methods: Tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
165
- max_response_time: Optional[int] = None,
168
+ data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
169
+ max_response_time: int | None = None,
166
170
  targets: Iterable[Target],
167
171
  workers_num: int = 1,
168
172
  hypothesis_settings: hypothesis.settings,
169
- auth: Optional[RawAuth] = None,
170
- auth_type: Optional[str] = None,
171
- headers: Optional[Dict[str, Any]] = None,
172
- request_timeout: Optional[int] = None,
173
- request_tls_verify: Union[bool, str] = True,
174
- request_cert: Optional[RequestCert] = None,
175
- seed: Optional[int] = None,
173
+ auth: RawAuth | None = None,
174
+ auth_type: str | None = None,
175
+ headers: dict[str, Any] | None = None,
176
+ request_timeout: int | None = None,
177
+ request_tls_verify: bool | str = True,
178
+ request_cert: RequestCert | None = None,
179
+ seed: int | None = None,
176
180
  exit_first: bool = False,
177
181
  dry_run: bool = False,
178
182
  store_interactions: bool = False,
179
- stateful: Optional[Stateful] = None,
183
+ stateful: Stateful | None = None,
180
184
  stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
181
185
  count_operations: bool = True,
186
+ count_links: bool = True,
182
187
  ) -> Generator[events.ExecutionEvent, None, None]:
183
188
  """Execute tests for the given schema.
184
189
 
@@ -226,6 +231,7 @@ def execute_from_schema(
226
231
  stateful=stateful,
227
232
  stateful_recursion_limit=stateful_recursion_limit,
228
233
  count_operations=count_operations,
234
+ count_links=count_links,
229
235
  ).execute()
230
236
  except SchemaError as error:
231
237
  yield events.InternalError.from_schema_error(error)
@@ -234,26 +240,26 @@ def execute_from_schema(
234
240
 
235
241
 
236
242
  def load_schema(
237
- schema_uri: Union[str, Dict[str, Any]],
243
+ schema_uri: str | dict[str, Any],
238
244
  *,
239
- base_url: Optional[str] = None,
245
+ base_url: str | None = None,
240
246
  loader: Callable = oas_loaders.from_uri,
241
247
  app: Any = None,
242
248
  validate_schema: bool = True,
243
249
  skip_deprecated_operations: bool = False,
244
- data_generation_methods: Tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
245
- force_schema_version: Optional[str] = None,
246
- request_tls_verify: Union[bool, str] = True,
247
- request_cert: Optional[RequestCert] = None,
250
+ data_generation_methods: tuple[DataGenerationMethod, ...] = DEFAULT_DATA_GENERATION_METHODS,
251
+ force_schema_version: str | None = None,
252
+ request_tls_verify: bool | str = True,
253
+ request_cert: RequestCert | None = None,
248
254
  # Network request parameters
249
- auth: Optional[Tuple[str, str]] = None,
250
- auth_type: Optional[str] = None,
251
- headers: Optional[Dict[str, str]] = None,
255
+ auth: tuple[str, str] | None = None,
256
+ auth_type: str | None = None,
257
+ headers: dict[str, str] | None = None,
252
258
  # Schema filters
253
- endpoint: Optional[Filter] = None,
254
- method: Optional[Filter] = None,
255
- tag: Optional[Filter] = None,
256
- operation_id: Optional[Filter] = None,
259
+ endpoint: Filter | None = None,
260
+ method: Filter | None = None,
261
+ tag: Filter | None = None,
262
+ operation_id: Filter | None = None,
257
263
  ) -> BaseSchema:
258
264
  """Load schema via specified loader and parameters."""
259
265
  loader_options = {
@@ -306,26 +312,28 @@ def load_schema(
306
312
  def from_schema(
307
313
  schema: BaseSchema,
308
314
  *,
309
- checks: Optional[Iterable[CheckFunction]] = None,
310
- max_response_time: Optional[int] = None,
315
+ checks: Iterable[CheckFunction] | None = None,
316
+ max_response_time: int | None = None,
311
317
  targets: Iterable[Target] = DEFAULT_TARGETS,
312
318
  workers_num: int = 1,
313
- hypothesis_settings: Optional[hypothesis.settings] = None,
314
- auth: Optional[RawAuth] = None,
315
- auth_type: Optional[str] = None,
316
- headers: Optional[Dict[str, Any]] = None,
317
- request_timeout: Optional[int] = None,
318
- request_tls_verify: Union[bool, str] = True,
319
- request_cert: Optional[RequestCert] = None,
320
- seed: Optional[int] = None,
319
+ hypothesis_settings: hypothesis.settings | None = None,
320
+ generation_config: GenerationConfig | None = None,
321
+ auth: RawAuth | None = None,
322
+ auth_type: str | None = None,
323
+ headers: dict[str, Any] | None = None,
324
+ request_timeout: int | None = None,
325
+ request_tls_verify: bool | str = True,
326
+ request_cert: RequestCert | None = None,
327
+ seed: int | None = None,
321
328
  exit_first: bool = False,
322
- max_failures: Optional[int] = None,
323
- started_at: Optional[str] = None,
329
+ max_failures: int | None = None,
330
+ started_at: str | None = None,
324
331
  dry_run: bool = False,
325
332
  store_interactions: bool = False,
326
- stateful: Optional[Stateful] = None,
333
+ stateful: Stateful | None = None,
327
334
  stateful_recursion_limit: int = DEFAULT_STATEFUL_RECURSION_LIMIT,
328
335
  count_operations: bool = True,
336
+ count_links: bool = True,
329
337
  ) -> BaseRunner:
330
338
  from starlette.applications import Starlette
331
339
  import hypothesis
@@ -342,6 +350,12 @@ def from_schema(
342
350
  checks = checks or DEFAULT_CHECKS
343
351
 
344
352
  hypothesis_settings = hypothesis_settings or hypothesis.settings(deadline=DEFAULT_DEADLINE)
353
+ generation_config = generation_config or GenerationConfig()
354
+
355
+ # Use the same seed for all tests unless `derandomize=True` is used
356
+ if seed is None and not hypothesis_settings.derandomize:
357
+ seed = Random().getrandbits(128)
358
+
345
359
  started_at = started_at or current_datetime()
346
360
  if workers_num > 1:
347
361
  if not schema.app:
@@ -351,6 +365,7 @@ def from_schema(
351
365
  max_response_time=max_response_time,
352
366
  targets=targets,
353
367
  hypothesis_settings=hypothesis_settings,
368
+ generation_config=generation_config,
354
369
  auth=auth,
355
370
  auth_type=auth_type,
356
371
  headers=headers,
@@ -367,6 +382,7 @@ def from_schema(
367
382
  stateful=stateful,
368
383
  stateful_recursion_limit=stateful_recursion_limit,
369
384
  count_operations=count_operations,
385
+ count_links=count_links,
370
386
  )
371
387
  if isinstance(schema.app, Starlette):
372
388
  return ThreadPoolASGIRunner(
@@ -375,6 +391,7 @@ def from_schema(
375
391
  max_response_time=max_response_time,
376
392
  targets=targets,
377
393
  hypothesis_settings=hypothesis_settings,
394
+ generation_config=generation_config,
378
395
  auth=auth,
379
396
  auth_type=auth_type,
380
397
  headers=headers,
@@ -387,6 +404,7 @@ def from_schema(
387
404
  stateful=stateful,
388
405
  stateful_recursion_limit=stateful_recursion_limit,
389
406
  count_operations=count_operations,
407
+ count_links=count_links,
390
408
  )
391
409
  return ThreadPoolWSGIRunner(
392
410
  schema=schema,
@@ -394,6 +412,7 @@ def from_schema(
394
412
  max_response_time=max_response_time,
395
413
  targets=targets,
396
414
  hypothesis_settings=hypothesis_settings,
415
+ generation_config=generation_config,
397
416
  auth=auth,
398
417
  auth_type=auth_type,
399
418
  headers=headers,
@@ -407,6 +426,7 @@ def from_schema(
407
426
  stateful=stateful,
408
427
  stateful_recursion_limit=stateful_recursion_limit,
409
428
  count_operations=count_operations,
429
+ count_links=count_links,
410
430
  )
411
431
  if not schema.app:
412
432
  return SingleThreadRunner(
@@ -415,6 +435,7 @@ def from_schema(
415
435
  max_response_time=max_response_time,
416
436
  targets=targets,
417
437
  hypothesis_settings=hypothesis_settings,
438
+ generation_config=generation_config,
418
439
  auth=auth,
419
440
  auth_type=auth_type,
420
441
  headers=headers,
@@ -430,6 +451,7 @@ def from_schema(
430
451
  stateful=stateful,
431
452
  stateful_recursion_limit=stateful_recursion_limit,
432
453
  count_operations=count_operations,
454
+ count_links=count_links,
433
455
  )
434
456
  if isinstance(schema.app, Starlette):
435
457
  return SingleThreadASGIRunner(
@@ -438,6 +460,7 @@ def from_schema(
438
460
  max_response_time=max_response_time,
439
461
  targets=targets,
440
462
  hypothesis_settings=hypothesis_settings,
463
+ generation_config=generation_config,
441
464
  auth=auth,
442
465
  auth_type=auth_type,
443
466
  headers=headers,
@@ -450,6 +473,7 @@ def from_schema(
450
473
  stateful=stateful,
451
474
  stateful_recursion_limit=stateful_recursion_limit,
452
475
  count_operations=count_operations,
476
+ count_links=count_links,
453
477
  )
454
478
  return SingleThreadWSGIRunner(
455
479
  schema=schema,
@@ -457,6 +481,7 @@ def from_schema(
457
481
  max_response_time=max_response_time,
458
482
  targets=targets,
459
483
  hypothesis_settings=hypothesis_settings,
484
+ generation_config=generation_config,
460
485
  auth=auth,
461
486
  auth_type=auth_type,
462
487
  headers=headers,
@@ -469,18 +494,19 @@ def from_schema(
469
494
  stateful=stateful,
470
495
  stateful_recursion_limit=stateful_recursion_limit,
471
496
  count_operations=count_operations,
497
+ count_links=count_links,
472
498
  )
473
499
 
474
500
 
475
501
  def prepare_hypothesis_settings(
476
- database: Optional[str] = None,
477
- deadline: Optional[Union[int, NotSet]] = None,
478
- derandomize: Optional[bool] = None,
479
- max_examples: Optional[int] = None,
480
- phases: Optional[List[hypothesis.Phase]] = None,
481
- report_multiple_bugs: Optional[bool] = None,
482
- suppress_health_check: Optional[List[hypothesis.HealthCheck]] = None,
483
- verbosity: Optional[hypothesis.Verbosity] = None,
502
+ database: str | None = None,
503
+ deadline: int | NotSet | None = None,
504
+ derandomize: bool | None = None,
505
+ max_examples: int | None = None,
506
+ phases: list[hypothesis.Phase] | None = None,
507
+ report_multiple_bugs: bool | None = None,
508
+ suppress_health_check: list[hypothesis.HealthCheck] | None = None,
509
+ verbosity: hypothesis.Verbosity | None = None,
484
510
  ) -> hypothesis.settings:
485
511
  import hypothesis
486
512
  from hypothesis.database import DirectoryBasedExampleDatabase, InMemoryExampleDatabase
@@ -3,7 +3,7 @@ import enum
3
3
  import threading
4
4
  import time
5
5
  from dataclasses import asdict, dataclass, field
6
- from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
6
+ from typing import Any, TYPE_CHECKING
7
7
 
8
8
  from ..internal.datetime import current_datetime
9
9
  from ..generation import DataGenerationMethod
@@ -23,7 +23,7 @@ class ExecutionEvent:
23
23
  # Whether this event is expected to be the last one in the event stream
24
24
  is_terminal = False
25
25
 
26
- def asdict(self, **kwargs: Any) -> Dict[str, Any]:
26
+ def asdict(self, **kwargs: Any) -> dict[str, Any]:
27
27
  data = asdict(self, **kwargs)
28
28
  # An internal tag for simpler type identification
29
29
  data["event_type"] = self.__class__.__name__
@@ -34,11 +34,14 @@ class ExecutionEvent:
34
34
  class Initialized(ExecutionEvent):
35
35
  """Runner is initialized, settings are prepared, requests session is ready."""
36
36
 
37
- schema: Dict[str, Any]
37
+ schema: dict[str, Any]
38
38
  # Total number of operations in the schema
39
- operations_count: Optional[int]
39
+ operations_count: int | None
40
+ # Total number of links in the schema
41
+ links_count: int | None
40
42
  # The place, where the API schema is located
41
- location: Optional[str]
43
+ location: str | None
44
+ seed: int | None
42
45
  # The base URL against which the tests are running
43
46
  base_url: str
44
47
  # API schema specification name
@@ -52,16 +55,24 @@ class Initialized(ExecutionEvent):
52
55
 
53
56
  @classmethod
54
57
  def from_schema(
55
- cls, *, schema: BaseSchema, count_operations: bool = True, started_at: Optional[str] = None
56
- ) -> "Initialized":
58
+ cls,
59
+ *,
60
+ schema: BaseSchema,
61
+ count_operations: bool = True,
62
+ count_links: bool = True,
63
+ started_at: str | None = None,
64
+ seed: int | None,
65
+ ) -> Initialized:
57
66
  """Computes all needed data from a schema instance."""
58
67
  return cls(
59
68
  schema=schema.raw_schema,
60
69
  operations_count=schema.operations_count if count_operations else None,
70
+ links_count=schema.links_count if count_links else None,
61
71
  location=schema.location,
62
72
  base_url=schema.get_base_url(),
63
73
  started_at=started_at or current_datetime(),
64
74
  specification_name=schema.verbose_name,
75
+ seed=seed,
65
76
  )
66
77
 
67
78
 
@@ -92,7 +103,7 @@ class BeforeExecution(CurrentOperationMixin, ExecutionEvent):
92
103
  # The current level of recursion during stateful testing
93
104
  recursion_level: int
94
105
  # The way data will be generated
95
- data_generation_method: List[DataGenerationMethod]
106
+ data_generation_method: list[DataGenerationMethod]
96
107
  # A unique ID which connects events that happen during testing of the same API operation
97
108
  # It may be useful when multiple threads are involved where incoming events are not ordered
98
109
  correlation_id: str
@@ -103,9 +114,9 @@ class BeforeExecution(CurrentOperationMixin, ExecutionEvent):
103
114
  cls,
104
115
  operation: APIOperation,
105
116
  recursion_level: int,
106
- data_generation_method: List[DataGenerationMethod],
117
+ data_generation_method: list[DataGenerationMethod],
107
118
  correlation_id: str,
108
- ) -> "BeforeExecution":
119
+ ) -> BeforeExecution:
109
120
  return cls(
110
121
  method=operation.method.upper(),
111
122
  path=operation.full_path,
@@ -130,14 +141,14 @@ class AfterExecution(CurrentOperationMixin, ExecutionEvent):
130
141
  # APIOperation test status - success / failure / error
131
142
  status: Status
132
143
  # The way data was generated
133
- data_generation_method: List[DataGenerationMethod]
144
+ data_generation_method: list[DataGenerationMethod]
134
145
  result: SerializedTestResult
135
146
  # Test running time
136
147
  elapsed_time: float
137
148
  correlation_id: str
138
149
  thread_id: int = field(default_factory=threading.get_ident)
139
150
  # Captured hypothesis stdout
140
- hypothesis_output: List[str] = field(default_factory=list)
151
+ hypothesis_output: list[str] = field(default_factory=list)
141
152
 
142
153
  @classmethod
143
154
  def from_result(
@@ -145,11 +156,11 @@ class AfterExecution(CurrentOperationMixin, ExecutionEvent):
145
156
  result: TestResult,
146
157
  status: Status,
147
158
  elapsed_time: float,
148
- hypothesis_output: List[str],
159
+ hypothesis_output: list[str],
149
160
  operation: APIOperation,
150
- data_generation_method: List[DataGenerationMethod],
161
+ data_generation_method: list[DataGenerationMethod],
151
162
  correlation_id: str,
152
- ) -> "AfterExecution":
163
+ ) -> AfterExecution:
153
164
  return cls(
154
165
  method=operation.method.upper(),
155
166
  path=operation.full_path,
@@ -185,10 +196,10 @@ class InternalError(ExecutionEvent):
185
196
 
186
197
  # Main error info
187
198
  type: InternalErrorType
188
- subtype: Optional[SchemaErrorType]
199
+ subtype: SchemaErrorType | None
189
200
  title: str
190
201
  message: str
191
- extras: List[str]
202
+ extras: list[str]
192
203
 
193
204
  # Exception info
194
205
  exception_type: str
@@ -198,7 +209,7 @@ class InternalError(ExecutionEvent):
198
209
  thread_id: int = field(default_factory=threading.get_ident)
199
210
 
200
211
  @classmethod
201
- def from_schema_error(cls, error: SchemaError) -> "InternalError":
212
+ def from_schema_error(cls, error: SchemaError) -> InternalError:
202
213
  return cls.with_exception(
203
214
  error,
204
215
  type_=InternalErrorType.SCHEMA,
@@ -209,7 +220,7 @@ class InternalError(ExecutionEvent):
209
220
  )
210
221
 
211
222
  @classmethod
212
- def from_exc(cls, exc: Exception) -> "InternalError":
223
+ def from_exc(cls, exc: Exception) -> InternalError:
213
224
  return cls.with_exception(
214
225
  exc,
215
226
  type_=InternalErrorType.OTHER,
@@ -224,11 +235,11 @@ class InternalError(ExecutionEvent):
224
235
  cls,
225
236
  exc: Exception,
226
237
  type_: InternalErrorType,
227
- subtype: Optional[SchemaErrorType],
238
+ subtype: SchemaErrorType | None,
228
239
  title: str,
229
240
  message: str,
230
- extras: List[str],
231
- ) -> "InternalError":
241
+ extras: list[str],
242
+ ) -> InternalError:
232
243
  exception_type = f"{exc.__class__.__module__}.{exc.__class__.__qualname__}"
233
244
  exception = format_exception(exc)
234
245
  exception_with_traceback = format_exception(exc, include_traceback=True)
@@ -262,17 +273,17 @@ class Finished(ExecutionEvent):
262
273
  has_errors: bool
263
274
  has_logs: bool
264
275
  is_empty: bool
265
- generic_errors: List[SerializedError]
266
- warnings: List[str]
276
+ generic_errors: list[SerializedError]
277
+ warnings: list[str]
267
278
 
268
- total: Dict[str, Dict[Union[str, Status], int]]
279
+ total: dict[str, dict[str | Status, int]]
269
280
 
270
281
  # Total test run execution time
271
282
  running_time: float
272
283
  thread_id: int = field(default_factory=threading.get_ident)
273
284
 
274
285
  @classmethod
275
- def from_results(cls, results: TestResultSet, running_time: float) -> "Finished":
286
+ def from_results(cls, results: TestResultSet, running_time: float) -> Finished:
276
287
  return cls(
277
288
  passed_count=results.passed_count,
278
289
  skipped_count=results.skipped_count,