schemathesis 4.3.4__py3-none-any.whl → 4.3.6__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.

Potentially problematic release.


This version of schemathesis might be problematic. Click here for more details.

@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  from dataclasses import dataclass, field
4
5
  from typing import TYPE_CHECKING, Callable, Generator
5
6
 
@@ -121,10 +122,32 @@ class Statistic:
121
122
  # We need a response to get there, so it should be present
122
123
  assert response is not None
123
124
 
125
+ history = None
126
+
127
+ if (
128
+ transition.request_body is not None
129
+ and isinstance(transition.request_body.value, Ok)
130
+ and transition.request_body.value.ok() is UNRESOLVABLE
131
+ ):
132
+ history = collect_history(parent, response)
133
+ extraction_failures.add(
134
+ ExtractionFailure(
135
+ id=transition.id,
136
+ case_id=case_id,
137
+ source=parent.value.operation.label,
138
+ target=case.value.operation.label,
139
+ parameter_name="body",
140
+ expression=json.dumps(transition.request_body.definition),
141
+ history=history,
142
+ response=response,
143
+ error=None,
144
+ )
145
+ )
146
+
124
147
  for params in transition.parameters.values():
125
148
  for parameter, extracted in params.items():
126
149
  if isinstance(extracted.value, Ok) and extracted.value.ok() is UNRESOLVABLE:
127
- history = collect_history(parent, response)
150
+ history = history or collect_history(parent, response)
128
151
  extraction_failures.add(
129
152
  ExtractionFailure(
130
153
  id=transition.id,
@@ -139,7 +162,7 @@ class Statistic:
139
162
  )
140
163
  )
141
164
  elif isinstance(extracted.value, Err):
142
- history = collect_history(parent, response)
165
+ history = history or collect_history(parent, response)
143
166
  extraction_failures.add(
144
167
  ExtractionFailure(
145
168
  id=transition.id,
@@ -1295,9 +1295,10 @@ class OutputHandler(EventHandler):
1295
1295
  else:
1296
1296
  click.echo(f"\n{indent}{failure.error.__class__.__name__}: {failure.error}")
1297
1297
  else:
1298
- description = (
1299
- f"\n{indent}Could not resolve parameter `{failure.parameter_name}` via `{failure.expression}`"
1300
- )
1298
+ if failure.parameter_name == "body":
1299
+ description = f"\n{indent}Could not resolve request body via {failure.expression}"
1300
+ else:
1301
+ description = f"\n{indent}Could not resolve parameter `{failure.parameter_name}` via `{failure.expression}`"
1301
1302
  prefix = "$response.body"
1302
1303
  if failure.expression.startswith(prefix):
1303
1304
  description += f"\n{indent}Path `{failure.expression[len(prefix) :]}` not found in response"
@@ -41,6 +41,7 @@ class Bundler:
41
41
  return schema
42
42
 
43
43
  # Track visited URIs and their local definition names
44
+ inlining_for_recursion: set[str] = set()
44
45
  visited: set[str] = set()
45
46
  uri_to_def_name: dict[str, str] = {}
46
47
  defs = {}
@@ -90,19 +91,26 @@ class Bundler:
90
91
  # L is the number of levels. Even quadratic growth can be unacceptable for large schemas.
91
92
  #
92
93
  # In the future, it **should** be handled by `hypothesis-jsonschema` instead.
93
- cloned = deepclone(resolved_schema)
94
- remaining_references = sanitize(cloned)
95
- if reference in remaining_references:
96
- # This schema is either infinitely recursive or the sanitization logic misses it
94
+ if resolved_uri in inlining_for_recursion:
95
+ # Check if we're already trying to inline this schema
96
+ # If yes, it means we have an unbreakable cycle
97
97
  cycle = scopes[scopes.index(resolved_uri) :]
98
98
  raise InfiniteRecursiveReference(reference, cycle)
99
99
 
100
- result = {key: _bundle_recursive(value) for key, value in current.items() if key != "$ref"}
101
- # Recursive references need `$ref` to be in them, which is only possible with `dict`
102
- bundled_clone = _bundle_recursive(cloned)
103
- assert isinstance(bundled_clone, dict)
104
- result.update(bundled_clone)
105
- return result
100
+ # Track that we're inlining this schema
101
+ inlining_for_recursion.add(resolved_uri)
102
+ try:
103
+ cloned = deepclone(resolved_schema)
104
+ # Sanitize to remove optional recursive references
105
+ sanitize(cloned)
106
+
107
+ result = {key: _bundle_recursive(value) for key, value in current.items() if key != "$ref"}
108
+ bundled_clone = _bundle_recursive(cloned)
109
+ assert isinstance(bundled_clone, dict)
110
+ result.update(bundled_clone)
111
+ return result
112
+ finally:
113
+ inlining_for_recursion.discard(resolved_uri)
106
114
  elif resolved_uri not in visited:
107
115
  # Bundle only new schemas
108
116
  visit(resolved_uri)
@@ -530,7 +530,6 @@ def _iter_coverage_cases(
530
530
  instant = Instant()
531
531
  responses = list(operation.responses.iter_examples())
532
532
  # NOTE: The HEAD method is excluded
533
- unexpected_methods = unexpected_methods or {"get", "put", "post", "delete", "options", "patch", "trace"}
534
533
  custom_formats = _build_custom_formats(generation_config)
535
534
 
536
535
  seen_negative = coverage.HashSet()
@@ -56,7 +56,7 @@ StrategyFactory = Callable[
56
56
 
57
57
  @st.composite # type: ignore
58
58
  def openapi_cases(
59
- draw: Callable,
59
+ draw: st.DrawFn,
60
60
  *,
61
61
  operation: APIOperation,
62
62
  hooks: HookDispatcher | None = None,
@@ -116,7 +116,7 @@ def openapi_cases(
116
116
  else:
117
117
  candidates = operation.body.items
118
118
  parameter = draw(st.sampled_from(candidates))
119
- strategy = _get_body_strategy(parameter, strategy_factory, operation, generation_config)
119
+ strategy = _get_body_strategy(parameter, strategy_factory, operation, generation_config, draw)
120
120
  strategy = apply_hooks(operation, ctx, hooks, strategy, ParameterLocation.BODY)
121
121
  # Parameter may have a wildcard media type. In this case, choose any supported one
122
122
  possible_media_types = sorted(
@@ -200,11 +200,15 @@ def openapi_cases(
200
200
  return instance
201
201
 
202
202
 
203
+ OPTIONAL_BODY_RATE = 0.05
204
+
205
+
203
206
  def _get_body_strategy(
204
207
  parameter: OpenApiBody,
205
208
  strategy_factory: StrategyFactory,
206
209
  operation: APIOperation,
207
210
  generation_config: GenerationConfig,
211
+ draw: st.DrawFn,
208
212
  ) -> st.SearchStrategy:
209
213
  from schemathesis.specs.openapi.schemas import BaseOpenAPISchema
210
214
 
@@ -220,7 +224,12 @@ def _get_body_strategy(
220
224
  generation_config,
221
225
  operation.schema.adapter.jsonschema_validator_cls,
222
226
  )
223
- if not parameter.is_required:
227
+ # It is likely will be rejected, hence choose it rarely
228
+ if (
229
+ not parameter.is_required
230
+ and draw(st.floats(min_value=0.0, max_value=1.0, allow_infinity=False, allow_nan=False, allow_subnormal=False))
231
+ < OPTIONAL_BODY_RATE
232
+ ):
224
233
  strategy |= st.just(NOT_SET)
225
234
  return strategy
226
235
 
@@ -228,7 +237,7 @@ def _get_body_strategy(
228
237
  def get_parameters_value(
229
238
  value: dict[str, Any] | None,
230
239
  location: ParameterLocation,
231
- draw: Callable,
240
+ draw: st.DrawFn,
232
241
  operation: APIOperation,
233
242
  ctx: HookContext,
234
243
  hooks: HookDispatcher | None,
@@ -279,7 +288,7 @@ def generate_parameter(
279
288
  location: ParameterLocation,
280
289
  explicit: dict[str, Any] | None,
281
290
  operation: APIOperation,
282
- draw: Callable,
291
+ draw: st.DrawFn,
283
292
  ctx: HookContext,
284
293
  hooks: HookDispatcher | None,
285
294
  generator: GenerationMode,
@@ -7,6 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
+ from schemathesis.core import NOT_SET
10
11
  from schemathesis.core.compat import RefResolutionError
11
12
  from schemathesis.core.result import Ok
12
13
  from schemathesis.specs.openapi.stateful.dependencies.inputs import extract_inputs, update_input_field_bindings
@@ -107,9 +108,10 @@ def inject_links(schema: BaseOpenAPISchema) -> int:
107
108
  for link_name, definition in response_links.links.items():
108
109
  inferred_link = definition.to_openapi()
109
110
 
110
- # Check if duplicate exists
111
+ # Check if duplicate / subsets exists
111
112
  if normalized_existing:
112
- if _normalize_link(inferred_link, schema) in normalized_existing:
113
+ normalized = _normalize_link(inferred_link, schema)
114
+ if any(_is_subset_link(normalized, existing) for existing in normalized_existing):
113
115
  continue
114
116
 
115
117
  # Find unique name if collision exists
@@ -170,3 +172,39 @@ def _resolve_link_name_collision(proposed_name: str, existing_links: dict[str, A
170
172
  if candidate not in existing_links:
171
173
  return candidate
172
174
  suffix += 1
175
+
176
+
177
+ def _is_subset_link(inferred: NormalizedLink, existing: NormalizedLink) -> bool:
178
+ """Check if inferred link is a subset of existing link."""
179
+ # Must target the same operation
180
+ if inferred.path != existing.path or inferred.method != existing.method:
181
+ return False
182
+
183
+ # Inferred parameters must be subset of existing parameters
184
+ if not inferred.parameters.issubset(existing.parameters):
185
+ return False
186
+
187
+ # Inferred request body must be subset of existing body
188
+ return _is_request_body_subset(inferred.request_body, existing.request_body)
189
+
190
+
191
+ def _is_request_body_subset(inferred_body: Any, existing_body: Any) -> bool:
192
+ """Check if inferred body is a subset of existing body."""
193
+ # Empty inferred body is always a subset
194
+ if not inferred_body:
195
+ return True
196
+
197
+ # If existing is empty but inferred isn't, not a subset
198
+ if not existing_body:
199
+ return False
200
+
201
+ # Both must be dicts for subset comparison, otherwise check for equality
202
+ if not isinstance(inferred_body, dict) or not isinstance(existing_body, dict):
203
+ return inferred_body == existing_body
204
+
205
+ # Check if all inferred fields exist in existing with same values
206
+ for key, value in inferred_body.items():
207
+ if existing_body.get(key, NOT_SET) != value:
208
+ return False
209
+
210
+ return True
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Iterator
5
5
  from schemathesis.core import media_types
6
6
  from schemathesis.core.errors import MalformedMediaType
7
7
  from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY
8
+ from schemathesis.core.jsonschema.types import get_type
8
9
  from schemathesis.core.parameters import ParameterLocation
9
10
  from schemathesis.specs.openapi.stateful.dependencies import naming
10
11
  from schemathesis.specs.openapi.stateful.dependencies.models import (
@@ -36,6 +37,7 @@ def extract_inputs(
36
37
  Connects each parameter (e.g., `userId`) to its resource definition (`User`),
37
38
  creating placeholder resources if not yet discovered from their schemas.
38
39
  """
40
+ known_dependencies = set()
39
41
  for param in operation.path_parameters:
40
42
  input_slot = _resolve_parameter_dependency(
41
43
  parameter_name=param.name,
@@ -47,12 +49,16 @@ def extract_inputs(
47
49
  canonicalization_cache=canonicalization_cache,
48
50
  )
49
51
  if input_slot is not None:
52
+ if input_slot.resource.source >= DefinitionSource.SCHEMA_WITH_PROPERTIES:
53
+ known_dependencies.add(input_slot.resource.name)
50
54
  yield input_slot
51
55
 
52
56
  for body in operation.body:
53
57
  try:
54
58
  if media_types.is_json(body.media_type):
55
- yield from _resolve_body_dependencies(body=body, operation=operation, resources=resources)
59
+ yield from _resolve_body_dependencies(
60
+ body=body, operation=operation, resources=resources, known_dependencies=known_dependencies
61
+ )
56
62
  except MalformedMediaType:
57
63
  continue
58
64
 
@@ -162,8 +168,23 @@ def _find_resource_in_responses(
162
168
  return None
163
169
 
164
170
 
171
+ GENERIC_FIELD_NAMES = frozenset(
172
+ {
173
+ "body",
174
+ "text",
175
+ "content",
176
+ "message",
177
+ "description",
178
+ }
179
+ )
180
+
181
+
165
182
  def _resolve_body_dependencies(
166
- *, body: OpenApiBody, operation: APIOperation, resources: ResourceMap
183
+ *,
184
+ body: OpenApiBody,
185
+ operation: APIOperation,
186
+ resources: ResourceMap,
187
+ known_dependencies: set[str],
167
188
  ) -> Iterator[InputSlot]:
168
189
  schema = body.raw_schema
169
190
  if not isinstance(schema, dict):
@@ -178,31 +199,57 @@ def _resolve_body_dependencies(
178
199
 
179
200
  # Inspect each property that could be a part of some other resource
180
201
  properties = resolved.get("properties", {})
202
+ required = resolved.get("required", [])
181
203
  path = operation.path
182
- for property_name in properties:
204
+ for property_name, subschema in properties.items():
183
205
  resource_name = naming.from_parameter(property_name, path)
184
- if resource_name is None:
185
- continue
186
- resource = resources.get(resource_name)
187
- if resource is None:
188
- resource = ResourceDefinition.inferred_from_parameter(
189
- name=resource_name,
190
- parameter_name=property_name,
191
- )
192
- resources[resource_name] = resource
193
- field = property_name
194
- else:
195
- field = (
196
- naming.find_matching_field(
197
- parameter=property_name,
198
- resource=resource_name,
199
- fields=resource.fields,
206
+ if resource_name is not None:
207
+ resource = resources.get(resource_name)
208
+ if resource is None:
209
+ resource = ResourceDefinition.inferred_from_parameter(
210
+ name=resource_name,
211
+ parameter_name=property_name,
212
+ )
213
+ resources[resource_name] = resource
214
+ field = property_name
215
+ else:
216
+ field = (
217
+ naming.find_matching_field(
218
+ parameter=property_name,
219
+ resource=resource_name,
220
+ fields=resource.fields,
221
+ )
222
+ or "id"
200
223
  )
201
- or "id"
224
+ yield InputSlot(
225
+ resource=resource,
226
+ resource_field=field,
227
+ parameter_name=property_name,
228
+ parameter_location=ParameterLocation.BODY,
202
229
  )
230
+ continue
231
+
232
+ # Skip generic property names & optional fields (at least for now)
233
+ if property_name in GENERIC_FIELD_NAMES or property_name not in required:
234
+ continue
235
+
236
+ # Find candidate resources among known dependencies that actually have this field
237
+ candidates = [
238
+ resources[dep] for dep in known_dependencies if dep in resources and property_name in resources[dep].fields
239
+ ]
240
+
241
+ # Skip ambiguous cases when multiple resources have same field name
242
+ if len(candidates) != 1:
243
+ continue
244
+
245
+ resource = candidates[0]
246
+ # Ensure the target field supports the same type
247
+ if not resource.types[property_name] & set(get_type(subschema)):
248
+ continue
249
+
203
250
  yield InputSlot(
204
251
  resource=resource,
205
- resource_field=field,
252
+ resource_field=property_name,
206
253
  parameter_name=property_name,
207
254
  parameter_location=ParameterLocation.BODY,
208
255
  )
@@ -73,6 +73,11 @@ class DependencyGraph:
73
73
  parameters = {
74
74
  f"{input_slot.parameter_location.value}.{input_slot.parameter_name}": f"$response.body#{body_pointer}",
75
75
  }
76
+ existing = links.get(link_name)
77
+ if existing is not None:
78
+ existing.parameters.update(parameters)
79
+ existing.request_body.update(request_body)
80
+ continue
76
81
  links[link_name] = LinkDefinition(
77
82
  operation_ref=f"#/paths/{consumer_path}/{consumer.method}",
78
83
  parameters=parameters,
@@ -267,18 +272,20 @@ class ResourceDefinition:
267
272
  name: str
268
273
  # A sorted list of resource fields
269
274
  fields: list[str]
275
+ # Field types mapping
276
+ types: dict[str, set[str]]
270
277
  # How this resource was created
271
278
  source: DefinitionSource
272
279
 
273
- __slots__ = ("name", "fields", "source")
280
+ __slots__ = ("name", "fields", "types", "source")
274
281
 
275
282
  @classmethod
276
283
  def without_properties(cls, name: str) -> ResourceDefinition:
277
- return cls(name=name, fields=[], source=DefinitionSource.SCHEMA_WITHOUT_PROPERTIES)
284
+ return cls(name=name, fields=[], types={}, source=DefinitionSource.SCHEMA_WITHOUT_PROPERTIES)
278
285
 
279
286
  @classmethod
280
287
  def inferred_from_parameter(cls, name: str, parameter_name: str) -> ResourceDefinition:
281
- return cls(name=name, fields=[parameter_name], source=DefinitionSource.PARAMETER_INFERENCE)
288
+ return cls(name=name, fields=[parameter_name], types={}, source=DefinitionSource.PARAMETER_INFERENCE)
282
289
 
283
290
 
284
291
  class DefinitionSource(enum.IntEnum):
@@ -4,26 +4,45 @@ from __future__ import annotations
4
4
  def from_parameter(parameter: str, path: str) -> str | None:
5
5
  # TODO: support other naming patterns
6
6
  # Named like "userId" -> look for "User" resource
7
- if parameter.endswith("Id"):
7
+ if parameter.endswith("Id") and len(parameter) > 2:
8
8
  return to_pascal_case(parameter[:-2])
9
9
  # Named like "user_id" -> look for "User" resource
10
10
  elif parameter.endswith("_id"):
11
11
  return to_pascal_case(parameter[:-3])
12
12
  # Just "id" -> infer from path context
13
13
  elif parameter == "id":
14
- return from_path(path)
14
+ return from_path(path, parameter_name=parameter)
15
15
  return None
16
16
 
17
17
 
18
- def from_path(path: str) -> str | None:
19
- segments = [s for s in path.split("/") if s and "{" not in s]
18
+ def from_path(path: str, parameter_name: str | None = None) -> str | None:
19
+ """Detect resource name from OpenAPI path."""
20
+ segments = [s for s in path.split("/") if s]
20
21
 
21
22
  if not segments:
22
23
  # API Root
23
24
  return None
24
25
 
25
- singular = to_singular(segments[-1])
26
- return to_pascal_case(singular)
26
+ # If parameter name provided, find the resource it refers to
27
+ if parameter_name:
28
+ placeholder = f"{{{parameter_name}}}"
29
+ try:
30
+ param_index = segments.index(placeholder)
31
+ if param_index > 0:
32
+ resource_segment = segments[param_index - 1]
33
+ if "{" not in resource_segment:
34
+ singular = to_singular(resource_segment)
35
+ return to_pascal_case(singular)
36
+ except ValueError:
37
+ pass # Parameter not found in path
38
+
39
+ # Fallback to last non-parameter segment
40
+ non_param_segments = [s for s in segments if "{" not in s]
41
+ if non_param_segments:
42
+ singular = to_singular(non_param_segments[-1])
43
+ return to_pascal_case(singular)
44
+
45
+ return None
27
46
 
28
47
 
29
48
  IRREGULAR_TO_PLURAL = {
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Iterator, Mapping, cast
5
5
 
6
6
  from schemathesis.core.errors import InfiniteRecursiveReference
7
7
  from schemathesis.core.jsonschema.bundler import BundleError
8
+ from schemathesis.core.jsonschema.types import get_type
8
9
  from schemathesis.specs.openapi.adapter.parameters import resource_name_from_ref
9
10
  from schemathesis.specs.openapi.adapter.references import maybe_resolve
10
11
  from schemathesis.specs.openapi.stateful.dependencies import naming
@@ -265,18 +266,21 @@ def _extract_resource_from_schema(
265
266
 
266
267
  properties = resolved.get("properties")
267
268
  if properties:
268
- fields = list(properties)
269
+ fields = sorted(properties)
270
+ types = {field: set(get_type(subschema)) for field, subschema in properties.items()}
269
271
  source = DefinitionSource.SCHEMA_WITH_PROPERTIES
270
272
  else:
271
273
  fields = []
274
+ types = {}
272
275
  source = DefinitionSource.SCHEMA_WITHOUT_PROPERTIES
273
276
  if resource is not None:
274
277
  if resource.source < source:
275
278
  resource.source = source
276
279
  resource.fields = fields
280
+ resource.types = types
277
281
  updated_resources.add(resource_name)
278
282
  else:
279
- resource = ResourceDefinition(name=resource_name, fields=fields, source=source)
283
+ resource = ResourceDefinition(name=resource_name, fields=fields, types=types, source=source)
280
284
  resources[resource_name] = resource
281
285
 
282
286
  return resource
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.3.4
3
+ Version: 4.3.6
4
4
  Summary: Property-based testing framework for Open API and GraphQL based apps
5
5
  Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
6
6
  Project-URL: Changelog, https://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
@@ -13,7 +13,7 @@ schemathesis/cli/core.py,sha256=ue7YUdVo3YvuzGL4s6i62NL6YqNDeVPBSnQ1znrvG2w,480
13
13
  schemathesis/cli/commands/__init__.py,sha256=DNzKEnXu7GjGSVe0244ZErmygUBA3nGSyVY6JP3ixD0,3740
14
14
  schemathesis/cli/commands/data.py,sha256=_ALywjIeCZjuaoDQFy-Kj8RZkEGqXd-Y95O47h8Jszs,171
15
15
  schemathesis/cli/commands/run/__init__.py,sha256=_ApiSVh9q-TsJQ_-IiVBNnLCtTCDMTnOLwuJhOvbCp4,18925
16
- schemathesis/cli/commands/run/context.py,sha256=Usa89aSPf8Uv-2m-nWr0ghvTKM1ZZehALBI0m_lFHv4,8087
16
+ schemathesis/cli/commands/run/context.py,sha256=vej33l5yOhlJ5gLXDwat9WCW_XdhrHNc9pdIQQYddoY,9004
17
17
  schemathesis/cli/commands/run/events.py,sha256=ew0TQOc9T2YBZynYWv95k9yfAk8-hGuZDLMxjT8EhvY,1595
18
18
  schemathesis/cli/commands/run/executor.py,sha256=_koznTX0DoELPN_1mxr9K_Qg7-9MPXWdld1MFn3YG_Y,5329
19
19
  schemathesis/cli/commands/run/filters.py,sha256=pzkNRcf5vLPSsMfnvt711GNzRSBK5iZIFjPA0fiH1N4,1701
@@ -23,7 +23,7 @@ schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31M
23
23
  schemathesis/cli/commands/run/handlers/base.py,sha256=qUtDvtr3F6were_BznfnaPpMibGJMnQ5CA9aEzcIUBc,1306
24
24
  schemathesis/cli/commands/run/handlers/cassettes.py,sha256=LzvQp--Ub5MXF7etet7fQD0Ufloh1R0j2X1o9dT8Z4k,19253
25
25
  schemathesis/cli/commands/run/handlers/junitxml.py,sha256=qiFvM4-SlM67sep003SkLqPslzaEb4nOm3bkzw-DO-Q,2602
26
- schemathesis/cli/commands/run/handlers/output.py,sha256=jWrqEkEQPO2kgzxOffZacqxH6r7dkDmAx0ep9GA3NU8,64020
26
+ schemathesis/cli/commands/run/handlers/output.py,sha256=pPp5-lJP3Zir1sTA7fmlhc-u1Jn17enXZNUerQMr56M,64166
27
27
  schemathesis/cli/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  schemathesis/cli/ext/fs.py,sha256=dHQYBjQozQmuSSfXVp-2KWFK0ESOb_w-lV2SptfMfco,461
29
29
  schemathesis/cli/ext/groups.py,sha256=kQ37t6qeArcKaY2y5VxyK3_KwAkBKCVm58IYV8gewds,2720
@@ -69,7 +69,7 @@ schemathesis/core/transport.py,sha256=LQcamAkFqJ0HuXQzepevAq2MCJW-uq5Nm-HE9yc7HM
69
69
  schemathesis/core/validation.py,sha256=b0USkKzkWvdz3jOW1JXYc_TfYshfKZeP7xAUnMqcNoc,2303
70
70
  schemathesis/core/version.py,sha256=dOBUWrY3-uA2NQXJp9z7EtZgkR6jYeLg8sMhQCL1mcI,205
71
71
  schemathesis/core/jsonschema/__init__.py,sha256=gBZGsXIpK2EFfcp8x0b69dqzWAm2OeZHepKImkkLvoE,320
72
- schemathesis/core/jsonschema/bundler.py,sha256=wK-UhI49TbfnNjr_riQZ28s005d4c-s05WcXLGQFlCs,7861
72
+ schemathesis/core/jsonschema/bundler.py,sha256=rHaNAVgBn0XvAk3t9dHsyym1xK4FyBW7zR1GLejPD0A,8204
73
73
  schemathesis/core/jsonschema/keywords.py,sha256=pjseXTfH9OItNs_Qq6ubkhNWQOrxTnwHmrP_jxrHeJU,631
74
74
  schemathesis/core/jsonschema/references.py,sha256=c2Q4IKWUbwENNtkbFaqf8r3LLZu6GFE5YLnYQlg5tPg,6069
75
75
  schemathesis/core/jsonschema/types.py,sha256=C7f9g8yKFuoxC5_0YNIh8QAyGU0-tj8pzTMfMDjjjVM,1248
@@ -99,7 +99,7 @@ schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz
99
99
  schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
100
100
  schemathesis/generation/overrides.py,sha256=xI2djHsa42fzP32xpxgxO52INixKagf5DjDAWJYswM8,3890
101
101
  schemathesis/generation/hypothesis/__init__.py,sha256=68BHULoXQC1WjFfw03ga5lvDGZ-c-J7H_fNEuUzFWRw,4976
102
- schemathesis/generation/hypothesis/builder.py,sha256=tnDN_0MNT48mAO421JmcI3E0U2zeeTX-mjF3KXmpc7A,38629
102
+ schemathesis/generation/hypothesis/builder.py,sha256=j7R_X9Z_50xjwIl_Z8DVBe6P1r8leVNvQME7mRLXM1A,38520
103
103
  schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
104
104
  schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
105
105
  schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
@@ -129,7 +129,7 @@ schemathesis/specs/graphql/scalars.py,sha256=6lew8mnwhrtg23leiEbG43mLGPLlRln8mCl
129
129
  schemathesis/specs/graphql/schemas.py,sha256=GKJcnTAT1wUzzUr3r6wiTfiAdFLcgFQjYRRz7x4VQl0,14457
130
130
  schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
131
131
  schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
132
- schemathesis/specs/openapi/_hypothesis.py,sha256=g5476s_ArzheWKHHlOfKwx46tqoiehP3KaQM7L_AoEI,22359
132
+ schemathesis/specs/openapi/_hypothesis.py,sha256=O8vN-koBjzBVZfpD3pmgIt6ecU4ddAPHOxTAORd23Lo,22642
133
133
  schemathesis/specs/openapi/checks.py,sha256=YYV6j6idyw2ubY4sLp-avs2OVEkAWeIihjT0xiV1RRA,30669
134
134
  schemathesis/specs/openapi/converter.py,sha256=4a6-8STT5snF7B-t6IsOIGdK5rV16oNqsdvWL7VFf2M,6472
135
135
  schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
@@ -164,12 +164,12 @@ schemathesis/specs/openapi/stateful/__init__.py,sha256=CQx2WJ3mKn5qmYRc90DqsG9w3
164
164
  schemathesis/specs/openapi/stateful/control.py,sha256=QaXLSbwQWtai5lxvvVtQV3BLJ8n5ePqSKB00XFxp-MA,3695
165
165
  schemathesis/specs/openapi/stateful/inference.py,sha256=B99jSTDVi2yKxU7-raIb91xpacOrr0nZkEZY5Ej3eCY,9783
166
166
  schemathesis/specs/openapi/stateful/links.py,sha256=SSA66mU50FFBz7e6sA37CfL-Vt0OY3gont72oFSvZYU,8163
167
- schemathesis/specs/openapi/stateful/dependencies/__init__.py,sha256=IE8WIeWNhQoNCwiivQKoDe3GD_aobxmjQYvarwxp_1M,6379
168
- schemathesis/specs/openapi/stateful/dependencies/inputs.py,sha256=DJDDCq73OYvCIPMxLKXJGTQGloNf6z6mgxjzjD0kJHA,8739
169
- schemathesis/specs/openapi/stateful/dependencies/models.py,sha256=s8_RBwpciAmPMISp5WDabqEuX7dXW84S-QWnhkodz6g,10938
170
- schemathesis/specs/openapi/stateful/dependencies/naming.py,sha256=MGoyh1bfw2SoKzdbzpHxed9LHMjokPJTU_YErZaF-Ls,11396
167
+ schemathesis/specs/openapi/stateful/dependencies/__init__.py,sha256=0JM-FrY6Awv6gl-qDHaaK7pXbt_GKutBKPyIaph8apA,7842
168
+ schemathesis/specs/openapi/stateful/dependencies/inputs.py,sha256=1qVVIlzx52qsy55Pht9dYNtn2dewRSiHegfrBO1RD8c,10347
169
+ schemathesis/specs/openapi/stateful/dependencies/models.py,sha256=HxdVcVebjUFhSlSs_M8vDB-BnYfYwLGceIQAzytawrs,11324
170
+ schemathesis/specs/openapi/stateful/dependencies/naming.py,sha256=HfpkCB1GglX1BAKXer3llvPkQsk8wx0QZhZq7ANcdMM,12214
171
171
  schemathesis/specs/openapi/stateful/dependencies/outputs.py,sha256=zvVUfQWNIuhMkKDpz5hsVGkkvkefLt1EswpJAnHajOw,1186
172
- schemathesis/specs/openapi/stateful/dependencies/resources.py,sha256=7E2Z6LvomSRrp_0vCD_adzoux0wBLEjKi_EiSqiN43U,9664
172
+ schemathesis/specs/openapi/stateful/dependencies/resources.py,sha256=4bgXILFgC1_y9aU_4scaNw3lkJ6laW5MMkLYh3Ph4Hg,9894
173
173
  schemathesis/specs/openapi/stateful/dependencies/schemas.py,sha256=yMu13RsXIPDeZT1tATTxI1vkpYhjs-XFSFEvx3_Xh_Q,14094
174
174
  schemathesis/specs/openapi/types/__init__.py,sha256=VPsWtLJle__Kodw_QqtQ3OuvBzBcCIKsTOrXy3eA7OU,66
175
175
  schemathesis/specs/openapi/types/v3.py,sha256=Vondr9Amk6JKCIM6i6RGcmTUjFfPgOOqzBXqerccLpo,1468
@@ -179,8 +179,8 @@ schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEz
179
179
  schemathesis/transport/requests.py,sha256=wriRI9fprTplE_qEZLEz1TerX6GwkE3pwr6ZnU2o6vQ,10648
180
180
  schemathesis/transport/serialization.py,sha256=GwO6OAVTmL1JyKw7HiZ256tjV4CbrRbhQN0ep1uaZwI,11157
181
181
  schemathesis/transport/wsgi.py,sha256=kQtasFre6pjdJWRKwLA_Qb-RyQHCFNpaey9ubzlFWKI,5907
182
- schemathesis-4.3.4.dist-info/METADATA,sha256=Su_dAAs7RpHERfreFwrore0Nx6LqQJQB60HlC-C8TAQ,8540
183
- schemathesis-4.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
184
- schemathesis-4.3.4.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
185
- schemathesis-4.3.4.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
186
- schemathesis-4.3.4.dist-info/RECORD,,
182
+ schemathesis-4.3.6.dist-info/METADATA,sha256=fSBJbdXmS3wufMePS7qdtcEmUy4eMoiV28sqkp0db68,8540
183
+ schemathesis-4.3.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
184
+ schemathesis-4.3.6.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
185
+ schemathesis-4.3.6.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
186
+ schemathesis-4.3.6.dist-info/RECORD,,