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
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
1
2
  from contextlib import suppress
2
3
  from functools import lru_cache
3
- from typing import Any, Dict, Generator, List
4
+ from typing import Any, Generator
4
5
 
5
6
  import requests
6
7
  from hypothesis.strategies import SearchStrategy
@@ -11,7 +12,7 @@ from ._hypothesis import PARAMETERS, get_case_strategy
11
12
  from .constants import LOCATION_TO_CONTAINER
12
13
 
13
14
 
14
- def get_object_example_from_properties(object_schema: Dict[str, Any]) -> Dict[str, Any]:
15
+ def get_object_example_from_properties(object_schema: dict[str, Any]) -> dict[str, Any]:
15
16
  return {
16
17
  prop_name: prop["example"]
17
18
  for prop_name, prop in object_schema.get("properties", {}).items()
@@ -19,7 +20,7 @@ def get_object_example_from_properties(object_schema: Dict[str, Any]) -> Dict[st
19
20
  }
20
21
 
21
22
 
22
- @lru_cache()
23
+ @lru_cache
23
24
  def load_external_example(url: str) -> bytes:
24
25
  """Load examples the `externalValue` keyword."""
25
26
  response = requests.get(url, timeout=DEFAULT_RESPONSE_TIMEOUT / 1000)
@@ -27,7 +28,7 @@ def load_external_example(url: str) -> bytes:
27
28
  return response.content
28
29
 
29
30
 
30
- def get_examples(examples: Dict[str, Any]) -> Generator[Any, None, None]:
31
+ def get_examples(examples: dict[str, Any]) -> Generator[Any, None, None]:
31
32
  for example in examples.values():
32
33
  # IDEA: report when it is not a dictionary
33
34
  if isinstance(example, dict):
@@ -39,7 +40,7 @@ def get_examples(examples: Dict[str, Any]) -> Generator[Any, None, None]:
39
40
  yield load_external_example(example["externalValue"])
40
41
 
41
42
 
42
- def get_parameter_examples(operation_definition: Dict[str, Any], examples_field: str) -> List[Dict[str, Any]]:
43
+ def get_parameter_examples(operation_definition: dict[str, Any], examples_field: str) -> list[dict[str, Any]]:
43
44
  """Gets parameter examples from OAS3 `examples` keyword or `x-examples` for Swagger 2."""
44
45
  return [
45
46
  {
@@ -52,8 +53,8 @@ def get_parameter_examples(operation_definition: Dict[str, Any], examples_field:
52
53
  ]
53
54
 
54
55
 
55
- def get_parameter_example_from_properties(operation_definition: Dict[str, Any]) -> Dict[str, Any]:
56
- static_parameters: Dict[str, Any] = {}
56
+ def get_parameter_example_from_properties(operation_definition: dict[str, Any]) -> dict[str, Any]:
57
+ static_parameters: dict[str, Any] = {}
57
58
  for parameter in operation_definition.get("parameters", []):
58
59
  parameter_schema = parameter["schema"] if "schema" in parameter else parameter
59
60
  example = get_object_example_from_properties(parameter_schema)
@@ -69,7 +70,7 @@ def get_parameter_example_from_properties(operation_definition: Dict[str, Any])
69
70
  return static_parameters
70
71
 
71
72
 
72
- def get_request_body_examples(operation_definition: Dict[str, Any], examples_field: str) -> Dict[str, Any]:
73
+ def get_request_body_examples(operation_definition: dict[str, Any], examples_field: str) -> dict[str, Any]:
73
74
  """Gets request body examples from OAS3 `examples` keyword or `x-examples` for Swagger 2."""
74
75
  # NOTE. `requestBody` is OAS3-specific. How should it work with OAS2?
75
76
  request_bodies_items = operation_definition.get("requestBody", {}).get("content", {}).items()
@@ -84,8 +85,8 @@ def get_request_body_examples(operation_definition: Dict[str, Any], examples_fie
84
85
  }
85
86
 
86
87
 
87
- def get_request_body_example_from_properties(operation_definition: Dict[str, Any]) -> Dict[str, Any]:
88
- static_parameters: Dict[str, Any] = {}
88
+ def get_request_body_example_from_properties(operation_definition: dict[str, Any]) -> dict[str, Any]:
89
+ static_parameters: dict[str, Any] = {}
89
90
  request_bodies_items = operation_definition.get("requestBody", {}).get("content", {}).items()
90
91
  if request_bodies_items:
91
92
  _, request_body_schema = next(iter(request_bodies_items))
@@ -96,7 +97,7 @@ def get_request_body_example_from_properties(operation_definition: Dict[str, Any
96
97
  return static_parameters
97
98
 
98
99
 
99
- def get_static_parameters_from_example(operation: APIOperation) -> Dict[str, Any]:
100
+ def get_static_parameters_from_example(operation: APIOperation) -> dict[str, Any]:
100
101
  static_parameters = {}
101
102
  for name in PARAMETERS:
102
103
  parameters = getattr(operation, name)
@@ -106,7 +107,7 @@ def get_static_parameters_from_example(operation: APIOperation) -> Dict[str, Any
106
107
  return static_parameters
107
108
 
108
109
 
109
- def get_static_parameters_from_examples(operation: APIOperation, examples_field: str) -> List[Dict[str, Any]]:
110
+ def get_static_parameters_from_examples(operation: APIOperation, examples_field: str) -> list[dict[str, Any]]:
110
111
  """Get static parameters from OpenAPI examples keyword."""
111
112
  operation_definition = operation.definition.resolved
112
113
  return merge_examples(
@@ -115,7 +116,7 @@ def get_static_parameters_from_examples(operation: APIOperation, examples_field:
115
116
  )
116
117
 
117
118
 
118
- def get_static_parameters_from_properties(operation: APIOperation) -> Dict[str, Any]:
119
+ def get_static_parameters_from_properties(operation: APIOperation) -> dict[str, Any]:
119
120
  operation_definition = operation.definition.resolved
120
121
  return {
121
122
  **get_parameter_example_from_properties(operation_definition),
@@ -125,7 +126,7 @@ def get_static_parameters_from_properties(operation: APIOperation) -> Dict[str,
125
126
 
126
127
  def get_strategies_from_examples(
127
128
  operation: APIOperation, examples_field: str = "examples"
128
- ) -> List[SearchStrategy[Case]]:
129
+ ) -> list[SearchStrategy[Case]]:
129
130
  maps = {}
130
131
  for location, container in LOCATION_TO_CONTAINER.items():
131
132
  serializer = operation.get_parameter_serializer(location)
@@ -155,12 +156,12 @@ def get_strategies_from_examples(
155
156
 
156
157
 
157
158
  def merge_examples(
158
- parameter_examples: List[Dict[str, Any]], request_body_examples: Dict[str, Any]
159
- ) -> List[Dict[str, Any]]:
159
+ parameter_examples: list[dict[str, Any]], request_body_examples: dict[str, Any]
160
+ ) -> list[dict[str, Any]]:
160
161
  """Create list of static parameter objects from the parameter and request body examples."""
161
162
  static_parameter_list = []
162
163
  for idx in range(num_examples(parameter_examples, request_body_examples)):
163
- static_parameters: Dict[str, Any] = {}
164
+ static_parameters: dict[str, Any] = {}
164
165
  for parameter in parameter_examples:
165
166
  container = static_parameters.setdefault(parameter["type"], {})
166
167
  container[parameter["name"]] = parameter["examples"][min(idx, len(parameter["examples"]) - 1)]
@@ -172,18 +173,18 @@ def merge_examples(
172
173
  return static_parameter_list
173
174
 
174
175
 
175
- def static_parameters_union(sp_1: Dict[str, Any], sp_2: Dict[str, Any]) -> List[Dict[str, Any]]:
176
+ def static_parameters_union(sp_1: dict[str, Any], sp_2: dict[str, Any]) -> list[dict[str, Any]]:
176
177
  """Fill missing parameters in each static parameter dict with parameters provided in the other dict."""
177
178
  full_static_parameters = (_static_parameters_union(sp_1, sp_2), _static_parameters_union(sp_2, sp_1))
178
179
  return [static_parameter for static_parameter in full_static_parameters if static_parameter]
179
180
 
180
181
 
181
- def _static_parameters_union(base_obj: Dict[str, Any], fill_obj: Dict[str, Any]) -> Dict[str, Any]:
182
+ def _static_parameters_union(base_obj: dict[str, Any], fill_obj: dict[str, Any]) -> dict[str, Any]:
182
183
  """Fill base_obj with parameter examples in fill_obj that were not in base_obj."""
183
184
  if not base_obj:
184
185
  return {}
185
186
 
186
- full_static_parameters: Dict[str, Any] = {**base_obj}
187
+ full_static_parameters: dict[str, Any] = {**base_obj}
187
188
 
188
189
  for parameter_type, examples in fill_obj.items():
189
190
  if parameter_type not in full_static_parameters:
@@ -197,7 +198,7 @@ def _static_parameters_union(base_obj: Dict[str, Any], fill_obj: Dict[str, Any])
197
198
  return full_static_parameters
198
199
 
199
200
 
200
- def num_examples(parameter_examples: List[Dict[str, Any]], request_body_examples: Dict[str, Any]) -> int:
201
+ def num_examples(parameter_examples: list[dict[str, Any]], request_body_examples: dict[str, Any]) -> int:
201
202
  max_parameter_examples = (
202
203
  max(len(parameter["examples"]) for parameter in parameter_examples) if parameter_examples else 0
203
204
  )
@@ -1,7 +1,8 @@
1
1
  """Expression nodes description and evaluation logic."""
2
+ from __future__ import annotations
2
3
  from dataclasses import dataclass
3
4
  from enum import Enum, unique
4
- from typing import Any, Dict, Optional, Union
5
+ from typing import Any
5
6
 
6
7
  from requests.structures import CaseInsensitiveDict
7
8
 
@@ -74,7 +75,7 @@ class NonBodyRequest(Node):
74
75
  parameter: str
75
76
 
76
77
  def evaluate(self, context: ExpressionContext) -> str:
77
- container: Union[Dict, CaseInsensitiveDict] = {
78
+ container: dict | CaseInsensitiveDict = {
78
79
  "query": context.case.query,
79
80
  "path": context.case.path_parameters,
80
81
  "header": context.case.headers,
@@ -88,7 +89,7 @@ class NonBodyRequest(Node):
88
89
  class BodyRequest(Node):
89
90
  """A node for `$request` expressions where location is `body`."""
90
91
 
91
- pointer: Optional[str] = None
92
+ pointer: str | None = None
92
93
 
93
94
  def evaluate(self, context: ExpressionContext) -> Any:
94
95
  document = context.case.body
@@ -111,7 +112,7 @@ class HeaderResponse(Node):
111
112
  class BodyResponse(Node):
112
113
  """A node for `$response.body` expressions."""
113
114
 
114
- pointer: Optional[str] = None
115
+ pointer: str | None = None
115
116
 
116
117
  def evaluate(self, context: ExpressionContext) -> Any:
117
118
  from ....transports.responses import WSGIResponse
@@ -1,19 +1,20 @@
1
+ from __future__ import annotations
1
2
  from functools import lru_cache
2
- from typing import Generator, List, Union
3
+ from typing import Generator
3
4
 
4
5
  from . import lexer, nodes
5
6
  from .errors import RuntimeExpressionError, UnknownToken
6
7
 
7
8
 
8
- @lru_cache()
9
- def parse(expr: str) -> List[nodes.Node]:
9
+ @lru_cache
10
+ def parse(expr: str) -> list[nodes.Node]:
10
11
  """Parse lexical tokens into concrete expression nodes."""
11
12
  return list(_parse(expr))
12
13
 
13
14
 
14
15
  def _parse(expr: str) -> Generator[nodes.Node, None, None]:
15
16
  tokens = lexer.tokenize(expr)
16
- brackets_stack: List[str] = []
17
+ brackets_stack: list[str] = []
17
18
  for token in tokens:
18
19
  if token.is_string or token.is_dot:
19
20
  yield nodes.String(token.value)
@@ -46,7 +47,7 @@ def _parse_variable(tokens: lexer.TokenGenerator, token: lexer.Token, expr: str)
46
47
  raise UnknownToken(token.value)
47
48
 
48
49
 
49
- def _parse_request(tokens: lexer.TokenGenerator, expr: str) -> Union[nodes.BodyRequest, nodes.NonBodyRequest]:
50
+ def _parse_request(tokens: lexer.TokenGenerator, expr: str) -> nodes.BodyRequest | nodes.NonBodyRequest:
50
51
  skip_dot(tokens, "$request")
51
52
  location = next(tokens)
52
53
  if location.value in ("query", "path", "header"):
@@ -63,7 +64,7 @@ def _parse_request(tokens: lexer.TokenGenerator, expr: str) -> Union[nodes.BodyR
63
64
  raise RuntimeExpressionError(f"Invalid expression: {expr}")
64
65
 
65
66
 
66
- def _parse_response(tokens: lexer.TokenGenerator, expr: str) -> Union[nodes.HeaderResponse, nodes.BodyResponse]:
67
+ def _parse_response(tokens: lexer.TokenGenerator, expr: str) -> nodes.HeaderResponse | nodes.BodyResponse:
67
68
  skip_dot(tokens, "$response")
68
69
  location = next(tokens)
69
70
  if location.value == "header":
@@ -1,23 +1,23 @@
1
+ from __future__ import annotations
1
2
  import re
2
- from typing import List, Optional, Union, Set, Tuple
3
3
 
4
4
  from ...types import Filter
5
5
 
6
6
 
7
- def should_skip_method(method: str, pattern: Optional[Filter]) -> bool:
7
+ def should_skip_method(method: str, pattern: Filter | None) -> bool:
8
8
  if pattern is None:
9
9
  return False
10
10
  patterns = _ensure_tuple(pattern)
11
11
  return method.upper() not in map(str.upper, patterns)
12
12
 
13
13
 
14
- def should_skip_endpoint(endpoint: str, pattern: Optional[Filter]) -> bool:
14
+ def should_skip_endpoint(endpoint: str, pattern: Filter | None) -> bool:
15
15
  if pattern is None:
16
16
  return False
17
17
  return not _match_any_pattern(endpoint, pattern)
18
18
 
19
19
 
20
- def should_skip_by_tag(tags: Optional[List[str]], pattern: Optional[Filter]) -> bool:
20
+ def should_skip_by_tag(tags: list[str] | None, pattern: Filter | None) -> bool:
21
21
  if pattern is None:
22
22
  return False
23
23
  if not tags:
@@ -26,7 +26,7 @@ def should_skip_by_tag(tags: Optional[List[str]], pattern: Optional[Filter]) ->
26
26
  return not any(re.search(item, tag) for item in patterns for tag in tags)
27
27
 
28
28
 
29
- def should_skip_by_operation_id(operation_id: Optional[str], pattern: Optional[Filter]) -> bool:
29
+ def should_skip_by_operation_id(operation_id: str | None, pattern: Filter | None) -> bool:
30
30
  if pattern is None:
31
31
  return False
32
32
  if not operation_id:
@@ -43,7 +43,7 @@ def _match_any_pattern(target: str, pattern: Filter) -> bool:
43
43
  return any(re.search(item, target) for item in patterns)
44
44
 
45
45
 
46
- def _ensure_tuple(item: Filter) -> Union[List, Set, Tuple]:
46
+ def _ensure_tuple(item: Filter) -> list | set | tuple:
47
47
  if not isinstance(item, (list, set, tuple)):
48
48
  return (item,)
49
49
  return item
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Dict
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from hypothesis import strategies as st
7
7
 
8
8
 
9
- STRING_FORMATS: Dict[str, st.SearchStrategy] = {}
9
+ STRING_FORMATS: dict[str, st.SearchStrategy] = {}
10
10
 
11
11
 
12
12
  def register_string_format(name: str, strategy: st.SearchStrategy) -> None:
@@ -5,7 +5,7 @@ Based on https://swagger.io/docs/specification/links/
5
5
  from __future__ import annotations
6
6
  from dataclasses import dataclass, field
7
7
  from difflib import get_close_matches
8
- from typing import Any, Dict, Generator, List, NoReturn, Optional, Sequence, Tuple, Union, TYPE_CHECKING
8
+ from typing import Any, Generator, NoReturn, Sequence, Union, TYPE_CHECKING
9
9
 
10
10
  from ...models import APIOperation, Case
11
11
  from ...parameters import ParameterSet
@@ -27,7 +27,7 @@ if TYPE_CHECKING:
27
27
  @dataclass(repr=False)
28
28
  class Link(StatefulTest):
29
29
  operation: APIOperation
30
- parameters: Dict[str, Any]
30
+ parameters: dict[str, Any]
31
31
  request_body: Any = NOT_SET
32
32
 
33
33
  def __post_init__(self) -> None:
@@ -38,9 +38,7 @@ class Link(StatefulTest):
38
38
  )
39
39
 
40
40
  @classmethod
41
- def from_definition(
42
- cls, name: str, definition: Dict[str, Dict[str, Any]], source_operation: APIOperation
43
- ) -> "Link":
41
+ def from_definition(cls, name: str, definition: dict[str, dict[str, Any]], source_operation: APIOperation) -> Link:
44
42
  # Links can be behind a reference
45
43
  _, definition = source_operation.schema.resolver.resolve_in_scope( # type: ignore
46
44
  definition, source_operation.definition.scope
@@ -73,7 +71,7 @@ class Link(StatefulTest):
73
71
  body=expressions.evaluate(self.request_body, context),
74
72
  )
75
73
 
76
- def make_operation(self, collected: List[ParsedData]) -> APIOperation:
74
+ def make_operation(self, collected: list[ParsedData]) -> APIOperation:
77
75
  """Create a modified version of the original API operation with additional data merged in."""
78
76
  # We split the gathered data among all locations & store the original parameter
79
77
  containers = {
@@ -91,7 +89,7 @@ class Link(StatefulTest):
91
89
  if "body" in containers["body"] and item.body is not NOT_SET:
92
90
  containers["body"]["body"]["options"].append(item.body)
93
91
  # These are the final `path_parameters`, `query`, and other API operation components
94
- components: Dict[str, ParameterSet] = {
92
+ components: dict[str, ParameterSet] = {
95
93
  container_name: getattr(self.operation, container_name).__class__()
96
94
  for location, container_name in LOCATION_TO_CONTAINER.items()
97
95
  }
@@ -123,9 +121,9 @@ class Link(StatefulTest):
123
121
  components[LOCATION_TO_CONTAINER[location]].add(parameter)
124
122
  return self.operation.clone(**components)
125
123
 
126
- def _get_container_by_parameter_name(self, full_name: str, templates: Dict[str, Dict[str, Dict[str, Any]]]) -> List:
124
+ def _get_container_by_parameter_name(self, full_name: str, templates: dict[str, dict[str, dict[str, Any]]]) -> list:
127
125
  """Detect in what request part the parameters is defined."""
128
- location: Optional[str]
126
+ location: str | None
129
127
  try:
130
128
  # The parameter name is prefixed with its location. Example: `path.id`
131
129
  location, name = full_name.split(".")
@@ -174,10 +172,10 @@ class OpenAPILink(Direction):
174
172
 
175
173
  name: str
176
174
  status_code: str
177
- definition: Dict[str, Any]
175
+ definition: dict[str, Any]
178
176
  operation: APIOperation
179
- parameters: List[Tuple[Optional[str], str, str]] = field(init=False)
180
- body: Union[Dict[str, Any], NotSet] = field(init=False)
177
+ parameters: list[tuple[str | None, str, str]] = field(init=False)
178
+ body: dict[str, Any] | NotSet = field(init=False)
181
179
 
182
180
  def __post_init__(self) -> None:
183
181
  self.parameters = [
@@ -218,7 +216,7 @@ class OpenAPILink(Direction):
218
216
  return self.operation.schema.get_operation_by_reference(self.definition["operationRef"]) # type: ignore
219
217
 
220
218
 
221
- def get_container(case: Case, location: Optional[str], name: str) -> Optional[Dict[str, Any]]:
219
+ def get_container(case: Case, location: str | None, name: str) -> dict[str, Any] | None:
222
220
  """Get a container that suppose to store the given parameter."""
223
221
  if location:
224
222
  container_name = LOCATION_TO_CONTAINER[location]
@@ -232,7 +230,7 @@ def get_container(case: Case, location: Optional[str], name: str) -> Optional[Di
232
230
  return getattr(case, container_name)
233
231
 
234
232
 
235
- def normalize_parameter(parameter: str, expression: str) -> Tuple[Optional[str], str, str]:
233
+ def normalize_parameter(parameter: str, expression: str) -> tuple[str | None, str, str]:
236
234
  """Normalize runtime expressions.
237
235
 
238
236
  Runtime expressions may have parameter names prefixed with their location - `path.id`.
@@ -247,7 +245,7 @@ def normalize_parameter(parameter: str, expression: str) -> Tuple[Optional[str],
247
245
  return None, parameter, expression
248
246
 
249
247
 
250
- def get_all_links(operation: APIOperation) -> Generator[Tuple[str, OpenAPILink], None, None]:
248
+ def get_all_links(operation: APIOperation) -> Generator[tuple[str, OpenAPILink], None, None]:
251
249
  for status_code, definition in operation.definition.resolved["responses"].items():
252
250
  for name, link_definition in definition.get(operation.schema.links_field, {}).items(): # type: ignore
253
251
  yield status_code, OpenAPILink(name, status_code, link_definition, operation)
@@ -256,7 +254,7 @@ def get_all_links(operation: APIOperation) -> Generator[Tuple[str, OpenAPILink],
256
254
  StatusCode = Union[str, int]
257
255
 
258
256
 
259
- def _get_response_by_status_code(responses: Dict[StatusCode, Dict[str, Any]], status_code: Union[str, int]) -> Dict:
257
+ def _get_response_by_status_code(responses: dict[StatusCode, dict[str, Any]], status_code: str | int) -> dict:
260
258
  if isinstance(status_code, int):
261
259
  # Invalid schemas may contain status codes as integers
262
260
  if status_code in responses:
@@ -275,17 +273,17 @@ def _get_response_by_status_code(responses: Dict[StatusCode, Dict[str, Any]], st
275
273
 
276
274
 
277
275
  def add_link(
278
- responses: Dict[StatusCode, Dict[str, Any]],
276
+ responses: dict[StatusCode, dict[str, Any]],
279
277
  links_field: str,
280
- parameters: Optional[Dict[str, str]],
278
+ parameters: dict[str, str] | None,
281
279
  request_body: Any,
282
280
  status_code: StatusCode,
283
- target: Union[str, APIOperation],
284
- name: Optional[str] = None,
281
+ target: str | APIOperation,
282
+ name: str | None = None,
285
283
  ) -> None:
286
284
  response = _get_response_by_status_code(responses, status_code)
287
285
  links_definition = response.setdefault(links_field, {})
288
- new_link: Dict[str, Union[str, Dict[str, str]]] = {}
286
+ new_link: dict[str, str | dict[str, str]] = {}
289
287
  if parameters is not None:
290
288
  new_link["parameters"] = parameters
291
289
  if request_body is not None: