schemathesis 3.27.0__py3-none-any.whl → 3.27.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
schemathesis/__init__.py CHANGED
@@ -3,7 +3,7 @@ from typing import Any
3
3
 
4
4
  from . import auths, checks, experimental, contrib, fixups, graphql, hooks, runner, serializers, targets # noqa: E402
5
5
  from ._lazy_import import lazy_import
6
- from .generation import DataGenerationMethod, GenerationConfig # noqa: E402
6
+ from .generation import DataGenerationMethod, GenerationConfig, HeaderConfig # noqa: E402
7
7
  from .constants import SCHEMATHESIS_VERSION # noqa: E402
8
8
  from .models import Case # noqa: E402
9
9
  from .specs import openapi # noqa: E402
@@ -0,0 +1,4 @@
1
+ if __name__ == "__main__":
2
+ import schemathesis.cli
3
+
4
+ schemathesis.cli.schemathesis()
@@ -1,8 +1,11 @@
1
1
  from __future__ import annotations
2
2
  import random
3
- from dataclasses import dataclass
3
+ from dataclasses import dataclass, field
4
4
  from enum import Enum
5
- from typing import Union, Iterable
5
+ from typing import Union, Iterable, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from hypothesis.strategies import SearchStrategy
6
9
 
7
10
 
8
11
  class DataGenerationMethod(str, Enum):
@@ -58,6 +61,13 @@ def generate_random_case_id(length: int = 6) -> str:
58
61
  return output
59
62
 
60
63
 
64
+ @dataclass
65
+ class HeaderConfig:
66
+ """Configuration for generating headers."""
67
+
68
+ strategy: SearchStrategy[str] | None = None
69
+
70
+
61
71
  @dataclass
62
72
  class GenerationConfig:
63
73
  """Holds various configuration options relevant for data generation."""
@@ -66,3 +76,5 @@ class GenerationConfig:
66
76
  allow_x00: bool = True
67
77
  # Generate strings using the given codec
68
78
  codec: str | None = "utf-8"
79
+ # Header generation configuration
80
+ headers: HeaderConfig = field(default_factory=HeaderConfig)
@@ -299,10 +299,7 @@ def run_probes(schema: BaseSchema, config: probes.ProbeConfig) -> list[probes.Pr
299
299
  if isinstance(result.probe, probes.NullByteInHeader) and result.is_failure:
300
300
  from ...specs.openapi._hypothesis import HEADER_FORMAT, header_values
301
301
 
302
- formats.register(
303
- HEADER_FORMAT,
304
- header_values(blacklist_characters="\n\r\x00").map(str.lstrip),
305
- )
302
+ formats.register(HEADER_FORMAT, header_values(blacklist_characters="\n\r\x00"))
306
303
  return results
307
304
 
308
305
 
@@ -45,7 +45,8 @@ StrategyFactory = Callable[[Dict[str, Any], str, str, Optional[str], GenerationC
45
45
  def header_values(blacklist_characters: str = "\n\r") -> st.SearchStrategy[str]:
46
46
  return st.text(
47
47
  alphabet=st.characters(min_codepoint=0, max_codepoint=255, blacklist_characters=blacklist_characters)
48
- )
48
+ # Header values with leading non-visible chars can't be sent with `requests`
49
+ ).map(str.lstrip)
49
50
 
50
51
 
51
52
  @lru_cache
@@ -67,8 +68,7 @@ def get_default_format_strategies() -> dict[str, st.SearchStrategy]:
67
68
  "_header_name": st.text(
68
69
  min_size=1, alphabet=st.sampled_from("!#$%&'*+-.^_`|~" + string.digits + string.ascii_letters)
69
70
  ),
70
- # Header values with leading non-visible chars can't be sent with `requests`
71
- HEADER_FORMAT: header_value.map(str.lstrip),
71
+ HEADER_FORMAT: header_value,
72
72
  "_basic_auth": st.tuples(latin1_text, latin1_text).map(make_basic_auth_str),
73
73
  "_bearer_auth": header_value.map("Bearer {}".format),
74
74
  }
@@ -425,6 +425,15 @@ def jsonify_python_specific_types(value: dict[str, Any]) -> dict[str, Any]:
425
425
  return value
426
426
 
427
427
 
428
+ def _build_custom_formats(
429
+ custom_formats: dict[str, st.SearchStrategy] | None, generation_config: GenerationConfig
430
+ ) -> dict[str, st.SearchStrategy]:
431
+ custom_formats = {**get_default_format_strategies(), **STRING_FORMATS, **(custom_formats or {})}
432
+ if generation_config.headers.strategy is not None:
433
+ custom_formats[HEADER_FORMAT] = generation_config.headers.strategy
434
+ return custom_formats
435
+
436
+
428
437
  def make_positive_strategy(
429
438
  schema: dict[str, Any],
430
439
  operation_name: str,
@@ -441,9 +450,10 @@ def make_positive_strategy(
441
450
  for sub_schema in schema.get("properties", {}).values():
442
451
  if list(sub_schema) == ["type"] and sub_schema["type"] == "string":
443
452
  sub_schema.setdefault("format", HEADER_FORMAT)
453
+ custom_formats = _build_custom_formats(custom_formats, generation_config)
444
454
  return from_schema(
445
455
  schema,
446
- custom_formats={**get_default_format_strategies(), **STRING_FORMATS, **(custom_formats or {})},
456
+ custom_formats=custom_formats,
447
457
  allow_x00=generation_config.allow_x00,
448
458
  codec=generation_config.codec,
449
459
  )
@@ -462,12 +472,13 @@ def make_negative_strategy(
462
472
  generation_config: GenerationConfig,
463
473
  custom_formats: dict[str, st.SearchStrategy] | None = None,
464
474
  ) -> st.SearchStrategy:
475
+ custom_formats = _build_custom_formats(custom_formats, generation_config)
465
476
  return negative_schema(
466
477
  schema,
467
478
  operation_name=operation_name,
468
479
  location=location,
469
480
  media_type=media_type,
470
- custom_formats={**get_default_format_strategies(), **STRING_FORMATS, **(custom_formats or {})},
481
+ custom_formats=custom_formats,
471
482
  generation_config=generation_config,
472
483
  )
473
484
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import base64
4
4
  import time
5
+ from inspect import iscoroutinefunction
5
6
  from contextlib import contextmanager
6
7
  from dataclasses import dataclass
7
8
  from datetime import timedelta
@@ -31,11 +32,11 @@ def serialize_payload(payload: bytes) -> str:
31
32
 
32
33
  def get(app: Any) -> Transport:
33
34
  """Get transport to send the data to the application."""
34
- from starlette.applications import Starlette
35
-
36
35
  if app is None:
37
36
  return RequestsTransport()
38
- if isinstance(app, Starlette):
37
+ if iscoroutinefunction(app) or (
38
+ hasattr(app, "__call__") and iscoroutinefunction(app.__call__) # noqa: B004
39
+ ):
39
40
  return ASGITransport(app=app)
40
41
  return WSGITransport(app=app)
41
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: schemathesis
3
- Version: 3.27.0
3
+ Version: 3.27.1
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
@@ -28,7 +28,6 @@ Classifier: Programming Language :: Python :: 3.12
28
28
  Classifier: Programming Language :: Python :: Implementation :: CPython
29
29
  Classifier: Topic :: Software Development :: Testing
30
30
  Requires-Python: >=3.8
31
- Requires-Dist: anyio<4
32
31
  Requires-Dist: backoff<3.0,>=2.1.2
33
32
  Requires-Dist: click<9.0,>=7.0
34
33
  Requires-Dist: colorama<1.0,>=0.4
@@ -44,7 +43,7 @@ Requires-Dist: pytest-subtests<0.8.0,>=0.2.1
44
43
  Requires-Dist: pytest<9,>=4.6.4
45
44
  Requires-Dist: pyyaml<7.0,>=5.1
46
45
  Requires-Dist: requests<3,>=2.22
47
- Requires-Dist: starlette-testclient<1
46
+ Requires-Dist: starlette-testclient<1,>=0.4.1
48
47
  Requires-Dist: starlette<1,>=0.13
49
48
  Requires-Dist: tomli-w<2.0,>=1.0.0
50
49
  Requires-Dist: tomli<3.0,>=2.0.1
@@ -1,4 +1,4 @@
1
- schemathesis/__init__.py,sha256=WW1NBZxmc5jRR0c24xz-ZtTkJMUQG5MOw1MGVRRGc1s,1970
1
+ schemathesis/__init__.py,sha256=zAa981OnVdIdjiETPP5a5vZL8QXAh2HbTWj8iEoxKC4,1984
2
2
  schemathesis/_compat.py,sha256=VizWR0QAVj4l7WZautNe_zmo3AJ7WBl2zrJQOfJAQtk,1837
3
3
  schemathesis/_dependency_versions.py,sha256=InIv6MZmuRVHc_9FxAiRg7_dY-vuF0jT69FBxrBLK6U,879
4
4
  schemathesis/_hypothesis.py,sha256=O3rfMbT0rChFONMUsYmMEGV9nPG5cHd_6v9NhWDI_IQ,10763
@@ -28,6 +28,7 @@ schemathesis/throttling.py,sha256=uwhL4XWPWAU8HECg0NhibfCMn5dT7NElTx3fdL3Mmcc,10
28
28
  schemathesis/types.py,sha256=AglR5M0bce-YXeDRkweToXTP0GjNOWVjS_mIsxLobwc,919
29
29
  schemathesis/utils.py,sha256=4HXvHysnHp-Uz2HfNgLfW5F5VjL-mtixrjjzRCEJhYo,5233
30
30
  schemathesis/cli/__init__.py,sha256=oTBqweV5CP681LqRFdRXtkd0VyYoG_xUKshkKEPnqIQ,64871
31
+ schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
31
32
  schemathesis/cli/callbacks.py,sha256=HmD0WmSYf5x8v4xeZdOKzy_8CEk23NitUlL8JQ7LHdQ,14950
32
33
  schemathesis/cli/cassettes.py,sha256=1ucgYyOZiNu44uOv044FdfKTzQolUyhAM2onk4m3MD0,12988
33
34
  schemathesis/cli/constants.py,sha256=XoezT0_fHuiOY2e--cmBUhNieJsepcUoW8e48QuLSDI,1544
@@ -56,7 +57,7 @@ schemathesis/extra/pytest_plugin.py,sha256=44RRkjkN1JupWu0muq9B5b3K9goucYndB394Q
56
57
  schemathesis/fixups/__init__.py,sha256=iTpOm-cwyZQRglbWRJHxj3k7WO9a4a-MCq-UDQa6U0Y,966
57
58
  schemathesis/fixups/fast_api.py,sha256=-gjYNSUtiKl7cKgyQ_rON0Oo_sGm6r7rOQDH0ahG32A,1322
58
59
  schemathesis/fixups/utf8_bom.py,sha256=ttzPOaJb6K7QtNAbIZ_TWrrm0N9FylS8QzO6zI0jXgY,764
59
- schemathesis/generation/__init__.py,sha256=NOmYWmbTt0uW5Vlx2-xfoXV_lLQs08d9T12v9pFBhrA,1951
60
+ schemathesis/generation/__init__.py,sha256=UXRrDTaUEX29VEK9bLqVi_xVzyNNqul3WhpLh1za90M,2277
60
61
  schemathesis/internal/__init__.py,sha256=93HcdG3LF0BbQKbCteOsFMa1w6nXl8yTmx87QLNJOik,161
61
62
  schemathesis/internal/copy.py,sha256=xsC7RyrFa9KadMsj9ai_aAy0XZg0oplz3cFZCka_2fg,459
62
63
  schemathesis/internal/datetime.py,sha256=zPLBL0XXLNfP-KYel3H2m8pnsxjsA_4d-zTOhJg2EPQ,136
@@ -70,7 +71,7 @@ schemathesis/runner/events.py,sha256=eZc4TT8C8mWCMr9h4JvlijvQQ-x8wFry8LntdsYIL-A
70
71
  schemathesis/runner/probes.py,sha256=t_B38-ljy-p3Odw-dqcZUVIjYTPwBvac8KE5GaLnz4M,5546
71
72
  schemathesis/runner/serialization.py,sha256=7DBve7E6JAbAHH5npzjME7lFnysKCc9dPXMmdIIYrNI,16667
72
73
  schemathesis/runner/impl/__init__.py,sha256=1E2iME8uthYPBh9MjwVBCTFV-P3fi7AdphCCoBBspjs,199
73
- schemathesis/runner/impl/core.py,sha256=w0Ky7DuOQD3qpMPBP2E5Pr04d5N_eKyKNyCVZAEEZJc,39684
74
+ schemathesis/runner/impl/core.py,sha256=Y8WyKEUeuEs0xi9hWmEA63a39-NO7L6I5hQRhwo9JBQ,39621
74
75
  schemathesis/runner/impl/solo.py,sha256=Y_tNhVBVxcuxv65hN0FjxLlVSC41ebHMOM1xSzVrNk8,3358
75
76
  schemathesis/runner/impl/threadpool.py,sha256=2-2Wvw7msezZtenZY5vU_x3FGLLVlH-ywvhU9hTwAAo,15073
76
77
  schemathesis/service/__init__.py,sha256=cDVTCFD1G-vvhxZkJUwiToTAEQ-0ByIoqwXvJBCf_V8,472
@@ -94,7 +95,7 @@ schemathesis/specs/graphql/scalars.py,sha256=W5oj6AcjiXpR-Z6eSSp1oPWl0mjH2NF-w87
94
95
  schemathesis/specs/graphql/schemas.py,sha256=6BlTF9iAongsDxzy4_l85YjDob2QzpB0Vt7mlbIUxaw,12637
95
96
  schemathesis/specs/graphql/validation.py,sha256=SqQbj9uymGUQxlHXc8HkQccIq25uwP5CvLF1zReb1Xg,1636
96
97
  schemathesis/specs/openapi/__init__.py,sha256=HDcx3bqpa6qWPpyMrxAbM3uTo0Lqpg-BUNZhDJSJKnw,279
97
- schemathesis/specs/openapi/_hypothesis.py,sha256=o1ssS7JJZ45l750ogoZ3gXoedzLmRsXqpPb1LCZXikY,22835
98
+ schemathesis/specs/openapi/_hypothesis.py,sha256=Qrm6Po2W2wsxZAlLFRUwq0svO8SmQp-5InCFa2cwYaw,23275
98
99
  schemathesis/specs/openapi/checks.py,sha256=1WB_UGNqptfJThWLUNbds1Q-IzOGbbHCOYPIhBYk-zs,5411
99
100
  schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
100
101
  schemathesis/specs/openapi/converter.py,sha256=9TKeKvNi9MVvoNMfqoPz_cODO8oNrMSTXTOwLLfjD_Q,2799
@@ -126,13 +127,13 @@ schemathesis/specs/openapi/stateful/__init__.py,sha256=R8RhPJD3rDzqL4eA9jSnUwh9Q
126
127
  schemathesis/specs/openapi/stateful/links.py,sha256=cSIwho2Hroty6au-gyCD-OwqnuCcIpnIIHU6FvF0SwA,3533
127
128
  schemathesis/stateful/__init__.py,sha256=T7rvhzftfl3wumEsir33DBBzCTK2PtRp9CxBxMLdMSE,4693
128
129
  schemathesis/stateful/state_machine.py,sha256=ZcKpbvEl1QGhVOYnA7Ow6zkuFHtEPDAyCjroPrj-FgU,11343
129
- schemathesis/transports/__init__.py,sha256=YEbD9etlUf6uR_9H8SO01uq2nJ2WBRv30k2pxolOwtE,11203
130
+ schemathesis/transports/__init__.py,sha256=IHQaob4pLHPP0zXXJukWEQBpV4aSIhGPHHMm0up8x64,11287
130
131
  schemathesis/transports/auth.py,sha256=ZKFku9gjhIG6445qNC2p_64Yt9Iz_4azbvja8AMptBk,404
131
132
  schemathesis/transports/content_types.py,sha256=xU8RZWxz-CyWRqQTI2fGYQacB7KasoY7LL_bxPQdyPY,2144
132
133
  schemathesis/transports/headers.py,sha256=EDxpm8su0AuhyqZUkMex-OFZMAJN_5NHah7fDT2HDZE,989
133
134
  schemathesis/transports/responses.py,sha256=j_-udvWbmi6XtEYmpdX8WoFnlrQ6-i3PSBizfozRjQI,1673
134
- schemathesis-3.27.0.dist-info/METADATA,sha256=wVeeZfcbs3AdGnpqcsHGrfx4iYX9gIV8kYgY_HW01Ps,16257
135
- schemathesis-3.27.0.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
136
- schemathesis-3.27.0.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
137
- schemathesis-3.27.0.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
138
- schemathesis-3.27.0.dist-info/RECORD,,
135
+ schemathesis-3.27.1.dist-info/METADATA,sha256=GtcBreI2a4R9b3KL_eS-QQb57LTmQckmsQ_vkPTIdDs,16242
136
+ schemathesis-3.27.1.dist-info/WHEEL,sha256=hKi7AIIx6qfnsRbr087vpeJnrVUuDokDHZacPPMW7-Y,87
137
+ schemathesis-3.27.1.dist-info/entry_points.txt,sha256=VHyLcOG7co0nOeuk8WjgpRETk5P1E2iCLrn26Zkn5uk,158
138
+ schemathesis-3.27.1.dist-info/licenses/LICENSE,sha256=PsPYgrDhZ7g9uwihJXNG-XVb55wj2uYhkl2DD8oAzY0,1103
139
+ schemathesis-3.27.1.dist-info/RECORD,,