schemathesis 4.0.9__py3-none-any.whl → 4.0.11__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.
@@ -429,6 +429,7 @@ def is_unrecoverable_network_error(exc: Exception) -> bool:
429
429
  if any(
430
430
  pattern in exc_str
431
431
  for pattern in [
432
+ "Connection aborted",
432
433
  "Connection reset by peer",
433
434
  "[Errno 104]",
434
435
  "ECONNRESET",
@@ -1100,6 +1100,8 @@ def _negative_type(
1100
1100
  del strategies["integer"]
1101
1101
  if "integer" in types:
1102
1102
  strategies["number"] = FLOAT_STRATEGY.filter(_is_non_integer_float)
1103
+ if ctx.location == "query":
1104
+ strategies.pop("object", None)
1103
1105
  for strategy in strategies.values():
1104
1106
  value = ctx.generate_from(strategy)
1105
1107
  if seen.insert(value) and ctx.is_valid_for_location(value):
@@ -206,20 +206,23 @@ def _find_request_body_examples_definition(
206
206
 
207
207
 
208
208
  def extract_inner_examples(
209
- examples: dict[str, Any], unresolved_definition: dict[str, Any]
209
+ examples: dict[str, Any] | list, unresolved_definition: dict[str, Any]
210
210
  ) -> Generator[Any, None, None]:
211
211
  """Extract exact examples values from the `examples` dictionary."""
212
- for name, example in examples.items():
213
- if "$ref" in unresolved_definition[name] and "value" not in example and "externalValue" not in example:
214
- # The example here is a resolved example and should be yielded as is
215
- yield example
216
- if isinstance(example, dict):
217
- if "value" in example:
218
- yield example["value"]
219
- elif "externalValue" in example:
220
- with suppress(requests.RequestException):
221
- # Report a warning if not available?
222
- yield load_external_example(example["externalValue"])
212
+ if isinstance(examples, dict):
213
+ for name, example in examples.items():
214
+ if "$ref" in unresolved_definition[name] and "value" not in example and "externalValue" not in example:
215
+ # The example here is a resolved example and should be yielded as is
216
+ yield example
217
+ if isinstance(example, dict):
218
+ if "value" in example:
219
+ yield example["value"]
220
+ elif "externalValue" in example:
221
+ with suppress(requests.RequestException):
222
+ # Report a warning if not available?
223
+ yield load_external_example(example["externalValue"])
224
+ elif isinstance(examples, list):
225
+ yield from examples
223
226
 
224
227
 
225
228
  @lru_cache
@@ -381,9 +384,12 @@ def find_in_responses(operation: APIOperation) -> dict[str, list[dict[str, Any]]
381
384
  ("x-examples", "x-example"),
382
385
  ):
383
386
  examples = definition.get(examples_field, {})
384
- for example in examples.values():
385
- if "value" in example:
386
- output.setdefault(name, []).append(example["value"])
387
+ if isinstance(examples, dict):
388
+ for example in examples.values():
389
+ if "value" in example:
390
+ output.setdefault(name, []).append(example["value"])
391
+ elif isinstance(examples, list):
392
+ output.setdefault(name, []).extend(examples)
387
393
  if example_field in definition:
388
394
  output.setdefault(name, []).append(definition[example_field])
389
395
  return output
@@ -60,9 +60,11 @@ class OpenAPIParameter(Parameter):
60
60
  # JSON Schema allows `examples` as an array
61
61
  examples = []
62
62
  if self.examples_field in self.definition:
63
- examples.extend(
64
- [example["value"] for example in self.definition[self.examples_field].values() if "value" in example]
65
- )
63
+ container = self.definition[self.examples_field]
64
+ if isinstance(container, dict):
65
+ examples.extend([example["value"] for example in container.values() if "value" in example])
66
+ elif isinstance(container, list):
67
+ examples.extend(container)
66
68
  if self.example_field in self.definition:
67
69
  examples.append(self.definition[self.example_field])
68
70
  schema = self.from_open_api_to_json_schema(operation, self.definition)
@@ -41,6 +41,7 @@ from schemathesis.generation.case import Case
41
41
  from schemathesis.generation.meta import CaseMetadata
42
42
  from schemathesis.openapi.checks import JsonSchemaError, MissingContentType
43
43
  from schemathesis.specs.openapi.stateful import links
44
+ from schemathesis.specs.openapi.utils import expand_status_code
44
45
 
45
46
  from ...generation import GenerationMode
46
47
  from ...hooks import HookContext, HookDispatcher
@@ -556,12 +557,10 @@ class BaseOpenAPISchema(BaseSchema):
556
557
  except KeyError as exc:
557
558
  path = operation.path
558
559
  self._raise_invalid_schema(exc, path, operation.method)
559
- status_code = str(response.status_code)
560
- if status_code in responses:
561
- return self.resolver.resolve_in_scope(responses[status_code], operation.definition.scope)
562
- if "default" in responses:
563
- return self.resolver.resolve_in_scope(responses["default"], operation.definition.scope)
564
- return None
560
+ definition = _get_response_definition_by_status(response.status_code, responses)
561
+ if definition is None:
562
+ return None
563
+ return self.resolver.resolve_in_scope(definition, operation.definition.scope)
565
564
 
566
565
  def get_headers(
567
566
  self, operation: APIOperation, response: Response
@@ -598,12 +597,8 @@ class BaseOpenAPISchema(BaseSchema):
598
597
  def validate_response(self, operation: APIOperation, response: Response) -> bool | None:
599
598
  __tracebackhide__ = True
600
599
  responses = {str(key): value for key, value in operation.definition.raw.get("responses", {}).items()}
601
- status_code = str(response.status_code)
602
- if status_code in responses:
603
- definition = responses[status_code]
604
- elif "default" in responses:
605
- definition = responses["default"]
606
- else:
600
+ definition = _get_response_definition_by_status(response.status_code, responses)
601
+ if definition is None:
607
602
  # No response defined for the received response status code
608
603
  return None
609
604
  scopes, schema = self.get_response_schema(definition, operation.definition.scope)
@@ -760,6 +755,24 @@ class BaseOpenAPISchema(BaseSchema):
760
755
  return schema
761
756
 
762
757
 
758
+ def _get_response_definition_by_status(status_code: int, responses: dict[str, Any]) -> dict[str, Any] | None:
759
+ # Cast to string, as integers are often there due to YAML deserialization
760
+ responses = {str(status): definition for status, definition in responses.items()}
761
+ if str(status_code) in responses:
762
+ return responses[str(status_code)]
763
+ # More specific should go first
764
+ keys = sorted(responses, key=lambda k: k.count("X"))
765
+ for key in keys:
766
+ if key == "default":
767
+ continue
768
+ status_codes = expand_status_code(key)
769
+ if status_code in status_codes:
770
+ return responses[key]
771
+ if "default" in responses:
772
+ return responses["default"]
773
+ return None
774
+
775
+
763
776
  def _maybe_raise_one_or_more(failures: list[Failure]) -> None:
764
777
  if not failures:
765
778
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 4.0.9
3
+ Version: 4.0.11
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
@@ -72,7 +72,7 @@ schemathesis/engine/__init__.py,sha256=QaFE-FinaTAaarteADo2RRMJ-Sz6hZB9TzD5KjMin
72
72
  schemathesis/engine/context.py,sha256=x-I9KX6rO6hdCvvN8FEdzIZBqIcNaxdNYHgQjcXbZhM,3931
73
73
  schemathesis/engine/control.py,sha256=FXzP8dxL47j1Giqpy2-Bsr_MdMw9YiATSK_UfpFwDtk,1348
74
74
  schemathesis/engine/core.py,sha256=5jfAqFH0XSD7NVgoSXuUPW-dooItscneAzUNq1RBh1E,5712
75
- schemathesis/engine/errors.py,sha256=JAWBsgmtXBeWkSL84rBvqDIQ4FFjiOn6qq8yL6Xcuhw,19130
75
+ schemathesis/engine/errors.py,sha256=wbFTOE0pmpU79oOzx1l-ypKG92wdFySmNzrN_KTNizM,19168
76
76
  schemathesis/engine/events.py,sha256=VV6epicFIJnX4c87fVNSd0ibDccX3gryDv52OUGa3FI,6370
77
77
  schemathesis/engine/recorder.py,sha256=K3HfMARrT5mPWXPnYebjjcq5CcsBRhMrtZwEL9_Lvtg,8432
78
78
  schemathesis/engine/phases/__init__.py,sha256=jUIfb_9QoUo4zmJEVU0z70PgXPYjt8CIqp4qP_HlYHg,3146
@@ -85,7 +85,7 @@ schemathesis/engine/phases/unit/_executor.py,sha256=9MmZoKSBVSPk0LWwN3PZ3iaO9nzp
85
85
  schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Cznqwn0pGQ5wNQ,2480
86
86
  schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
87
87
  schemathesis/generation/case.py,sha256=MuqnKsJBpGm2gaqDFdJi1yGSWgBhqJUwtYaX97kfXgo,11820
88
- schemathesis/generation/coverage.py,sha256=A5bDFxXVSpEYhbKPlSpk3AY0t3TLeoKcwiIXZuO9DOY,49878
88
+ schemathesis/generation/coverage.py,sha256=0d52xHf1HUbilPmCeJlplnw7knSdc0lEv4Hr0HXYUTE,49949
89
89
  schemathesis/generation/meta.py,sha256=adkoMuCfzSjHJ9ZDocQn0GnVldSCkLL3eVR5A_jafwM,2552
90
90
  schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz5-kt4,2836
91
91
  schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
@@ -128,13 +128,13 @@ schemathesis/specs/openapi/checks.py,sha256=0YiMoUy_wsnPvbOrsbnQ2iDxLloNe2-dc5-h
128
128
  schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
129
129
  schemathesis/specs/openapi/converter.py,sha256=lil8IewM5j8tvt4lpA9g_KITvIwx1M96i45DNSHNjoc,3505
130
130
  schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
131
- schemathesis/specs/openapi/examples.py,sha256=11GMuwyTws8MizQ3CkfT3dQD4TFP22CGYBMUyESYsDM,21218
131
+ schemathesis/specs/openapi/examples.py,sha256=V1fbsbMto_So7lTWnyGa7f3u9On2br8yZ-cPzcC2-Bw,21542
132
132
  schemathesis/specs/openapi/formats.py,sha256=8AIS7Uey-Z1wm1WYRqnsVqHwG9d316PdqfKLqwUs7us,3516
133
133
  schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
134
- schemathesis/specs/openapi/parameters.py,sha256=BevME4DWLQ-OFvc_7fREMjj99VAbVNxVb5i8OEX6Pfs,14453
134
+ schemathesis/specs/openapi/parameters.py,sha256=ifu_QQCMUzsUHQAkvsOvLuokns6CzesssmQ3Nd3zxII,14594
135
135
  schemathesis/specs/openapi/patterns.py,sha256=cBj8W4wn7VLJd4nABaIH5f502-zBDiqljxLgPWUn-50,15609
136
136
  schemathesis/specs/openapi/references.py,sha256=40YcDExPLR2B8EOwt-Csw-5MYFi2xj_DXf91J0Pc9d4,8855
137
- schemathesis/specs/openapi/schemas.py,sha256=4bL-nOFOykfOGHbkmUor9MnCqhAzW5S5oIQbL36wvVc,51972
137
+ schemathesis/specs/openapi/schemas.py,sha256=knaGUtxcy4CKeOpvTrVgPifnqKh9eSYdXRPhExFKElk,52539
138
138
  schemathesis/specs/openapi/security.py,sha256=6UWYMhL-dPtkTineqqBFNKca1i4EuoTduw-EOLeE0aQ,7149
139
139
  schemathesis/specs/openapi/serialization.py,sha256=VdDLmeHqxlWM4cxQQcCkvrU6XurivolwEEaT13ohelA,11972
140
140
  schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
@@ -157,8 +157,8 @@ schemathesis/transport/prepare.py,sha256=iiB8KTAqnnuqjWzblIPiGVdkGIF7Yr1SAEz-KZz
157
157
  schemathesis/transport/requests.py,sha256=rziZTrZCVMAqgy6ldB8iTwhkpAsnjKSgK8hj5Sq3ThE,10656
158
158
  schemathesis/transport/serialization.py,sha256=igUXKZ_VJ9gV7P0TUc5PDQBJXl_s0kK9T3ljGWWvo6E,10339
159
159
  schemathesis/transport/wsgi.py,sha256=KoAfvu6RJtzyj24VGB8e-Iaa9smpgXJ3VsM8EgAz2tc,6152
160
- schemathesis-4.0.9.dist-info/METADATA,sha256=X7vMnzd00mlF78IKDhy-Bl6Yz9abZkE2Dxr424OemLo,8471
161
- schemathesis-4.0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
- schemathesis-4.0.9.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
- schemathesis-4.0.9.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
- schemathesis-4.0.9.dist-info/RECORD,,
160
+ schemathesis-4.0.11.dist-info/METADATA,sha256=ee1q1ZJgd2YpYahYSfaxUBU3QsKNqZWDhWuSv_gxmGg,8472
161
+ schemathesis-4.0.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
162
+ schemathesis-4.0.11.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
163
+ schemathesis-4.0.11.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
164
+ schemathesis-4.0.11.dist-info/RECORD,,