schemathesis 3.39.11__py3-none-any.whl → 3.39.13__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.
@@ -114,8 +114,14 @@ def create_test(
114
114
  wrapped_test, operation, hook_dispatcher=hook_dispatcher, as_strategy_kwargs=as_strategy_kwargs
115
115
  )
116
116
  if COVERAGE_PHASE.is_enabled:
117
+ unexpected_methods = generation_config.unexpected_methods if generation_config else None
117
118
  wrapped_test = add_coverage(
118
- wrapped_test, operation, data_generation_methods, auth_storage, as_strategy_kwargs
119
+ wrapped_test,
120
+ operation,
121
+ data_generation_methods,
122
+ auth_storage,
123
+ as_strategy_kwargs,
124
+ unexpected_methods,
119
125
  )
120
126
  return wrapped_test
121
127
 
@@ -225,6 +231,7 @@ def add_coverage(
225
231
  data_generation_methods: list[DataGenerationMethod],
226
232
  auth_storage: AuthStorage | None,
227
233
  as_strategy_kwargs: dict[str, Any],
234
+ unexpected_methods: set[str] | None = None,
228
235
  ) -> Callable:
229
236
  from schemathesis.specs.openapi.constants import LOCATION_TO_CONTAINER
230
237
 
@@ -237,7 +244,7 @@ def add_coverage(
237
244
  for container in LOCATION_TO_CONTAINER.values()
238
245
  if container in as_strategy_kwargs
239
246
  }
240
- for case in _iter_coverage_cases(operation, data_generation_methods):
247
+ for case in _iter_coverage_cases(operation, data_generation_methods, unexpected_methods):
241
248
  if case.media_type and get_first_matching_media_type(case.media_type) is None:
242
249
  continue
243
250
  adjust_urlencoded_payload(case)
@@ -363,7 +370,9 @@ def _stringify_value(val: Any, container_name: str) -> Any:
363
370
 
364
371
 
365
372
  def _iter_coverage_cases(
366
- operation: APIOperation, data_generation_methods: list[DataGenerationMethod]
373
+ operation: APIOperation,
374
+ data_generation_methods: list[DataGenerationMethod],
375
+ unexpected_methods: set[str] | None = None,
367
376
  ) -> Generator[Case, None, None]:
368
377
  from .specs.openapi.constants import LOCATION_TO_CONTAINER
369
378
  from .specs.openapi.examples import find_in_responses, find_matching_in_responses
@@ -373,6 +382,8 @@ def _iter_coverage_cases(
373
382
  serializers = get_serializers_for_operation(operation)
374
383
  template = Template(serializers)
375
384
  responses = find_in_responses(operation)
385
+ # NOTE: The HEAD method is excluded
386
+ unexpected_methods = unexpected_methods or {"get", "put", "post", "delete", "options", "patch", "trace"}
376
387
  for parameter in operation.iter_parameters():
377
388
  location = parameter.location
378
389
  name = parameter.name
@@ -448,8 +459,7 @@ def _iter_coverage_cases(
448
459
  yield case
449
460
  if DataGenerationMethod.negative in data_generation_methods:
450
461
  # Generate HTTP methods that are not specified in the spec
451
- # NOTE: The HEAD method is excluded
452
- methods = {"get", "put", "post", "delete", "options", "patch", "trace"} - set(operation.schema[operation.path])
462
+ methods = unexpected_methods - set(operation.schema[operation.path])
453
463
  for method in sorted(methods):
454
464
  data = template.unmodified()
455
465
  case = operation.make_case(**data.kwargs)
@@ -9,7 +9,7 @@ from collections import defaultdict
9
9
  from dataclasses import dataclass
10
10
  from enum import Enum
11
11
  from queue import Queue
12
- from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Literal, NoReturn, Sequence, Type, cast
12
+ from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Literal, NoReturn, Sequence, cast
13
13
  from urllib.parse import urlparse
14
14
 
15
15
  import click
@@ -27,6 +27,7 @@ from ..constants import (
27
27
  DEFAULT_STATEFUL_RECURSION_LIMIT,
28
28
  EXTENSIONS_DOCUMENTATION_URL,
29
29
  HOOKS_MODULE_ENV_VAR,
30
+ HTTP_METHODS,
30
31
  HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER,
31
32
  ISSUE_TRACKER_URL,
32
33
  WAIT_FOR_SCHEMA_ENV_VAR,
@@ -323,6 +324,16 @@ REPORT_TO_SERVICE = ReportToService()
323
324
  multiple=True,
324
325
  metavar="",
325
326
  )
327
+ @grouped_option(
328
+ "--experimental-coverage-unexpected-methods",
329
+ "coverage_unexpected_methods",
330
+ help="HTTP methods to use when generating test cases with methods not specified in the API during the coverage phase.",
331
+ type=CsvChoice(sorted(HTTP_METHODS), case_sensitive=False),
332
+ callback=callbacks.convert_http_methods,
333
+ metavar="",
334
+ default=None,
335
+ envvar="SCHEMATHESIS_EXPERIMENTAL_COVERAGE_UNEXPECTED_METHODS",
336
+ )
326
337
  @grouped_option(
327
338
  "--experimental-no-failfast",
328
339
  "no_failfast",
@@ -874,6 +885,7 @@ def run(
874
885
  set_path: dict[str, str],
875
886
  experiments: list,
876
887
  no_failfast: bool,
888
+ coverage_unexpected_methods: set[str] | None,
877
889
  missing_required_header_allowed_statuses: list[str],
878
890
  positive_data_acceptance_allowed_statuses: list[str],
879
891
  negative_data_rejection_allowed_statuses: list[str],
@@ -1000,6 +1012,7 @@ def run(
1000
1012
  graphql_allow_null=generation_graphql_allow_null,
1001
1013
  codec=generation_codec,
1002
1014
  with_security_parameters=generation_with_security_parameters,
1015
+ unexpected_methods=coverage_unexpected_methods,
1003
1016
  )
1004
1017
 
1005
1018
  report: ReportToService | click.utils.LazyFile | None
@@ -344,6 +344,14 @@ def convert_checks(ctx: click.core.Context, param: click.core.Parameter, value:
344
344
  return reduce(operator.iadd, value, [])
345
345
 
346
346
 
347
+ def convert_http_methods(
348
+ ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
349
+ ) -> set[str] | None:
350
+ if value is None:
351
+ return value
352
+ return {item.lower() for item in value}
353
+
354
+
347
355
  def convert_status_codes(
348
356
  ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
349
357
  ) -> list[str] | None:
@@ -26,7 +26,12 @@ class CustomHelpMessageChoice(click.Choice):
26
26
  class BaseCsvChoice(click.Choice):
27
27
  def parse_value(self, value: str) -> tuple[list[str], set[str]]:
28
28
  selected = [item for item in value.split(",") if item]
29
- invalid_options = set(selected) - set(self.choices)
29
+ if not self.case_sensitive:
30
+ invalid_options = {
31
+ item for item in selected if item.upper() not in {choice.upper() for choice in self.choices}
32
+ }
33
+ else:
34
+ invalid_options = set(selected) - set(self.choices)
30
35
  return selected, invalid_options
31
36
 
32
37
  def fail_on_invalid_options(self, invalid_options: set[str], selected: list[str]) -> NoReturn:
@@ -50,3 +50,4 @@ class GenerationConfig:
50
50
  with_security_parameters: bool = True
51
51
  # Header generation configuration
52
52
  headers: HeaderConfig = field(default_factory=HeaderConfig)
53
+ unexpected_methods: set[str] | None = None
@@ -124,6 +124,21 @@ def _handle_anchored_pattern(parsed: list, pattern: str, min_length: int | None,
124
124
 
125
125
  for op, value in pattern_parts:
126
126
  if op == LITERAL:
127
+ # Check if the literal comes from a bracketed expression,
128
+ # e.g. Python regex parses "[+]" as a single LITERAL token.
129
+ if pattern[current_position] == "[":
130
+ # Find the matching closing bracket.
131
+ end_idx = current_position + 1
132
+ while end_idx < len(pattern):
133
+ # Check for an unescaped closing bracket.
134
+ if pattern[end_idx] == "]" and (end_idx == current_position + 1 or pattern[end_idx - 1] != "\\"):
135
+ end_idx += 1
136
+ break
137
+ end_idx += 1
138
+ # Append the entire character set.
139
+ result += pattern[current_position:end_idx]
140
+ current_position = end_idx
141
+ continue
127
142
  if pattern[current_position] == "\\":
128
143
  # Escaped value
129
144
  current_position += 2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: schemathesis
3
- Version: 3.39.11
3
+ Version: 3.39.13
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://schemathesis.readthedocs.io/en/stable/changelog.html
@@ -213,7 +213,7 @@ api-tests:
213
213
  schema: "https://example.schemathesis.io/openapi.json"
214
214
  ```
215
215
 
216
- For more details, check out our [GitHub Action](https://github.com/schemathesis/action) repository or see our [GitHub Tutorial](https://docs.schemathesis.io/tutorials/github).
216
+ For more details, check out our [GitHub Action](https://github.com/schemathesis/action) repository.
217
217
 
218
218
  ## Who's Using Schemathesis?
219
219
 
@@ -1,7 +1,7 @@
1
1
  schemathesis/__init__.py,sha256=UW2Bq8hDDkcBeAAA7PzpBFXkOOxkmHox-mfQwzHDjL0,1914
2
2
  schemathesis/_compat.py,sha256=y4RZd59i2NCnZ91VQhnKeMn_8t3SgvLOk2Xm8nymUHY,1837
3
3
  schemathesis/_dependency_versions.py,sha256=pjEkkGAfOQJYNb-9UOo84V8nj_lKHr_TGDVdFwY2UU0,816
4
- schemathesis/_hypothesis.py,sha256=2v6nQk5wiV0z6M_JPbOYhXJTXJ3Cyf6oWfef5VYCNdQ,30350
4
+ schemathesis/_hypothesis.py,sha256=CEfWX38CsPy-RzwMGdKuJD9mY_AV8fIq_ZhabGp4tW0,30759
5
5
  schemathesis/_lazy_import.py,sha256=aMhWYgbU2JOltyWBb32vnWBb6kykOghucEzI_F70yVE,470
6
6
  schemathesis/_override.py,sha256=TAjYB3eJQmlw9K_xiR9ptt9Wj7if4U7UFlUhGjpBAoM,1625
7
7
  schemathesis/_patches.py,sha256=Hsbpn4UVeXUQD2Kllrbq01CSWsTYENWa0VJTyhX5C2k,895
@@ -28,16 +28,16 @@ schemathesis/targets.py,sha256=XIGRghvEzbmEJjse9aZgNEj67L3jAbiazm2rxURWgDE,2351
28
28
  schemathesis/throttling.py,sha256=aisUc4MJDGIOGUAs9L2DlWWpdd4KyAFuNVKhYoaUC9M,1719
29
29
  schemathesis/types.py,sha256=Tem2Q_zyMCd0Clp5iGKv6Fu13wdcbxVE8tCVH9WWt7A,1065
30
30
  schemathesis/utils.py,sha256=LwqxqoAKmRiAdj-qUbNmgQgsamc49V5lc5TnOIDuuMA,4904
31
- schemathesis/cli/__init__.py,sha256=IxJnRTCp0bl1B044jjD9GFJ2pisckw3-PBnNHcABBy4,75795
31
+ schemathesis/cli/__init__.py,sha256=jiDVw31fF8oNl0TyAWJpDo-166PkCqNV4t1rUcy_4oo,76352
32
32
  schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
33
- schemathesis/cli/callbacks.py,sha256=-VA_I_mVma9WxFNtUR8d2KNICKJD5ScayfSdKKPEP5Y,16321
33
+ schemathesis/cli/callbacks.py,sha256=UMCYPfoHXgxtT-uui0zlfeNoOLzteJu90q9ID3qt1m4,16540
34
34
  schemathesis/cli/cassettes.py,sha256=zji-B-uuwyr0Z0BzQX-DLMV6lWb58JtLExcUE1v3m4Y,20153
35
35
  schemathesis/cli/constants.py,sha256=wk-0GsoJIel8wFFerQ6Kf_6eAYUtIWkwMFwyAqv3yj4,1635
36
36
  schemathesis/cli/context.py,sha256=j_lvYQiPa6Q7P4P_IGCM9V2y2gJSpDbpxIIzR5oFB2I,2567
37
37
  schemathesis/cli/debug.py,sha256=_YA-bX1ujHl4bqQDEum7M-I2XHBTEGbvgkhvcvKhmgU,658
38
38
  schemathesis/cli/handlers.py,sha256=EXSAFe5TQlHANz1AVlSttfsoDT2oeaeFbqq1N7e2udw,467
39
39
  schemathesis/cli/junitxml.py,sha256=_psBdqGwH4OKySSWeva41mbgGLav86UnWhQyOt99gnU,5331
40
- schemathesis/cli/options.py,sha256=yL7nrzKkbGCc4nQya9wpTW48XGz_OT9hOFrzPxRrDe4,2853
40
+ schemathesis/cli/options.py,sha256=jPqJxkuAb91wtB_aOUFkGDdGtJ3UDwT2Nn3vvL-odsE,3062
41
41
  schemathesis/cli/reporting.py,sha256=KC3sxSc1u4aFQ-0Q8CQ3G4HTEl7QxlubGnJgNKmVJdQ,3627
42
42
  schemathesis/cli/sanitization.py,sha256=Onw_NWZSom6XTVNJ5NHnC0PAhrYAcGzIXJbsBCzLkn4,1005
43
43
  schemathesis/cli/output/__init__.py,sha256=AXaUzQ1nhQ-vXhW4-X-91vE2VQtEcCOrGtQXXNN55iQ,29
@@ -58,7 +58,7 @@ schemathesis/extra/pytest_plugin.py,sha256=3FF7pcqK26J__FGq6uOZdaD-1tZ-khQEwpdwb
58
58
  schemathesis/fixups/__init__.py,sha256=RP5QYJVJhp8LXjhH89fCRaIVU26dHCy74jD9seoYMuc,967
59
59
  schemathesis/fixups/fast_api.py,sha256=mn-KzBqnR8jl4W5fY-_ZySabMDMUnpzCIESMHnlvE1c,1304
60
60
  schemathesis/fixups/utf8_bom.py,sha256=lWT9RNmJG8i-l5AXIpaCT3qCPUwRgzXPW3eoOjmZETA,745
61
- schemathesis/generation/__init__.py,sha256=29Zys_tD6kfngaC4zHeC6TOBZQcmo7CWm7KDSYsHStQ,1581
61
+ schemathesis/generation/__init__.py,sha256=PClFLK3bu-8Gsy71rgdD0ULMqySrzX-Um8Tan77x_5A,1628
62
62
  schemathesis/generation/_hypothesis.py,sha256=74fzLPHugZgMQXerWYFAMqCAjtAXz5E4gek7Gnkhli4,1756
63
63
  schemathesis/generation/_methods.py,sha256=r8oVlJ71_gXcnEhU-byw2E0R2RswQQFm8U7yGErSqbw,1204
64
64
  schemathesis/generation/coverage.py,sha256=1CilQSe2DIdMdeWA6RL22so2bZULPRwc0CQBRxcLRFs,39370
@@ -117,7 +117,7 @@ schemathesis/specs/openapi/links.py,sha256=C4Uir2P_EcpqME8ee_a1vdUM8Tm3ZcKNn2YsG
117
117
  schemathesis/specs/openapi/loaders.py,sha256=jlTYLoG5sVRh8xycIF2M2VDCZ44M80Sct07a_ycg1Po,25698
118
118
  schemathesis/specs/openapi/media_types.py,sha256=dNTxpRQbY3SubdVjh4Cjb38R6Bc9MF9BsRQwPD87x0g,1017
119
119
  schemathesis/specs/openapi/parameters.py,sha256=X_3PKqUScIiN_vbSFEauPYyxASyFv-_9lZ_9QEZRLqo,14655
120
- schemathesis/specs/openapi/patterns.py,sha256=1hhhLJTJtF2snYEAEd_RzAwEBrNB5ayCXff--Fv6JEs,11881
120
+ schemathesis/specs/openapi/patterns.py,sha256=L99UtslPvwObCVf5ndq3vL2YjQ7H1nMb-ZNMcyz_Qvk,12677
121
121
  schemathesis/specs/openapi/references.py,sha256=euxM02kQGMHh4Ss1jWjOY_gyw_HazafKITIsvOEiAvI,9831
122
122
  schemathesis/specs/openapi/schemas.py,sha256=JA9SiBnwYg75kYnd4_0CWOuQv_XTfYwuDeGmFe4RtVo,53724
123
123
  schemathesis/specs/openapi/security.py,sha256=Z-6pk2Ga1PTUtBe298KunjVHsNh5A-teegeso7zcPIE,7138
@@ -153,8 +153,8 @@ schemathesis/transports/auth.py,sha256=urSTO9zgFO1qU69xvnKHPFQV0SlJL3d7_Ojl0tLnZ
153
153
  schemathesis/transports/content_types.py,sha256=MiKOm-Hy5i75hrROPdpiBZPOTDzOwlCdnthJD12AJzI,2187
154
154
  schemathesis/transports/headers.py,sha256=hr_AIDOfUxsJxpHfemIZ_uNG3_vzS_ZeMEKmZjbYiBE,990
155
155
  schemathesis/transports/responses.py,sha256=OFD4ZLqwEFpo7F9vaP_SVgjhxAqatxIj38FS4XVq8Qs,1680
156
- schemathesis-3.39.11.dist-info/METADATA,sha256=iM14usTIHG9JMQNJsUkBWUUQEcuYJwL74itZ9mZmvXc,11977
157
- schemathesis-3.39.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
158
- schemathesis-3.39.11.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
159
- schemathesis-3.39.11.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
160
- schemathesis-3.39.11.dist-info/RECORD,,
156
+ schemathesis-3.39.13.dist-info/METADATA,sha256=rFdsmXi833eMqVorQ59g1gYgoPeuhQCnripqRiMlFKg,11901
157
+ schemathesis-3.39.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
158
+ schemathesis-3.39.13.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
159
+ schemathesis-3.39.13.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
160
+ schemathesis-3.39.13.dist-info/RECORD,,