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

schemathesis/checks.py CHANGED
@@ -93,7 +93,7 @@ CHECKS = Registry[CheckFunction]()
93
93
 
94
94
  def load_all_checks() -> None:
95
95
  # NOTE: Trigger registering all Open API checks
96
- from schemathesis.specs.openapi.checks import status_code_conformance # noqa: F401, F403
96
+ from schemathesis.specs.openapi.checks import status_code_conformance # noqa: F401
97
97
 
98
98
 
99
99
  def check(func: CheckFunction) -> CheckFunction:
@@ -187,3 +187,14 @@ def run_checks(
187
187
  on_failure(name, collected, sub_failure)
188
188
 
189
189
  return collected
190
+
191
+
192
+ def __getattr__(name: str) -> Any:
193
+ try:
194
+ return CHECKS.get_one(name)
195
+ except KeyError:
196
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
197
+
198
+
199
+ def __dir__() -> list[str]:
200
+ return sorted(list(globals().keys()) + CHECKS.get_all_names())
@@ -123,8 +123,7 @@ def vcr_writer(output: TextOutput, config: ProjectConfig, queue: Queue) -> None:
123
123
  current_id = 1
124
124
 
125
125
  def write_header_values(stream: IO, values: list[str]) -> None:
126
- for v in values:
127
- stream.write(f" - {json.dumps(v)}\n")
126
+ stream.writelines(f" - {json.dumps(v)}\n" for v in values)
128
127
 
129
128
  if config.output.sanitization.enabled:
130
129
  sanitization_keys = config.output.sanitization.keys_to_sanitize
@@ -142,7 +142,7 @@ def _format_anyof_error(error: ValidationError) -> str:
142
142
  )
143
143
  elif list(error.schema_path) == ["properties", "workers", "anyOf"]:
144
144
  return (
145
- f"Invalid value for 'workers': {repr(error.instance)}\n\n"
145
+ f"Invalid value for 'workers': {error.instance!r}\n\n"
146
146
  f"Expected either:\n"
147
147
  f" - A positive integer (e.g., workers = 4)\n"
148
148
  f' - The string "auto" for automatic detection (workers = "auto")'
@@ -161,6 +161,8 @@ class InvalidSchema(SchemathesisError):
161
161
  message += "\n File reference could not be resolved. Check that the file exists."
162
162
  elif reference.startswith(("#/components", "#/definitions")):
163
163
  message += "\n Component does not exist in the schema."
164
+ elif isinstance(error.__cause__, RemoteDocumentError):
165
+ message += f"\n {error.__cause__}"
164
166
  return cls(message, path=path, method=method)
165
167
 
166
168
  def as_failing_test_function(self) -> Callable:
@@ -176,6 +178,13 @@ class InvalidSchema(SchemathesisError):
176
178
  return actual_test
177
179
 
178
180
 
181
+ class RemoteDocumentError(SchemathesisError):
182
+ """Remote reference resolution failed.
183
+
184
+ This exception carries more context than the default one in `jsonschema`.
185
+ """
186
+
187
+
179
188
  class HookError(SchemathesisError):
180
189
  """Happens during hooks loading."""
181
190
 
@@ -26,6 +26,9 @@ class Registry(Generic[T]):
26
26
  def get_all(self) -> list[T]:
27
27
  return list(self._items.values())
28
28
 
29
+ def get_one(self, name: str) -> T:
30
+ return self._items[name]
31
+
29
32
  def get_by_names(self, names: Sequence[str]) -> list[T]:
30
33
  """Get items by their names."""
31
34
  return [self._items[name] for name in names]
@@ -12,6 +12,8 @@ from dataclasses import dataclass
12
12
  from functools import cached_property
13
13
  from typing import TYPE_CHECKING, Callable, Iterator, Sequence, cast
14
14
 
15
+ from hypothesis import HealthCheck
16
+
15
17
  from schemathesis import errors
16
18
  from schemathesis.core.errors import (
17
19
  InfiniteRecursiveReference,
@@ -23,6 +25,7 @@ from schemathesis.core.errors import (
23
25
  get_request_error_message,
24
26
  split_traceback,
25
27
  )
28
+ from schemathesis.generation.hypothesis.reporting import HEALTH_CHECK_ACTIONS, HEALTH_CHECK_TITLES
26
29
 
27
30
  if TYPE_CHECKING:
28
31
  import hypothesis.errors
@@ -119,15 +122,6 @@ class EngineErrorInfo:
119
122
  scalar_name = scalar_name_from_error(self._error)
120
123
  return f"Scalar type '{scalar_name}' is not recognized"
121
124
 
122
- if self._kind == RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_DATA_TOO_LARGE:
123
- return HEALTH_CHECK_MESSAGE_DATA_TOO_LARGE
124
- if self._kind == RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_FILTER_TOO_MUCH:
125
- return HEALTH_CHECK_MESSAGE_FILTER_TOO_MUCH
126
- if self._kind == RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_TOO_SLOW:
127
- return HEALTH_CHECK_MESSAGE_TOO_SLOW
128
- if self._kind == RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_LARGE_BASE_EXAMPLE:
129
- return HEALTH_CHECK_MESSAGE_LARGE_BASE_EXAMPLE
130
-
131
125
  if self._kind in (
132
126
  RuntimeErrorKind.SCHEMA_INVALID_REGULAR_EXPRESSION,
133
127
  RuntimeErrorKind.SCHEMA_GENERIC,
@@ -216,16 +210,9 @@ def scalar_name_from_error(exception: hypothesis.errors.InvalidArgument) -> str:
216
210
 
217
211
 
218
212
  def extract_health_check_error(error: hypothesis.errors.FailedHealthCheck) -> hypothesis.HealthCheck | None:
219
- from hypothesis import HealthCheck
220
-
221
- match = re.search(r"add HealthCheck\.(\w+) to the suppress_health_check ", str(error))
222
- if match:
223
- return {
224
- "data_too_large": HealthCheck.data_too_large,
225
- "filter_too_much": HealthCheck.filter_too_much,
226
- "too_slow": HealthCheck.too_slow,
227
- "large_base_example": HealthCheck.large_base_example,
228
- }.get(match.group(1))
213
+ for key, title in HEALTH_CHECK_TITLES.items():
214
+ if title in str(error):
215
+ return key
229
216
  return None
230
217
 
231
218
 
@@ -233,7 +220,13 @@ def get_runtime_error_suggestion(error_type: RuntimeErrorKind, bold: Callable[[s
233
220
  """Get a user-friendly suggestion for handling the error."""
234
221
 
235
222
  def _format_health_check_suggestion(label: str) -> str:
236
- return f"Bypass this health check using {bold(f'`--suppress-health-check={label}`')}."
223
+ base = {
224
+ "data_too_large": HEALTH_CHECK_ACTIONS[HealthCheck.data_too_large],
225
+ "filter_too_much": HEALTH_CHECK_ACTIONS[HealthCheck.filter_too_much],
226
+ "too_slow": HEALTH_CHECK_ACTIONS[HealthCheck.too_slow],
227
+ "large_base_example": HEALTH_CHECK_ACTIONS[HealthCheck.large_base_example],
228
+ }[label]
229
+ return f"{base} or bypass this health check using {bold(f'`--suppress-health-check={label}`')}."
237
230
 
238
231
  return {
239
232
  RuntimeErrorKind.CONNECTION_SSL: f"Bypass SSL verification with {bold('`--tls-verify=false`')}.",
@@ -252,28 +245,6 @@ def get_runtime_error_suggestion(error_type: RuntimeErrorKind, bold: Callable[[s
252
245
  }.get(error_type)
253
246
 
254
247
 
255
- HEALTH_CHECK_MESSAGE_DATA_TOO_LARGE = """There's a notable occurrence of examples surpassing the maximum size limit.
256
- Typically, generating excessively large examples can compromise the quality of test outcomes.
257
-
258
- Consider revising the schema to more accurately represent typical use cases
259
- or applying constraints to reduce the data size."""
260
- HEALTH_CHECK_MESSAGE_FILTER_TOO_MUCH = """A significant number of generated examples are being filtered out, indicating
261
- that the schema's constraints may be too complex.
262
-
263
- This level of filtration can slow down testing and affect the distribution
264
- of generated data. Review and simplify the schema constraints where
265
- possible to mitigate this issue."""
266
- HEALTH_CHECK_MESSAGE_TOO_SLOW = "Data generation is extremely slow. Consider reducing the complexity of the schema."
267
- HEALTH_CHECK_MESSAGE_LARGE_BASE_EXAMPLE = """A health check has identified that the smallest example derived from the schema
268
- is excessively large, potentially leading to inefficient test execution.
269
-
270
- This is commonly due to schemas that specify large-scale data structures by
271
- default, such as an array with an extensive number of elements.
272
-
273
- Consider revising the schema to more accurately represent typical use cases
274
- or applying constraints to reduce the data size."""
275
-
276
-
277
248
  @enum.unique
278
249
  class RuntimeErrorKind(str, enum.Enum):
279
250
  """Classification of runtime errors."""
@@ -54,7 +54,11 @@ from schemathesis.generation.hypothesis.builder import (
54
54
  UnresolvableReferenceMark,
55
55
  UnsatisfiableExampleMark,
56
56
  )
57
- from schemathesis.generation.hypothesis.reporting import build_unsatisfiable_error, ignore_hypothesis_output
57
+ from schemathesis.generation.hypothesis.reporting import (
58
+ build_health_check_error,
59
+ build_unsatisfiable_error,
60
+ ignore_hypothesis_output,
61
+ )
58
62
 
59
63
  if TYPE_CHECKING:
60
64
  from schemathesis.schemas import APIOperation
@@ -162,6 +166,9 @@ def run_test(
162
166
  status = Status.FAILURE
163
167
  except BaseExceptionGroup:
164
168
  status = Status.ERROR
169
+ except hypothesis.errors.FailedHealthCheck as exc:
170
+ status = Status.ERROR
171
+ yield non_fatal_error(build_health_check_error(operation, exc, with_tip=False))
165
172
  except hypothesis.errors.Unsatisfiable:
166
173
  # We need more clear error message here
167
174
  status = Status.ERROR
@@ -211,7 +218,11 @@ def run_test(
211
218
  # `hypothesis-jsonschema` emits a warning on invalid regular expression syntax
212
219
  yield non_fatal_error(InvalidRegexPattern.from_hypothesis_jsonschema_message(message))
213
220
  else:
214
- yield non_fatal_error(exc)
221
+ health_check = build_health_check_error(operation, exc, with_tip=False)
222
+ if isinstance(health_check, hypothesis.errors.FailedHealthCheck):
223
+ yield non_fatal_error(health_check)
224
+ else:
225
+ yield non_fatal_error(exc)
215
226
  except hypothesis.errors.DeadlineExceeded as exc:
216
227
  status = Status.ERROR
217
228
  yield non_fatal_error(DeadlineExceeded.from_exc(exc))
@@ -28,17 +28,23 @@ def default_settings() -> settings:
28
28
  T = TypeVar("T")
29
29
 
30
30
 
31
- def generate_one(strategy: st.SearchStrategy[T]) -> T: # type: ignore[type-var]
31
+ def generate_one(strategy: st.SearchStrategy[T], suppress_health_check: list | None = None) -> T: # type: ignore[type-var]
32
32
  examples: list[T] = []
33
- add_single_example(strategy, examples)
33
+ add_single_example(strategy, examples, suppress_health_check)
34
34
  return examples[0]
35
35
 
36
36
 
37
- def add_single_example(strategy: st.SearchStrategy[T], examples: list[T]) -> None:
38
- from hypothesis import given, seed
37
+ def add_single_example(
38
+ strategy: st.SearchStrategy[T], examples: list[T], suppress_health_check: list | None = None
39
+ ) -> None:
40
+ from hypothesis import given, seed, settings
41
+
42
+ applied_settings = default_settings()
43
+ if suppress_health_check is not None:
44
+ applied_settings = settings(applied_settings, suppress_health_check=suppress_health_check)
39
45
 
40
46
  @given(strategy) # type: ignore
41
- @default_settings() # type: ignore
47
+ @applied_settings # type: ignore
42
48
  def example_generating_inner_function(ex: T) -> None:
43
49
  examples.append(ex)
44
50
 
@@ -2,9 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from contextlib import contextmanager
4
4
  from dataclasses import dataclass
5
+ from enum import Enum
5
6
  from typing import TYPE_CHECKING, Any, Generator
6
7
 
7
- from hypothesis.errors import Unsatisfiable
8
+ from hypothesis import HealthCheck
9
+ from hypothesis.errors import FailedHealthCheck, InvalidArgument, Unsatisfiable
8
10
  from hypothesis.reporting import with_reporter
9
11
 
10
12
  from schemathesis.config import OutputConfig
@@ -143,3 +145,141 @@ def build_unsatisfiable_error(operation: APIOperation, *, with_tip: bool) -> Uns
143
145
  message += "\n\nTip: Review all parameters and request body schemas for conflicting constraints"
144
146
 
145
147
  return Unsatisfiable(message)
148
+
149
+
150
+ HEALTH_CHECK_CAUSES = {
151
+ HealthCheck.data_too_large: """ - Arrays with large minItems (e.g., minItems: 1000)
152
+ - Strings with large minLength (e.g., minLength: 10000)
153
+ - Deeply nested objects with many required properties""",
154
+ HealthCheck.filter_too_much: """ - Complex regex patterns that match few strings
155
+ - Multiple overlapping constraints (pattern + format + enum)""",
156
+ HealthCheck.too_slow: """ - Regex with excessive backtracking (e.g., (a+)+b)
157
+ - Many interdependent constraints
158
+ - Large combinatorial complexity""",
159
+ HealthCheck.large_base_example: """ - Arrays with large minimum size (e.g., minItems: 100)
160
+ - Many required properties with their own large minimums
161
+ - Nested structures that multiply size requirements""",
162
+ }
163
+
164
+ HEALTH_CHECK_ACTIONS = {
165
+ HealthCheck.data_too_large: "Reduce minItems, minLength, or size constraints to realistic values",
166
+ HealthCheck.filter_too_much: "Simplify constraints or widen acceptable value ranges",
167
+ HealthCheck.too_slow: "Simplify regex patterns or reduce constraint complexity",
168
+ HealthCheck.large_base_example: "Reduce minimum size requirements or number of required properties",
169
+ }
170
+
171
+ HEALTH_CHECK_TITLES = {
172
+ HealthCheck.data_too_large: "Generated examples exceed size limits",
173
+ HealthCheck.filter_too_much: "Too many generated examples are filtered out",
174
+ HealthCheck.too_slow: "Data generation is too slow",
175
+ HealthCheck.large_base_example: "Minimum possible example is too large",
176
+ }
177
+
178
+
179
+ @dataclass
180
+ class SlowParameter:
181
+ """Information about a parameter with slow or problematic data generation."""
182
+
183
+ location: ParameterLocation
184
+ name: str
185
+ schema: JsonSchema
186
+ original: HealthCheck
187
+
188
+ __slots__ = ("location", "name", "schema", "original")
189
+
190
+ def get_error_message(self, config: OutputConfig) -> str:
191
+ formatted_schema = truncate_json(self.schema, config=config)
192
+ if self.location == ParameterLocation.BODY:
193
+ # For body, name is the media type
194
+ location = f"request body ({self.name})"
195
+ else:
196
+ location = f"{self.location.value} parameter '{self.name}'"
197
+ title = HEALTH_CHECK_TITLES[self.original]
198
+ causes = HEALTH_CHECK_CAUSES[self.original]
199
+
200
+ return f"""{title} for {location}
201
+ Schema:
202
+
203
+ {formatted_schema}
204
+
205
+ This usually means:
206
+ {causes}"""
207
+
208
+
209
+ def _extract_health_check_reason(exc: FailedHealthCheck | InvalidArgument) -> HealthCheck | None:
210
+ message = str(exc).lower()
211
+ if "data_too_large" in message or "too large" in message:
212
+ return HealthCheck.data_too_large
213
+ elif "filter_too_much" in message or "filtered out" in message:
214
+ return HealthCheck.filter_too_much
215
+ elif "too_slow" in message or "too slow" in message:
216
+ return HealthCheck.too_slow
217
+ elif ("large_base_example" in message or "can never generate an example, because min_size" in message) or (
218
+ isinstance(exc, InvalidArgument)
219
+ and message.endswith("larger than hypothesis is designed to handle")
220
+ or "can never generate an example, because min_size is larger than hypothesis supports" in message
221
+ ):
222
+ return HealthCheck.large_base_example
223
+
224
+ return None
225
+
226
+
227
+ def find_slow_parameter(operation: APIOperation, reason: HealthCheck) -> SlowParameter | None:
228
+ from hypothesis.errors import FailedHealthCheck
229
+ from hypothesis_jsonschema import from_schema
230
+
231
+ for location, container in (
232
+ (ParameterLocation.QUERY, operation.query),
233
+ (ParameterLocation.PATH, operation.path_parameters),
234
+ (ParameterLocation.HEADER, operation.headers),
235
+ (ParameterLocation.COOKIE, operation.cookies),
236
+ (ParameterLocation.BODY, operation.body),
237
+ ):
238
+ for parameter in container:
239
+ try:
240
+ generate_one(from_schema(parameter.optimized_schema), suppress_health_check=[])
241
+ except (FailedHealthCheck, Unsatisfiable, InvalidArgument):
242
+ if location == ParameterLocation.BODY:
243
+ name = parameter.media_type
244
+ else:
245
+ name = parameter.name
246
+
247
+ schema = unbundle_schema_refs(parameter.optimized_schema, parameter.name_to_uri)
248
+ return SlowParameter(location=location, name=name, schema=schema, original=reason)
249
+ return None
250
+
251
+
252
+ def _get_generic_health_check_message(reason: HealthCheck) -> str:
253
+ title = HEALTH_CHECK_TITLES[reason]
254
+ causes = HEALTH_CHECK_CAUSES[reason]
255
+ return f"{title} for this operation\n\nUnable to identify the specific parameter. Common causes:\n{causes}"
256
+
257
+
258
+ class HealthCheckTipStyle(Enum):
259
+ DEFAULT = "default"
260
+ PYTEST = "pytest"
261
+
262
+
263
+ def build_health_check_error(
264
+ operation: APIOperation,
265
+ original: FailedHealthCheck | InvalidArgument,
266
+ with_tip: bool,
267
+ tip_style: HealthCheckTipStyle = HealthCheckTipStyle.DEFAULT,
268
+ ) -> FailedHealthCheck | InvalidArgument:
269
+ __tracebackhide__ = True
270
+ reason = _extract_health_check_reason(original)
271
+ if reason is None:
272
+ return original
273
+ slow_param = find_slow_parameter(operation, reason)
274
+
275
+ if slow_param is not None:
276
+ message = slow_param.get_error_message(operation.schema.config.output)
277
+ else:
278
+ message = _get_generic_health_check_message(reason)
279
+
280
+ if with_tip:
281
+ message += f"\n\nTip: {HEALTH_CHECK_ACTIONS[reason]}"
282
+ if tip_style == HealthCheckTipStyle.PYTEST:
283
+ message += f". You can disable this health check with @settings(suppress_health_check=[{reason!r}])"
284
+
285
+ return FailedHealthCheck(message)
@@ -449,7 +449,7 @@ class UnsupportedMethodResponse(Failure):
449
449
  allow_header_present: bool | None = None,
450
450
  failure_reason: str, # "wrong_status" or "missing_allow_header"
451
451
  message: str,
452
- title: str = "Unsupported method incorrect response",
452
+ title: str = "Unsupported methods",
453
453
  case_id: str | None = None,
454
454
  ) -> None:
455
455
  self.operation = operation
@@ -9,7 +9,7 @@ import pytest
9
9
  from _pytest import nodes
10
10
  from _pytest.config import hookimpl
11
11
  from _pytest.python import Class, Function, FunctionDefinition, Metafunc, Module, PyCollector
12
- from hypothesis.errors import InvalidArgument, Unsatisfiable
12
+ from hypothesis.errors import FailedHealthCheck, InvalidArgument, Unsatisfiable
13
13
  from jsonschema.exceptions import SchemaError
14
14
 
15
15
  from schemathesis.core.control import SkipTest
@@ -34,7 +34,12 @@ from schemathesis.generation.hypothesis.given import (
34
34
  merge_given_args,
35
35
  validate_given_args,
36
36
  )
37
- from schemathesis.generation.hypothesis.reporting import build_unsatisfiable_error, ignore_hypothesis_output
37
+ from schemathesis.generation.hypothesis.reporting import (
38
+ HealthCheckTipStyle,
39
+ build_health_check_error,
40
+ build_unsatisfiable_error,
41
+ ignore_hypothesis_output,
42
+ )
38
43
  from schemathesis.pytest.control_flow import fail_on_no_matches
39
44
  from schemathesis.schemas import APIOperation
40
45
 
@@ -328,6 +333,12 @@ def pytest_pyfunc_call(pyfuncitem): # type:ignore
328
333
  if invalid_headers is not None:
329
334
  raise InvalidHeadersExample.from_headers(invalid_headers) from None
330
335
  pytest.skip(exc.args[0])
336
+ except FailedHealthCheck as exc:
337
+ operation = ApiOperationMark.get(pyfuncitem.obj)
338
+ assert operation is not None
339
+ raise build_health_check_error(
340
+ operation, exc, with_tip=True, tip_style=HealthCheckTipStyle.PYTEST
341
+ ) from None
331
342
  except Unsatisfiable:
332
343
  operation = ApiOperationMark.get(pyfuncitem.obj)
333
344
  assert operation is not None
@@ -320,7 +320,7 @@ def unsupported_method(ctx: CheckContext, response: Response, case: Case) -> boo
320
320
  method=cast(str, response.request.method),
321
321
  status_code=response.status_code,
322
322
  failure_reason="wrong_status",
323
- message=f"Wrong status for unsupported method {response.request.method} (got {response.status_code}, expected 405)",
323
+ message=f"Unsupported method {response.request.method} returned {response.status_code}, expected 405 Method Not Allowed\n\nReturn 405 for methods not listed in the OpenAPI spec",
324
324
  )
325
325
 
326
326
  allow_header = response.headers.get("allow")
@@ -331,7 +331,7 @@ def unsupported_method(ctx: CheckContext, response: Response, case: Case) -> boo
331
331
  status_code=response.status_code,
332
332
  allow_header_present=False,
333
333
  failure_reason="missing_allow_header",
334
- message=f"Missing Allow header for unsupported method {response.request.method}",
334
+ message=f"{response.request.method} returned 405 without required `Allow` header\n\nAdd `Allow` header listing supported methods (required by RFC 9110)",
335
335
  )
336
336
  return None
337
337
 
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from itertools import chain
4
3
  from typing import Any, Callable, overload
5
4
 
6
5
  from schemathesis.core.jsonschema.bundler import BUNDLE_STORAGE_KEY
@@ -93,6 +92,8 @@ def _to_json_schema(
93
92
  # Read-only properties should not occur in requests
94
93
  rewrite_properties(schema, is_read_only)
95
94
 
95
+ ensure_required_properties(schema)
96
+
96
97
  for keyword, value in schema.items():
97
98
  if keyword in IN_VALUE and isinstance(value, dict):
98
99
  schema[keyword] = _to_json_schema(
@@ -121,6 +122,22 @@ def _to_json_schema(
121
122
  return schema
122
123
 
123
124
 
125
+ def ensure_required_properties(schema: dict[str, Any]) -> None:
126
+ if schema.get("additionalProperties") is not False:
127
+ return
128
+
129
+ required = schema.get("required")
130
+ if not required or not isinstance(required, list):
131
+ return
132
+
133
+ properties = schema.setdefault("properties", {})
134
+
135
+ # Add missing required properties as empty schemas
136
+ for name in required:
137
+ if name not in properties:
138
+ properties[name] = {}
139
+
140
+
124
141
  IN_VALUE = frozenset(
125
142
  (
126
143
  "additionalProperties",
@@ -170,29 +187,17 @@ def update_pattern_in_schema(schema: dict[str, Any]) -> None:
170
187
 
171
188
  def rewrite_properties(schema: dict[str, Any], predicate: Callable[[dict[str, Any]], bool]) -> None:
172
189
  required = schema.get("required", [])
173
- forbidden = []
174
190
  for name, subschema in list(schema.get("properties", {}).items()):
175
191
  if predicate(subschema):
176
192
  if name in required:
177
193
  required.remove(name)
178
- del schema["properties"][name]
179
- forbidden.append(name)
180
- if forbidden:
181
- forbid_properties(schema, forbidden)
194
+ schema["properties"][name] = {"not": {}}
182
195
  if not schema.get("required"):
183
196
  schema.pop("required", None)
184
197
  if not schema.get("properties"):
185
198
  schema.pop("properties", None)
186
199
 
187
200
 
188
- def forbid_properties(schema: dict[str, Any], forbidden: list[str]) -> None:
189
- """Explicitly forbid properties via the `not` keyword."""
190
- not_schema = schema.setdefault("not", {})
191
- already_forbidden = not_schema.setdefault("required", [])
192
- already_forbidden.extend(forbidden)
193
- not_schema["required"] = list(set(chain(already_forbidden, forbidden)))
194
-
195
-
196
201
  def is_write_only(schema: dict[str, Any] | bool) -> bool:
197
202
  if isinstance(schema, bool):
198
203
  return False
@@ -242,7 +242,7 @@ def _expand_subschemas(
242
242
  except InfiniteRecursiveReference:
243
243
  return
244
244
 
245
- yield (schema, current_path)
245
+ yield schema, current_path
246
246
 
247
247
  if isinstance(schema, dict):
248
248
  # For anyOf/oneOf, yield each alternative with the same path
@@ -250,10 +250,10 @@ def _expand_subschemas(
250
250
  if key in schema:
251
251
  for subschema in schema[key]:
252
252
  # Each alternative starts with the current path
253
- yield (subschema, current_path)
253
+ yield subschema, current_path
254
254
 
255
255
  # For allOf, merge all alternatives
256
- if "allOf" in schema and schema["allOf"]:
256
+ if schema.get("allOf"):
257
257
  subschema = deepclone(schema["allOf"][0])
258
258
  try:
259
259
  subschema, expanded_path = _resolve_bundled(subschema, resolver, current_path)
@@ -278,7 +278,7 @@ def _expand_subschemas(
278
278
  else:
279
279
  subschema[key] = value
280
280
 
281
- yield (subschema, expanded_path)
281
+ yield subschema, expanded_path
282
282
 
283
283
 
284
284
  def extract_inner_examples(examples: dict[str, Any] | list, schema: BaseOpenAPISchema) -> Generator[Any, None, None]:
@@ -9,6 +9,7 @@ import requests
9
9
 
10
10
  from schemathesis.core.compat import RefResolutionError, RefResolver
11
11
  from schemathesis.core.deserialization import deserialize_yaml
12
+ from schemathesis.core.errors import RemoteDocumentError
12
13
  from schemathesis.core.transport import DEFAULT_RESPONSE_TIMEOUT
13
14
 
14
15
 
@@ -30,10 +31,39 @@ def load_file_uri(location: str) -> dict[str, Any]:
30
31
  return load_file_impl(location, urlopen)
31
32
 
32
33
 
34
+ _HTML_MARKERS = (b"<!doctype", b"<html", b"<head", b"<body")
35
+
36
+
37
+ def _looks_like_html(content_type: str | None, body: bytes) -> bool:
38
+ if content_type and "html" in content_type.lower():
39
+ return True
40
+ head = body.lstrip()[:64].lower()
41
+ return any(head.startswith(m) for m in _HTML_MARKERS)
42
+
43
+
33
44
  def load_remote_uri(uri: str) -> Any:
34
45
  """Load the resource and parse it as YAML / JSON."""
35
46
  response = requests.get(uri, timeout=DEFAULT_RESPONSE_TIMEOUT)
36
- return deserialize_yaml(response.content)
47
+ content_type = response.headers.get("Content-Type", "")
48
+ body = response.content or b""
49
+
50
+ def _suffix() -> str:
51
+ return f"(HTTP {response.status_code}, Content-Type={content_type}, size={len(body)})"
52
+
53
+ if not (200 <= response.status_code < 300):
54
+ raise RemoteDocumentError(f"Failed to fetch {_suffix()}")
55
+
56
+ if _looks_like_html(content_type, body):
57
+ raise RemoteDocumentError(f"Expected YAML/JSON, got HTML {_suffix()}")
58
+
59
+ document = deserialize_yaml(response.content)
60
+
61
+ if not isinstance(document, (dict, list)):
62
+ raise RemoteDocumentError(
63
+ f"Remote document is parsed as {type(document).__name__}, but an object/array is expected {_suffix()}"
64
+ )
65
+
66
+ return document
37
67
 
38
68
 
39
69
  JSONType = Union[None, bool, float, str, list, Dict[str, Any]]
@@ -114,7 +114,9 @@ class RequestsTransport(BaseTransport["requests.Session"]):
114
114
  data.setdefault("cert", cert)
115
115
 
116
116
  kwargs.pop("base_url", None)
117
- data.update({key: value for key, value in kwargs.items() if key not in data})
117
+ for key, value in kwargs.items():
118
+ if key not in ("headers", "cookies", "params") or key not in data:
119
+ data[key] = value
118
120
  data.setdefault("timeout", DEFAULT_RESPONSE_TIMEOUT)
119
121
 
120
122
  current_session_headers: MutableMapping[str, Any] = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.3.14
3
+ Version: 4.3.16
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
@@ -1,6 +1,6 @@
1
1
  schemathesis/__init__.py,sha256=QqVUCBQr-RDEstgCZLsxzIa9HJslVSeijrm9gES4b_0,1423
2
2
  schemathesis/auths.py,sha256=JdEwPRS9WKmPcxzGXYYz9pjlIUMQYCfif7ZJU0Kde-I,16400
3
- schemathesis/checks.py,sha256=yyR219TQjLAieop5O2waEVLILF6_i10B8mkutd6QaCs,6590
3
+ schemathesis/checks.py,sha256=F_lsC5JTUKm_ByvilBN_9IpbL4mJiidfLgS8ir2ZDoQ,6873
4
4
  schemathesis/errors.py,sha256=K3irHIZkrBH2-9LIjlgXlm8RNC41Nffd39ncfwagUvw,1053
5
5
  schemathesis/filters.py,sha256=IevPA5A04GfRLLjmkFLZ0CLhjNO3RmpZq_yw6MqjLIA,13515
6
6
  schemathesis/hooks.py,sha256=q2wqYNgpMCO8ImSBkbrWDSwN0BSELelqJMgAAgGvv2M,14836
@@ -21,7 +21,7 @@ schemathesis/cli/commands/run/loaders.py,sha256=eRgP1ZPfhOfxR7iXQ_CfV9r_8jP1DN4t
21
21
  schemathesis/cli/commands/run/validation.py,sha256=DQaMiBLN2tYT9hONvv8xnyPvNXZH768UlOdUxTd5kZs,9193
22
22
  schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31MAXXn1qI7uU4FtiDwroXZI,1915
23
23
  schemathesis/cli/commands/run/handlers/base.py,sha256=qUtDvtr3F6were_BznfnaPpMibGJMnQ5CA9aEzcIUBc,1306
24
- schemathesis/cli/commands/run/handlers/cassettes.py,sha256=LzvQp--Ub5MXF7etet7fQD0Ufloh1R0j2X1o9dT8Z4k,19253
24
+ schemathesis/cli/commands/run/handlers/cassettes.py,sha256=2sXW9jykEFw4HCv25ycRfRd8uw5VV1e26Cp14O7PVhs,19245
25
25
  schemathesis/cli/commands/run/handlers/junitxml.py,sha256=qiFvM4-SlM67sep003SkLqPslzaEb4nOm3bkzw-DO-Q,2602
26
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
@@ -33,7 +33,7 @@ schemathesis/config/_auth.py,sha256=83RLVPm97W2thbn-yi01Rt94YwOxLG_a5VoxhEfjUjs,
33
33
  schemathesis/config/_checks.py,sha256=F0r16eSSiICvoiTUkNNOE2PH73EGd8bikoeZdME_3Yw,10763
34
34
  schemathesis/config/_diff_base.py,sha256=U7wuE4480EjP3K16mfC528TP5q7Q5IwAZwZLqRIrS1E,4300
35
35
  schemathesis/config/_env.py,sha256=8XfIyrnGNQuCDnfG0lwmKRFbasRUjgeQGBAMupsmtOU,613
36
- schemathesis/config/_error.py,sha256=jfv9chQ4NGoDYypszNGymr0zxXVo65yP0AWK1WVEPIM,5781
36
+ schemathesis/config/_error.py,sha256=0QnXO7Zagr69aYsX46GHm5xcTDd_18m8pFQBb0SsYvY,5777
37
37
  schemathesis/config/_generation.py,sha256=giWs4z17z9nRe_9Z3mAZ3LEoyh4hkcJnlAA6LSy6iEo,5210
38
38
  schemathesis/config/_health_check.py,sha256=zC9inla5ibMBlEy5WyM4_TME7ju_KH3Bwfo21RI3Gks,561
39
39
  schemathesis/config/_operations.py,sha256=JvfMkieYBkbEmZRb4cTvQLfvHQLhmsxa3GXzgjOtmFc,12383
@@ -52,7 +52,7 @@ schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1
52
52
  schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
53
53
  schemathesis/core/curl.py,sha256=jrPL9KpNHteyJ6A1oxJRSkL5bfuBeuPs3xh9Z_ml2cE,1892
54
54
  schemathesis/core/deserialization.py,sha256=qjXUPaz_mc1OSgXzTUSkC8tuVR8wgVQtb9g3CcAF6D0,2951
55
- schemathesis/core/errors.py,sha256=sr23WgbD-52n5fmC-QBn2suzNUbsB1okvXIs_L5EyR0,19918
55
+ schemathesis/core/errors.py,sha256=NxuhMozUnC57BSsTKC95zJgSflZ1d01D3j6p8VyXhcw,20209
56
56
  schemathesis/core/failures.py,sha256=yFpAxWdEnm0Ri8z8RqRI9H7vcLH5ztOeSIi4m4SGx5g,8996
57
57
  schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
58
58
  schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
@@ -62,7 +62,7 @@ schemathesis/core/marks.py,sha256=SH7jsVuNRJjx2gZN9Ze5MY01u7FJiHeO0iruzKi5rm4,21
62
62
  schemathesis/core/media_types.py,sha256=VN1QhgfwBHpTw0CQquzAfhJstxXIXBzSSy0NZmAUcAo,2185
63
63
  schemathesis/core/parameters.py,sha256=20pd4DLW1uXs61YuESKG1Fx9QJJQN3JY7CKnOJMgDb4,790
64
64
  schemathesis/core/rate_limit.py,sha256=7tg9Znk11erTfw8-ANutjEmu7hbfUHZx_iEdkoaP174,1757
65
- schemathesis/core/registries.py,sha256=T4jZB4y3zBHdeSgQc0pRbgSeMblvO-6z4I3zmzIfTi0,811
65
+ schemathesis/core/registries.py,sha256=ksWLxPcohKInH9DwB56KNK0AwGO0Gk5LR2z_Q7cRvps,884
66
66
  schemathesis/core/result.py,sha256=d449YvyONjqjDs-A5DAPgtAI96iT753K8sU6_1HLo2Q,461
67
67
  schemathesis/core/transforms.py,sha256=n_inn8Vb-CFiDUd9Z8E7JIp2kn2eAnxO8DD3OSwOTiM,4310
68
68
  schemathesis/core/transport.py,sha256=LQcamAkFqJ0HuXQzepevAq2MCJW-uq5Nm-HE9yc7HMI,7503
@@ -79,7 +79,7 @@ schemathesis/engine/__init__.py,sha256=QaFE-FinaTAaarteADo2RRMJ-Sz6hZB9TzD5KjMin
79
79
  schemathesis/engine/context.py,sha256=YaBfwTUyTCZaMq7-jtAKFQj-Eh1aQdbZ0UNcC5d_epU,5792
80
80
  schemathesis/engine/control.py,sha256=FXzP8dxL47j1Giqpy2-Bsr_MdMw9YiATSK_UfpFwDtk,1348
81
81
  schemathesis/engine/core.py,sha256=qlPHnZVq2RrUe93fOciXd1hC3E1gVyF2BIWMPMeLIj8,6655
82
- schemathesis/engine/errors.py,sha256=Vg64E97RS--IH6bXPPkQTDl2vw1DbasveCfL037JALU,18478
82
+ schemathesis/engine/errors.py,sha256=WVDITmwEgs1O59yrBtQX4_bWCNhdNKZ8DkJXYMRjUpY,16835
83
83
  schemathesis/engine/events.py,sha256=jpCtMkWWfNe2jUeZh_Ly_wfZEF44EOodL-I_W4C9rgg,6594
84
84
  schemathesis/engine/observations.py,sha256=T-5R8GeVIqvxpCMxc6vZ04UUxUTx3w7689r3Dc6bIcE,1416
85
85
  schemathesis/engine/recorder.py,sha256=KWyWkGkZxIwSDU92jNWCJXU4G4E5WqfhLM6G1Yi7Jyo,8636
@@ -89,7 +89,7 @@ schemathesis/engine/phases/stateful/__init__.py,sha256=Lz1rgNqCfUSIz173XqCGsiMuU
89
89
  schemathesis/engine/phases/stateful/_executor.py,sha256=yRpUJqKLTKMVRy7hEXPwmI23CtgGIprz341lCJwvTrU,15613
90
90
  schemathesis/engine/phases/stateful/context.py,sha256=A7X1SLDOWFpCvFN9IiIeNVZM0emjqatmJL_k9UsO7vM,2946
91
91
  schemathesis/engine/phases/unit/__init__.py,sha256=9dDcxyj887pktnE9YDIPNaR-vc7iqKQWIrFr77SbUTQ,8786
92
- schemathesis/engine/phases/unit/_executor.py,sha256=8ryOJuAXulDum3NDR2w-ey1x7wSrEmdR9dnpL8vOcsY,17419
92
+ schemathesis/engine/phases/unit/_executor.py,sha256=9qlc0SEq-9M1mqszB-IaIuaqQYoGkSUD3ygTciUhBl0,17871
93
93
  schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
94
94
  schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
95
95
  schemathesis/generation/case.py,sha256=SLMw6zkzmeiZdaIij8_0tjTF70BrMlRSWREaqWii0uM,12508
@@ -100,16 +100,16 @@ schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeX
100
100
  schemathesis/generation/overrides.py,sha256=xI2djHsa42fzP32xpxgxO52INixKagf5DjDAWJYswM8,3890
101
101
  schemathesis/generation/hypothesis/__init__.py,sha256=68BHULoXQC1WjFfw03ga5lvDGZ-c-J7H_fNEuUzFWRw,4976
102
102
  schemathesis/generation/hypothesis/builder.py,sha256=N9UiJfwUplmrCLsROHQtirm1UPt_TDqUiLv4as2qAjU,38562
103
- schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
103
+ schemathesis/generation/hypothesis/examples.py,sha256=9fc3fQW1e0ZxJuHyyCGhPeyL_gCtb8h29XK-Yz-s1Vk,1716
104
104
  schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
105
- schemathesis/generation/hypothesis/reporting.py,sha256=8MUT_HC_iUJ8aVfxEVQ7aPTLx2ZwqOMnTucxfP5qblM,5863
105
+ schemathesis/generation/hypothesis/reporting.py,sha256=d5vuv5KF4bgIoMV2qy_LwikfowK8BQkj8fjORMJT6PU,11481
106
106
  schemathesis/generation/stateful/__init__.py,sha256=s7jiJEnguIj44IsRyMi8afs-8yjIUuBbzW58bH5CHjs,1042
107
107
  schemathesis/generation/stateful/state_machine.py,sha256=CiVtpBEeotpNOUkYO3vJLKRe89gdT1kjguZ88vbfqs0,9500
108
108
  schemathesis/graphql/__init__.py,sha256=_eO6MAPHGgiADVGRntnwtPxmuvk666sAh-FAU4cG9-0,326
109
109
  schemathesis/graphql/checks.py,sha256=IADbxiZjgkBWrC5yzHDtohRABX6zKXk5w_zpWNwdzYo,3186
110
110
  schemathesis/graphql/loaders.py,sha256=2tgG4HIvFmjHLr_KexVXnT8hSBM-dKG_fuXTZgE97So,9445
111
111
  schemathesis/openapi/__init__.py,sha256=-KcsSAM19uOM0N5J4s-yTnQ1BFsptYhW1E51cEf6kVM,311
112
- schemathesis/openapi/checks.py,sha256=nrkkagRqg-HOsDCAMbJqCnHyBZEA2PpRV_AB8lI_I9c,13080
112
+ schemathesis/openapi/checks.py,sha256=icUnRTkfFNP0nOYCyaj7hFHO8gJic0XPKxP0xHkm0Qw,13062
113
113
  schemathesis/openapi/loaders.py,sha256=aaCIf6P8R33l6DBNGD_99m_wruYOPR7ecyL5hT6UChg,10710
114
114
  schemathesis/openapi/generation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
115
  schemathesis/openapi/generation/filters.py,sha256=pY9cUZdL_kQK80Z2aylTOqqa12zmaYUlYC5BfYgeQMk,2395
@@ -117,7 +117,7 @@ schemathesis/pytest/__init__.py,sha256=7W0q-Thcw03IAQfXE_Mo8JPZpUdHJzfu85fjK1Zdf
117
117
  schemathesis/pytest/control_flow.py,sha256=F8rAPsPeNv_sJiJgbZYtTpwKWjauZmqFUaKroY2GmQI,217
118
118
  schemathesis/pytest/lazy.py,sha256=wP0sqcVFcD-OjDIFUpYdJdFQ-BY18CVyL0iB6eHiWRw,11088
119
119
  schemathesis/pytest/loaders.py,sha256=Sbv8e5F77_x4amLP50iwubfm6kpOhx7LhLFGsVXW5Ys,925
120
- schemathesis/pytest/plugin.py,sha256=j93P7Bp6V2kjkw4Xg9ZoNgd9txcDiQFHByP6rMkI2R0,14518
120
+ schemathesis/pytest/plugin.py,sha256=fAT76woft3Uak5kGPpt3I8n_LjnzBDjl1adHrho0dA4,14900
121
121
  schemathesis/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
122
  schemathesis/python/asgi.py,sha256=5PyvuTBaivvyPUEi3pwJni91K1kX5Zc0u9c6c1D8a1Q,287
123
123
  schemathesis/python/wsgi.py,sha256=uShAgo_NChbfYaV1117e6UHp0MTg7jaR0Sy_to3Jmf8,219
@@ -129,14 +129,14 @@ schemathesis/specs/graphql/schemas.py,sha256=oNbluhfxpbxRWVIMoLn3_g-bUaVLCea29jO
129
129
  schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
130
130
  schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
131
131
  schemathesis/specs/openapi/_hypothesis.py,sha256=O8vN-koBjzBVZfpD3pmgIt6ecU4ddAPHOxTAORd23Lo,22642
132
- schemathesis/specs/openapi/checks.py,sha256=12ks0V2F8-YKPkItgAc0ZrxsHufWWlsgj-jpj-cF40A,31578
133
- schemathesis/specs/openapi/converter.py,sha256=4a6-8STT5snF7B-t6IsOIGdK5rV16oNqsdvWL7VFf2M,6472
132
+ schemathesis/specs/openapi/checks.py,sha256=tb2s8azZYcO__Jf13ONUstO1inXoZBlo3dD2uuABB24,31712
133
+ schemathesis/specs/openapi/converter.py,sha256=OlvGCCDAiIZS_osM9OQBvDGzwEToUZmVWwzQa4wQNws,6463
134
134
  schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
135
- schemathesis/specs/openapi/examples.py,sha256=uHV1HRqFhwpGNsBWHt7WmehyIyr8d-n-VeKKs4FRt2c,24475
135
+ schemathesis/specs/openapi/examples.py,sha256=bNQVYJAb2LGtl_6josaNj5O4B0b_WaqtXVg1ZTvDDv8,24451
136
136
  schemathesis/specs/openapi/formats.py,sha256=4tYRdckauHxkJCmOhmdwDq_eOpHPaKloi89lzMPbPzw,3975
137
137
  schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
138
138
  schemathesis/specs/openapi/patterns.py,sha256=GqPZEXMRdWENQxanWjBOalIZ2MQUjuxk21kmdiI703E,18027
139
- schemathesis/specs/openapi/references.py,sha256=AW1laU23BkiRf0EEFM538vyVFLXycGUiucGVV461le0,1927
139
+ schemathesis/specs/openapi/references.py,sha256=Jez2KpfsDIwXSvxZCmRQtlHk2X9UZ-JZkKAJaCPSLk8,2981
140
140
  schemathesis/specs/openapi/schemas.py,sha256=ONFB8kMBrryZL_tKHWvxnBjyUHoHh_MAUqxjuVDc78c,34034
141
141
  schemathesis/specs/openapi/serialization.py,sha256=RPNdadne5wdhsGmjSvgKLRF58wpzpRx3wura8PsHM3o,12152
142
142
  schemathesis/specs/openapi/utils.py,sha256=XkOJT8qD-6uhq-Tmwxk_xYku1Gy5F9pKL3ldNg_DRZw,522
@@ -175,11 +175,11 @@ schemathesis/specs/openapi/types/v3.py,sha256=Vondr9Amk6JKCIM6i6RGcmTUjFfPgOOqzB
175
175
  schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
176
176
  schemathesis/transport/asgi.py,sha256=qTClt6oT_xUEWnRHokACN_uqCNNUZrRPT6YG0PjbElY,926
177
177
  schemathesis/transport/prepare.py,sha256=erYXRaxpQokIDzaIuvt_csHcw72iHfCyNq8VNEzXd0o,4743
178
- schemathesis/transport/requests.py,sha256=wriRI9fprTplE_qEZLEz1TerX6GwkE3pwr6ZnU2o6vQ,10648
178
+ schemathesis/transport/requests.py,sha256=jJAKqmsnlErU066WlUTIZHa3rx5lFtHWT43nm8CewUc,10717
179
179
  schemathesis/transport/serialization.py,sha256=GwO6OAVTmL1JyKw7HiZ256tjV4CbrRbhQN0ep1uaZwI,11157
180
180
  schemathesis/transport/wsgi.py,sha256=kQtasFre6pjdJWRKwLA_Qb-RyQHCFNpaey9ubzlFWKI,5907
181
- schemathesis-4.3.14.dist-info/METADATA,sha256=TvgjAcKH_S1rOrvKQsipJY2hJbJE1s_yzxNxKATSEfM,8566
182
- schemathesis-4.3.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
183
- schemathesis-4.3.14.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
184
- schemathesis-4.3.14.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
185
- schemathesis-4.3.14.dist-info/RECORD,,
181
+ schemathesis-4.3.16.dist-info/METADATA,sha256=C--ciooZRdh7oAzk5hwFfccPqx1-G8QPHTTxbh_lZGw,8566
182
+ schemathesis-4.3.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
183
+ schemathesis-4.3.16.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
184
+ schemathesis-4.3.16.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
185
+ schemathesis-4.3.16.dist-info/RECORD,,