schemathesis 3.15.4__py3-none-any.whl → 4.4.2__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.
Files changed (251) hide show
  1. schemathesis/__init__.py +53 -25
  2. schemathesis/auths.py +507 -0
  3. schemathesis/checks.py +190 -25
  4. schemathesis/cli/__init__.py +27 -1219
  5. schemathesis/cli/__main__.py +4 -0
  6. schemathesis/cli/commands/__init__.py +133 -0
  7. schemathesis/cli/commands/data.py +10 -0
  8. schemathesis/cli/commands/run/__init__.py +602 -0
  9. schemathesis/cli/commands/run/context.py +228 -0
  10. schemathesis/cli/commands/run/events.py +60 -0
  11. schemathesis/cli/commands/run/executor.py +157 -0
  12. schemathesis/cli/commands/run/filters.py +53 -0
  13. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  14. schemathesis/cli/commands/run/handlers/base.py +45 -0
  15. schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
  16. schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
  17. schemathesis/cli/commands/run/handlers/output.py +1750 -0
  18. schemathesis/cli/commands/run/loaders.py +118 -0
  19. schemathesis/cli/commands/run/validation.py +256 -0
  20. schemathesis/cli/constants.py +5 -0
  21. schemathesis/cli/core.py +19 -0
  22. schemathesis/cli/ext/fs.py +16 -0
  23. schemathesis/cli/ext/groups.py +203 -0
  24. schemathesis/cli/ext/options.py +81 -0
  25. schemathesis/config/__init__.py +202 -0
  26. schemathesis/config/_auth.py +51 -0
  27. schemathesis/config/_checks.py +268 -0
  28. schemathesis/config/_diff_base.py +101 -0
  29. schemathesis/config/_env.py +21 -0
  30. schemathesis/config/_error.py +163 -0
  31. schemathesis/config/_generation.py +157 -0
  32. schemathesis/config/_health_check.py +24 -0
  33. schemathesis/config/_operations.py +335 -0
  34. schemathesis/config/_output.py +171 -0
  35. schemathesis/config/_parameters.py +19 -0
  36. schemathesis/config/_phases.py +253 -0
  37. schemathesis/config/_projects.py +543 -0
  38. schemathesis/config/_rate_limit.py +17 -0
  39. schemathesis/config/_report.py +120 -0
  40. schemathesis/config/_validator.py +9 -0
  41. schemathesis/config/_warnings.py +89 -0
  42. schemathesis/config/schema.json +975 -0
  43. schemathesis/core/__init__.py +72 -0
  44. schemathesis/core/adapter.py +34 -0
  45. schemathesis/core/compat.py +32 -0
  46. schemathesis/core/control.py +2 -0
  47. schemathesis/core/curl.py +100 -0
  48. schemathesis/core/deserialization.py +210 -0
  49. schemathesis/core/errors.py +588 -0
  50. schemathesis/core/failures.py +316 -0
  51. schemathesis/core/fs.py +19 -0
  52. schemathesis/core/hooks.py +20 -0
  53. schemathesis/core/jsonschema/__init__.py +13 -0
  54. schemathesis/core/jsonschema/bundler.py +183 -0
  55. schemathesis/core/jsonschema/keywords.py +40 -0
  56. schemathesis/core/jsonschema/references.py +222 -0
  57. schemathesis/core/jsonschema/types.py +41 -0
  58. schemathesis/core/lazy_import.py +15 -0
  59. schemathesis/core/loaders.py +107 -0
  60. schemathesis/core/marks.py +66 -0
  61. schemathesis/core/media_types.py +79 -0
  62. schemathesis/core/output/__init__.py +46 -0
  63. schemathesis/core/output/sanitization.py +54 -0
  64. schemathesis/core/parameters.py +45 -0
  65. schemathesis/core/rate_limit.py +60 -0
  66. schemathesis/core/registries.py +34 -0
  67. schemathesis/core/result.py +27 -0
  68. schemathesis/core/schema_analysis.py +17 -0
  69. schemathesis/core/shell.py +203 -0
  70. schemathesis/core/transforms.py +144 -0
  71. schemathesis/core/transport.py +223 -0
  72. schemathesis/core/validation.py +73 -0
  73. schemathesis/core/version.py +7 -0
  74. schemathesis/engine/__init__.py +28 -0
  75. schemathesis/engine/context.py +152 -0
  76. schemathesis/engine/control.py +44 -0
  77. schemathesis/engine/core.py +201 -0
  78. schemathesis/engine/errors.py +446 -0
  79. schemathesis/engine/events.py +284 -0
  80. schemathesis/engine/observations.py +42 -0
  81. schemathesis/engine/phases/__init__.py +108 -0
  82. schemathesis/engine/phases/analysis.py +28 -0
  83. schemathesis/engine/phases/probes.py +172 -0
  84. schemathesis/engine/phases/stateful/__init__.py +68 -0
  85. schemathesis/engine/phases/stateful/_executor.py +364 -0
  86. schemathesis/engine/phases/stateful/context.py +85 -0
  87. schemathesis/engine/phases/unit/__init__.py +220 -0
  88. schemathesis/engine/phases/unit/_executor.py +459 -0
  89. schemathesis/engine/phases/unit/_pool.py +82 -0
  90. schemathesis/engine/recorder.py +254 -0
  91. schemathesis/errors.py +47 -0
  92. schemathesis/filters.py +395 -0
  93. schemathesis/generation/__init__.py +25 -0
  94. schemathesis/generation/case.py +478 -0
  95. schemathesis/generation/coverage.py +1528 -0
  96. schemathesis/generation/hypothesis/__init__.py +121 -0
  97. schemathesis/generation/hypothesis/builder.py +992 -0
  98. schemathesis/generation/hypothesis/examples.py +56 -0
  99. schemathesis/generation/hypothesis/given.py +66 -0
  100. schemathesis/generation/hypothesis/reporting.py +285 -0
  101. schemathesis/generation/meta.py +227 -0
  102. schemathesis/generation/metrics.py +93 -0
  103. schemathesis/generation/modes.py +20 -0
  104. schemathesis/generation/overrides.py +127 -0
  105. schemathesis/generation/stateful/__init__.py +37 -0
  106. schemathesis/generation/stateful/state_machine.py +294 -0
  107. schemathesis/graphql/__init__.py +15 -0
  108. schemathesis/graphql/checks.py +109 -0
  109. schemathesis/graphql/loaders.py +285 -0
  110. schemathesis/hooks.py +270 -91
  111. schemathesis/openapi/__init__.py +13 -0
  112. schemathesis/openapi/checks.py +467 -0
  113. schemathesis/openapi/generation/__init__.py +0 -0
  114. schemathesis/openapi/generation/filters.py +72 -0
  115. schemathesis/openapi/loaders.py +315 -0
  116. schemathesis/pytest/__init__.py +5 -0
  117. schemathesis/pytest/control_flow.py +7 -0
  118. schemathesis/pytest/lazy.py +341 -0
  119. schemathesis/pytest/loaders.py +36 -0
  120. schemathesis/pytest/plugin.py +357 -0
  121. schemathesis/python/__init__.py +0 -0
  122. schemathesis/python/asgi.py +12 -0
  123. schemathesis/python/wsgi.py +12 -0
  124. schemathesis/schemas.py +682 -257
  125. schemathesis/specs/graphql/__init__.py +0 -1
  126. schemathesis/specs/graphql/nodes.py +26 -2
  127. schemathesis/specs/graphql/scalars.py +77 -12
  128. schemathesis/specs/graphql/schemas.py +367 -148
  129. schemathesis/specs/graphql/validation.py +33 -0
  130. schemathesis/specs/openapi/__init__.py +9 -1
  131. schemathesis/specs/openapi/_hypothesis.py +555 -318
  132. schemathesis/specs/openapi/adapter/__init__.py +10 -0
  133. schemathesis/specs/openapi/adapter/parameters.py +729 -0
  134. schemathesis/specs/openapi/adapter/protocol.py +59 -0
  135. schemathesis/specs/openapi/adapter/references.py +19 -0
  136. schemathesis/specs/openapi/adapter/responses.py +368 -0
  137. schemathesis/specs/openapi/adapter/security.py +144 -0
  138. schemathesis/specs/openapi/adapter/v2.py +30 -0
  139. schemathesis/specs/openapi/adapter/v3_0.py +30 -0
  140. schemathesis/specs/openapi/adapter/v3_1.py +30 -0
  141. schemathesis/specs/openapi/analysis.py +96 -0
  142. schemathesis/specs/openapi/checks.py +748 -82
  143. schemathesis/specs/openapi/converter.py +176 -37
  144. schemathesis/specs/openapi/definitions.py +599 -4
  145. schemathesis/specs/openapi/examples.py +581 -165
  146. schemathesis/specs/openapi/expressions/__init__.py +52 -5
  147. schemathesis/specs/openapi/expressions/extractors.py +25 -0
  148. schemathesis/specs/openapi/expressions/lexer.py +34 -31
  149. schemathesis/specs/openapi/expressions/nodes.py +97 -46
  150. schemathesis/specs/openapi/expressions/parser.py +35 -13
  151. schemathesis/specs/openapi/formats.py +122 -0
  152. schemathesis/specs/openapi/media_types.py +75 -0
  153. schemathesis/specs/openapi/negative/__init__.py +93 -73
  154. schemathesis/specs/openapi/negative/mutations.py +294 -103
  155. schemathesis/specs/openapi/negative/utils.py +0 -9
  156. schemathesis/specs/openapi/patterns.py +458 -0
  157. schemathesis/specs/openapi/references.py +60 -81
  158. schemathesis/specs/openapi/schemas.py +647 -666
  159. schemathesis/specs/openapi/serialization.py +53 -30
  160. schemathesis/specs/openapi/stateful/__init__.py +403 -68
  161. schemathesis/specs/openapi/stateful/control.py +87 -0
  162. schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
  163. schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
  164. schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
  165. schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
  166. schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
  167. schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
  168. schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
  169. schemathesis/specs/openapi/stateful/inference.py +254 -0
  170. schemathesis/specs/openapi/stateful/links.py +219 -78
  171. schemathesis/specs/openapi/types/__init__.py +3 -0
  172. schemathesis/specs/openapi/types/common.py +23 -0
  173. schemathesis/specs/openapi/types/v2.py +129 -0
  174. schemathesis/specs/openapi/types/v3.py +134 -0
  175. schemathesis/specs/openapi/utils.py +7 -6
  176. schemathesis/specs/openapi/warnings.py +75 -0
  177. schemathesis/transport/__init__.py +224 -0
  178. schemathesis/transport/asgi.py +26 -0
  179. schemathesis/transport/prepare.py +126 -0
  180. schemathesis/transport/requests.py +278 -0
  181. schemathesis/transport/serialization.py +329 -0
  182. schemathesis/transport/wsgi.py +175 -0
  183. schemathesis-4.4.2.dist-info/METADATA +213 -0
  184. schemathesis-4.4.2.dist-info/RECORD +192 -0
  185. {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
  186. schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
  187. {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
  188. schemathesis/_compat.py +0 -57
  189. schemathesis/_hypothesis.py +0 -123
  190. schemathesis/auth.py +0 -214
  191. schemathesis/cli/callbacks.py +0 -240
  192. schemathesis/cli/cassettes.py +0 -351
  193. schemathesis/cli/context.py +0 -38
  194. schemathesis/cli/debug.py +0 -21
  195. schemathesis/cli/handlers.py +0 -11
  196. schemathesis/cli/junitxml.py +0 -41
  197. schemathesis/cli/options.py +0 -70
  198. schemathesis/cli/output/__init__.py +0 -1
  199. schemathesis/cli/output/default.py +0 -521
  200. schemathesis/cli/output/short.py +0 -40
  201. schemathesis/constants.py +0 -88
  202. schemathesis/exceptions.py +0 -257
  203. schemathesis/extra/_aiohttp.py +0 -27
  204. schemathesis/extra/_flask.py +0 -10
  205. schemathesis/extra/_server.py +0 -16
  206. schemathesis/extra/pytest_plugin.py +0 -251
  207. schemathesis/failures.py +0 -145
  208. schemathesis/fixups/__init__.py +0 -29
  209. schemathesis/fixups/fast_api.py +0 -30
  210. schemathesis/graphql.py +0 -5
  211. schemathesis/internal.py +0 -6
  212. schemathesis/lazy.py +0 -301
  213. schemathesis/models.py +0 -1113
  214. schemathesis/parameters.py +0 -91
  215. schemathesis/runner/__init__.py +0 -470
  216. schemathesis/runner/events.py +0 -242
  217. schemathesis/runner/impl/__init__.py +0 -3
  218. schemathesis/runner/impl/core.py +0 -791
  219. schemathesis/runner/impl/solo.py +0 -85
  220. schemathesis/runner/impl/threadpool.py +0 -367
  221. schemathesis/runner/serialization.py +0 -206
  222. schemathesis/serializers.py +0 -253
  223. schemathesis/service/__init__.py +0 -18
  224. schemathesis/service/auth.py +0 -10
  225. schemathesis/service/client.py +0 -62
  226. schemathesis/service/constants.py +0 -25
  227. schemathesis/service/events.py +0 -39
  228. schemathesis/service/handler.py +0 -46
  229. schemathesis/service/hosts.py +0 -74
  230. schemathesis/service/metadata.py +0 -42
  231. schemathesis/service/models.py +0 -21
  232. schemathesis/service/serialization.py +0 -184
  233. schemathesis/service/worker.py +0 -39
  234. schemathesis/specs/graphql/loaders.py +0 -215
  235. schemathesis/specs/openapi/constants.py +0 -7
  236. schemathesis/specs/openapi/expressions/context.py +0 -12
  237. schemathesis/specs/openapi/expressions/pointers.py +0 -29
  238. schemathesis/specs/openapi/filters.py +0 -44
  239. schemathesis/specs/openapi/links.py +0 -303
  240. schemathesis/specs/openapi/loaders.py +0 -453
  241. schemathesis/specs/openapi/parameters.py +0 -430
  242. schemathesis/specs/openapi/security.py +0 -129
  243. schemathesis/specs/openapi/validation.py +0 -24
  244. schemathesis/stateful.py +0 -358
  245. schemathesis/targets.py +0 -32
  246. schemathesis/types.py +0 -38
  247. schemathesis/utils.py +0 -475
  248. schemathesis-3.15.4.dist-info/METADATA +0 -202
  249. schemathesis-3.15.4.dist-info/RECORD +0 -99
  250. schemathesis-3.15.4.dist-info/entry_points.txt +0 -7
  251. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
schemathesis/utils.py DELETED
@@ -1,475 +0,0 @@
1
- import cgi
2
- import functools
3
- import pathlib
4
- import re
5
- import sys
6
- import traceback
7
- import warnings
8
- from contextlib import contextmanager
9
- from copy import copy, deepcopy
10
- from inspect import getfullargspec
11
- from json import JSONDecodeError
12
- from typing import (
13
- Any,
14
- Callable,
15
- Dict,
16
- Generator,
17
- Generic,
18
- List,
19
- NoReturn,
20
- Optional,
21
- Set,
22
- Tuple,
23
- Type,
24
- TypeVar,
25
- Union,
26
- overload,
27
- )
28
-
29
- import pytest
30
- import requests
31
- import yaml
32
- import yarl
33
- from hypothesis.core import is_invalid_test
34
- from hypothesis.reporting import with_reporter
35
- from hypothesis.strategies import SearchStrategy
36
- from requests.auth import HTTPDigestAuth
37
- from requests.exceptions import InvalidHeader # type: ignore
38
- from requests.utils import check_header_validity # type: ignore
39
- from werkzeug.wrappers import Response as BaseResponse
40
-
41
- from ._compat import InferType, JSONMixin
42
- from .constants import USER_AGENT, DataGenerationMethod
43
- from .exceptions import UsageError
44
- from .types import DataGenerationMethodInput, Filter, GenericTest, NotSet, RawAuth
45
-
46
- try:
47
- from yaml import CSafeLoader as SafeLoader
48
- except ImportError:
49
- # pylint: disable=unused-import
50
- from yaml import SafeLoader # type: ignore
51
-
52
-
53
- NOT_SET = NotSet()
54
-
55
-
56
- def file_exists(path: str) -> bool:
57
- try:
58
- return pathlib.Path(path).is_file()
59
- except OSError:
60
- # For example, path could be too long
61
- return False
62
-
63
-
64
- def is_latin_1_encodable(value: str) -> bool:
65
- """Header values are encoded to latin-1 before sending."""
66
- try:
67
- value.encode("latin-1")
68
- return True
69
- except UnicodeEncodeError:
70
- return False
71
-
72
-
73
- # Adapted from http.client._is_illegal_header_value
74
- INVALID_HEADER_RE = re.compile(r"\n(?![ \t])|\r(?![ \t\n])") # pragma: no mutate
75
-
76
-
77
- def has_invalid_characters(name: str, value: str) -> bool:
78
- try:
79
- check_header_validity((name, value))
80
- return bool(INVALID_HEADER_RE.search(value))
81
- except InvalidHeader:
82
- return True
83
-
84
-
85
- def is_schemathesis_test(func: Callable) -> bool:
86
- """Check whether test is parametrized with schemathesis."""
87
- try:
88
- from .schemas import BaseSchema # pylint: disable=import-outside-toplevel
89
-
90
- item = getattr(func, PARAMETRIZE_MARKER, None)
91
- # Comparison is needed to avoid false-positives when mocks are collected by pytest
92
- return isinstance(item, BaseSchema)
93
- except Exception:
94
- return False
95
-
96
-
97
- def fail_on_no_matches(node_id: str) -> NoReturn: # type: ignore
98
- pytest.fail(f"Test function {node_id} does not match any API operations and therefore has no effect")
99
-
100
-
101
- def force_tuple(item: Filter) -> Union[List, Set, Tuple]:
102
- if not isinstance(item, (list, set, tuple)):
103
- return (item,)
104
- return item
105
-
106
-
107
- def dict_true_values(**kwargs: Any) -> Dict[str, Any]:
108
- """Create a dict with given kwargs while skipping items where bool(value) evaluates to False."""
109
- return {key: value for key, value in kwargs.items() if bool(value)}
110
-
111
-
112
- def dict_not_none_values(**kwargs: Any) -> Dict[str, Any]:
113
- return {key: value for key, value in kwargs.items() if value is not None}
114
-
115
-
116
- IGNORED_PATTERNS = (
117
- "Falsifying example: ",
118
- "Falsifying explicit example: ",
119
- "You can add @seed",
120
- "Failed to reproduce exception. Expected:",
121
- "Flaky example!",
122
- "Traceback (most recent call last):",
123
- "You can reproduce this example by temporarily",
124
- "Unreliable test timings",
125
- )
126
-
127
-
128
- @contextmanager
129
- def capture_hypothesis_output() -> Generator[List[str], None, None]:
130
- """Capture all output of Hypothesis into a list of strings.
131
-
132
- It allows us to have more granular control over Schemathesis output.
133
-
134
- Usage::
135
-
136
- @given(i=st.integers())
137
- def test(i):
138
- assert 0
139
-
140
- with capture_hypothesis_output() as output:
141
- test() # hypothesis test
142
- # output == ["Falsifying example: test(i=0)"]
143
- """
144
- output = []
145
-
146
- def get_output(value: str) -> None:
147
- # Drop messages that could be confusing in the Schemathesis context
148
- if value.startswith(IGNORED_PATTERNS):
149
- return
150
- output.append(value)
151
-
152
- # the following context manager is untyped
153
- with with_reporter(get_output): # type: ignore
154
- yield output
155
-
156
-
157
- def format_exception(error: Exception, include_traceback: bool = False) -> str:
158
- """Format exception as text."""
159
- error_type = type(error)
160
- if include_traceback:
161
- lines = traceback.format_exception(error_type, error, error.__traceback__)
162
- else:
163
- lines = traceback.format_exception_only(error_type, error)
164
- return "".join(lines)
165
-
166
-
167
- def parse_content_type(content_type: str) -> Tuple[str, str]:
168
- """Parse Content Type and return main type and subtype."""
169
- try:
170
- content_type, _ = cgi.parse_header(content_type)
171
- main_type, sub_type = content_type.split("/", 1)
172
- except ValueError as exc:
173
- raise ValueError(f"Malformed media type: `{content_type}`") from exc
174
- return main_type.lower(), sub_type.lower()
175
-
176
-
177
- def is_json_media_type(value: str) -> bool:
178
- """Detect whether the content type is JSON-compatible.
179
-
180
- For example - ``application/problem+json`` matches.
181
- """
182
- main, sub = parse_content_type(value)
183
- return main == "application" and (sub == "json" or sub.endswith("+json"))
184
-
185
-
186
- def is_plain_text_media_type(value: str) -> bool:
187
- """Detect variations of the ``text/plain`` media type."""
188
- return parse_content_type(value) == ("text", "plain")
189
-
190
-
191
- def make_loader(*tags_to_remove: str) -> Type[yaml.SafeLoader]:
192
- """Create a YAML loader, that doesn't parse specific tokens into Python objects."""
193
- cls: Type[yaml.SafeLoader] = type("YAMLLoader", (SafeLoader,), {})
194
- cls.yaml_implicit_resolvers = {
195
- key: [(tag, regexp) for tag, regexp in mapping if tag not in tags_to_remove]
196
- for key, mapping in cls.yaml_implicit_resolvers.copy().items()
197
- }
198
-
199
- # Fix pyyaml scientific notation parse bug
200
- # See PR: https://github.com/yaml/pyyaml/pull/174 for upstream fix
201
- cls.add_implicit_resolver( # type: ignore
202
- "tag:yaml.org,2002:float",
203
- re.compile(
204
- r"""^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+]?[0-9]+)?
205
- |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
206
- |\.[0-9_]+(?:[eE][-+]?[0-9]+)?
207
- |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
208
- |[-+]?\.(?:inf|Inf|INF)
209
- |\.(?:nan|NaN|NAN))$""",
210
- re.X,
211
- ),
212
- list("-+0123456789."),
213
- )
214
-
215
- def construct_mapping(self: SafeLoader, node: yaml.Node, deep: bool = False) -> Dict[str, Any]:
216
- if isinstance(node, yaml.MappingNode):
217
- self.flatten_mapping(node) # type: ignore
218
- mapping = {}
219
- for key_node, value_node in node.value:
220
- # If the key has a tag different from `str` - use its string value.
221
- # With this change all integer keys or YAML 1.1 boolean-ish values like "on" / "off" will not be cast to
222
- # a different type
223
- if key_node.tag != "tag:yaml.org,2002:str":
224
- key = key_node.value
225
- else:
226
- key = self.construct_object(key_node, deep) # type: ignore
227
- mapping[key] = self.construct_object(value_node, deep) # type: ignore
228
- return mapping
229
-
230
- cls.construct_mapping = construct_mapping # type: ignore
231
- return cls
232
-
233
-
234
- StringDatesYAMLLoader = make_loader("tag:yaml.org,2002:timestamp")
235
-
236
-
237
- class WSGIResponse(BaseResponse, JSONMixin): # pylint: disable=too-many-ancestors
238
- # We store "requests" request to build a reproduction code
239
- request: requests.PreparedRequest
240
-
241
- def on_json_loading_failed(self, e: JSONDecodeError) -> NoReturn:
242
- # We don't need a werkzeug-specific exception when JSON parsing error happens
243
- raise e
244
-
245
-
246
- def get_requests_auth(auth: Optional[RawAuth], auth_type: Optional[str]) -> Optional[Union[HTTPDigestAuth, RawAuth]]:
247
- if auth and auth_type == "digest":
248
- return HTTPDigestAuth(*auth)
249
- return auth
250
-
251
-
252
- GenericResponse = Union[requests.Response, WSGIResponse] # pragma: no mutate
253
-
254
-
255
- def copy_response(response: GenericResponse) -> GenericResponse:
256
- """Create a copy of the given response as far as it makes sense."""
257
- if isinstance(response, requests.Response):
258
- copied_response = deepcopy(response)
259
- setattr(copied_response, "raw", response.raw)
260
- return copied_response
261
- # Can't deepcopy WSGI response due to generators inside (`response.freeze` doesn't completely help)
262
- response.freeze()
263
- copied_response = copy(response)
264
- copied_response.request = deepcopy(response.request)
265
- return copied_response
266
-
267
-
268
- def get_response_payload(response: GenericResponse) -> str:
269
- if isinstance(response, requests.Response):
270
- return response.text
271
- return response.get_data(as_text=True)
272
-
273
-
274
- def import_app(path: str) -> Any:
275
- """Import an application from a string."""
276
- path, name = (re.split(r":(?![\\/])", path, 1) + [""])[:2]
277
- __import__(path)
278
- # accessing the module from sys.modules returns a proper module, while `__import__`
279
- # may return a parent module (system dependent)
280
- module = sys.modules[path]
281
- return getattr(module, name)
282
-
283
-
284
- Schema = Union[Dict[str, Any], List, str, float, int]
285
-
286
-
287
- @overload
288
- def traverse_schema(schema: Dict[str, Any], callback: Callable, *args: Any, **kwargs: Any) -> Dict[str, Any]:
289
- pass
290
-
291
-
292
- @overload
293
- def traverse_schema(schema: List, callback: Callable, *args: Any, **kwargs: Any) -> List:
294
- pass
295
-
296
-
297
- @overload
298
- def traverse_schema(schema: str, callback: Callable, *args: Any, **kwargs: Any) -> str:
299
- pass
300
-
301
-
302
- @overload
303
- def traverse_schema(schema: float, callback: Callable, *args: Any, **kwargs: Any) -> float:
304
- pass
305
-
306
-
307
- def traverse_schema(schema: Schema, callback: Callable[..., Dict[str, Any]], *args: Any, **kwargs: Any) -> Schema:
308
- """Apply callback recursively to the given schema."""
309
- if isinstance(schema, dict):
310
- schema = callback(schema, *args, **kwargs)
311
- for key, sub_item in schema.items():
312
- schema[key] = traverse_schema(sub_item, callback, *args, **kwargs)
313
- elif isinstance(schema, list):
314
- schema = [traverse_schema(sub_item, callback, *args, **kwargs) for sub_item in schema]
315
- return schema
316
-
317
-
318
- def _warn_deprecation(*, thing: str, removed_in: str, replacement: str) -> None:
319
- warnings.warn(
320
- f"Property `{thing}` is deprecated and will be removed in Schemathesis {removed_in}. "
321
- f"Use `{replacement}` instead.",
322
- DeprecationWarning,
323
- )
324
-
325
-
326
- def deprecated_property(*, removed_in: str, replacement: str) -> Callable:
327
- def wrapper(prop: Callable) -> Callable:
328
- @property # type: ignore
329
- def inner(self: Any) -> Any:
330
- _warn_deprecation(thing=prop.__name__, removed_in=removed_in, replacement=replacement)
331
- return prop(self)
332
-
333
- return inner
334
-
335
- return wrapper
336
-
337
-
338
- def deprecated(*, removed_in: str, replacement: str) -> Callable:
339
- def wrapper(func: Callable) -> Callable:
340
- def inner(*args: Any, **kwargs: Any) -> Any:
341
- _warn_deprecation(thing=func.__name__, removed_in=removed_in, replacement=replacement)
342
- return func(*args, **kwargs)
343
-
344
- return inner
345
-
346
- return wrapper
347
-
348
-
349
- def setup_headers(kwargs: Dict[str, Any]) -> None:
350
- headers = kwargs.setdefault("headers", {})
351
- if "user-agent" not in {header.lower() for header in headers}:
352
- kwargs["headers"]["User-Agent"] = USER_AGENT
353
-
354
-
355
- def require_relative_url(url: str) -> None:
356
- """Raise an error if the URL is not relative."""
357
- if yarl.URL(url).is_absolute():
358
- raise ValueError("Schema path should be relative for WSGI/ASGI loaders")
359
-
360
-
361
- T = TypeVar("T")
362
- E = TypeVar("E", bound=Exception)
363
-
364
-
365
- class Ok(Generic[T]):
366
- __slots__ = ("_value",)
367
-
368
- def __init__(self, value: T):
369
- self._value = value
370
-
371
- def ok(self) -> T:
372
- return self._value
373
-
374
-
375
- class Err(Generic[E]):
376
- __slots__ = ("_error",)
377
-
378
- def __init__(self, error: E):
379
- self._error = error
380
-
381
- def err(self) -> E:
382
- return self._error
383
-
384
-
385
- Result = Union[Ok[T], Err[E]]
386
- GivenInput = Union[SearchStrategy, InferType]
387
- PARAMETRIZE_MARKER = "_schemathesis_test"
388
- GIVEN_ARGS_MARKER = "_schemathesis_given_args"
389
- GIVEN_KWARGS_MARKER = "_schemathesis_given_kwargs"
390
-
391
-
392
- def get_given_args(func: GenericTest) -> Tuple:
393
- return getattr(func, GIVEN_ARGS_MARKER, ())
394
-
395
-
396
- def get_given_kwargs(func: GenericTest) -> Dict[str, Any]:
397
- return getattr(func, GIVEN_KWARGS_MARKER, {})
398
-
399
-
400
- def is_given_applied(func: GenericTest) -> bool:
401
- return hasattr(func, GIVEN_ARGS_MARKER) or hasattr(func, GIVEN_KWARGS_MARKER)
402
-
403
-
404
- def given_proxy(*args: GivenInput, **kwargs: GivenInput) -> Callable[[GenericTest], GenericTest]:
405
- """Proxy Hypothesis strategies to ``hypothesis.given``."""
406
-
407
- def wrapper(func: GenericTest) -> GenericTest:
408
- if hasattr(func, GIVEN_ARGS_MARKER):
409
-
410
- def wrapped_test(*_: Any, **__: Any) -> NoReturn:
411
- raise UsageError(
412
- f"You have applied `given` to the `{func.__name__}` test more than once, which "
413
- "overrides the previous decorator. You need to pass all arguments to the same `given` call."
414
- )
415
-
416
- return wrapped_test
417
-
418
- setattr(func, GIVEN_ARGS_MARKER, args)
419
- setattr(func, GIVEN_KWARGS_MARKER, kwargs)
420
- return func
421
-
422
- return wrapper
423
-
424
-
425
- def merge_given_args(func: GenericTest, args: Tuple, kwargs: Dict[str, Any]) -> Dict[str, Any]:
426
- """Merge positional arguments to ``@schema.given`` into a dictionary with keyword arguments.
427
-
428
- Kwargs are modified inplace.
429
- """
430
- if args:
431
- argspec = getfullargspec(func)
432
- for name, strategy in zip(reversed([arg for arg in argspec.args if arg != "case"]), reversed(args)):
433
- kwargs[name] = strategy
434
- return kwargs
435
-
436
-
437
- def validate_given_args(func: GenericTest, args: Tuple, kwargs: Dict[str, Any]) -> Optional[Callable]:
438
- argspec = getfullargspec(func)
439
- return is_invalid_test(func, argspec, args, kwargs) # type: ignore
440
-
441
-
442
- def compose(*functions: Callable) -> Callable:
443
- """Compose multiple functions into a single one."""
444
-
445
- def noop(x: Any) -> Any:
446
- return x
447
-
448
- return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, noop)
449
-
450
-
451
- def maybe_set_assertion_message(exc: AssertionError, check_name: str) -> str:
452
- message = str(exc)
453
- if not message:
454
- message = f"Check '{check_name}' failed"
455
- exc.args = (message,)
456
- return message
457
-
458
-
459
- def prepare_data_generation_methods(data_generation_methods: DataGenerationMethodInput) -> List[DataGenerationMethod]:
460
- if isinstance(data_generation_methods, DataGenerationMethod):
461
- return [data_generation_methods]
462
- return list(data_generation_methods)
463
-
464
-
465
- def merge(a: Dict[str, Any], b: Dict[str, Any]) -> Dict[str, Any]:
466
- """Merge two dictionaries recursively."""
467
- for key in b:
468
- if key in a:
469
- if isinstance(a[key], dict) and isinstance(b[key], dict):
470
- merge(a[key], b[key])
471
- else:
472
- a[key] = b[key]
473
- else:
474
- a[key] = b[key]
475
- return a
@@ -1,202 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: schemathesis
3
- Version: 3.15.4
4
- Summary: Property-based testing framework for Open API and GraphQL based apps
5
- Home-page: https://github.com/schemathesis/schemathesis
6
- License: MIT
7
- Keywords: pytest,hypothesis,openapi,swagger,graphql,testing
8
- Author: Dmitry Dygalo
9
- Author-email: dadygalo@gmail.com
10
- Maintainer: Dmitry Dygalo
11
- Maintainer-email: dadygalo@gmail.com
12
- Requires-Python: >=3.7,<4.0
13
- Classifier: Development Status :: 5 - Production/Stable
14
- Classifier: Environment :: Console
15
- Classifier: Framework :: Hypothesis
16
- Classifier: Framework :: Pytest
17
- Classifier: Intended Audience :: Developers
18
- Classifier: License :: OSI Approved :: MIT License
19
- Classifier: Operating System :: OS Independent
20
- Classifier: Programming Language :: Python :: 3
21
- Classifier: Programming Language :: Python :: 3 :: Only
22
- Classifier: Programming Language :: Python :: 3.10
23
- Classifier: Programming Language :: Python :: 3.7
24
- Classifier: Programming Language :: Python :: 3.8
25
- Classifier: Programming Language :: Python :: 3.9
26
- Classifier: Programming Language :: Python :: Implementation :: CPython
27
- Classifier: Topic :: Software Development :: Testing
28
- Requires-Dist: PyYAML (>=5.1,<7.0)
29
- Requires-Dist: attrs (>=19.2,<=21.4)
30
- Requires-Dist: click (>=7.0,<9.0)
31
- Requires-Dist: colorama (>=0.4,<0.5)
32
- Requires-Dist: curlify (>=2.2.1,<3.0.0)
33
- Requires-Dist: hypothesis (>=6.13.3,<7.0.0)
34
- Requires-Dist: hypothesis_graphql (>=0.9.0,<0.10.0)
35
- Requires-Dist: hypothesis_jsonschema (>=0.22.0,<0.23.0)
36
- Requires-Dist: importlib_metadata (>=1.1,!=3.8,<5); python_version < "3.8"
37
- Requires-Dist: jsonschema (>=4.3.2,<5.0.0)
38
- Requires-Dist: junit-xml (>=1.9,<2.0)
39
- Requires-Dist: pytest (>4.6.4,<8)
40
- Requires-Dist: pytest-subtests (>=0.2.1,<0.8.0)
41
- Requires-Dist: requests (>=2.22,<2.28.0)
42
- Requires-Dist: starlette (>=0.13,<1)
43
- Requires-Dist: tomli (>=2.0.1,<3.0.0)
44
- Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
45
- Requires-Dist: typing-extensions (>=3.7,<5)
46
- Requires-Dist: werkzeug (>=0.16.0,<2.2)
47
- Requires-Dist: yarl (>=1.5,<2.0)
48
- Project-URL: Change Log, https://github.com/schemathesis/schemathesis/blob/master/docs/changelog.rst
49
- Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
50
- Project-URL: Funding, https://github.com/sponsors/Stranger6667
51
- Project-URL: Repository, https://github.com/schemathesis/schemathesis
52
- Project-URL: Source Code, https://github.com/schemathesis/schemathesis
53
- Description-Content-Type: text/x-rst
54
-
55
- Schemathesis
56
- ============
57
-
58
- |Build| |Coverage| |Version| |Python versions| |Docs| |Chat| |License|
59
-
60
- Schemathesis is a modern API testing tool for web applications built with Open API and GraphQL specifications.
61
-
62
- It reads the application schema and generates test cases, which will ensure that your application is compliant with its schema (read more about how it works in `our research paper <https://arxiv.org/abs/2112.10328>`_).
63
-
64
- The application under test could be written in any language; the only thing you need is a valid API schema in a supported format.
65
-
66
- Simple to use and yet powerful to uncover hard-to-find errors thanks to the property-based testing approach backed by state-of-the-art `Hypothesis <http://hypothesis.works/>`_ library.
67
-
68
- 🚀 Schemathesis is available as a `service <https://schemathesis.io/?utm_source=github>`_, standalone CLI, or a Python library 🚀
69
-
70
- The service enables you to verify your API schema in a few clicks, CLI gives more control.
71
- Schemathesis.io has a free tier, so you can combine the CLI flexibility with rich visuals by uploading your test results there.
72
-
73
- `Signup to reveal all issues on a dashboard <https://app.schemathesis.io/auth/sign-up/?utm_source=oss_readme>`_.
74
-
75
- Features
76
- --------
77
-
78
- - Content-Type, schema, and status code conformance checks for Open API;
79
- - Testing of explicit examples from the input schema;
80
- - Stateful testing via Open API links;
81
- - Concurrent test execution;
82
- - Targeted testing;
83
- - Storing and replaying network requests;
84
- - Built-in ASGI / WSGI application support;
85
- - Code samples for easy failure reproduction;
86
- - Ready-to-go Docker image;
87
- - Configurable with user-defined checks, string formats, hooks, and targets.
88
-
89
- CLI installation
90
- ----------------
91
-
92
- To install Schemathesis via ``pip`` run the following command:
93
-
94
- .. code:: bash
95
-
96
- pip install schemathesis
97
-
98
- This command installs the ``schemathesis`` entrypoint + ``st`` as its alias.
99
-
100
- You can also use our Docker image without installing Schemathesis as a Python package.
101
-
102
- Usage
103
- -----
104
-
105
- You can use Schemathesis in the command line directly (``st`` is an alias to ``schemathesis``):
106
-
107
- .. code:: bash
108
-
109
- st run --checks all https://example.schemathesis.io/openapi.json
110
-
111
- Or via Docker:
112
-
113
- .. code:: bash
114
-
115
- docker run schemathesis/schemathesis:stable \
116
- run --checks all https://example.schemathesis.io/openapi.json
117
-
118
- .. image:: https://raw.githubusercontent.com/schemathesis/schemathesis/master/img/schemathesis.gif
119
-
120
- Or in your Python tests:
121
-
122
- .. code:: python
123
-
124
- import schemathesis
125
-
126
- schema = schemathesis.from_uri("https://example.schemathesis.io/openapi.json")
127
-
128
-
129
- @schema.parametrize()
130
- def test_api(case):
131
- case.call_and_validate()
132
-
133
- CLI is simple to use and requires no coding; the in-code approach gives more flexibility.
134
-
135
- Both examples above will run hundreds of requests against the API under test and report all found failures and inconsistencies along with instructions to reproduce them.
136
-
137
- 💡 See a complete working example project in the ``/example`` directory. 💡
138
-
139
- Contributing
140
- ------------
141
-
142
- Any contribution to development, testing, or any other area is highly appreciated and useful to the project.
143
- For guidance on how to contribute to Schemathesis, see the `contributing guidelines <https://github.com/schemathesis/schemathesis/blob/master/CONTRIBUTING.rst>`_.
144
-
145
- Support this project
146
- --------------------
147
-
148
- Hi, my name is Dmitry! I started this project during my work at `Kiwi.com <https://kiwi.com/>`_. I am grateful to them for all the support they
149
- provided to this project during its early days and for the opportunity to evolve Schemathesis independently.
150
-
151
- In order to grow the community of contributors and users, and allow me to devote more time to this project, please `donate today <https://github.com/sponsors/Stranger6667>`_.
152
-
153
- Also, I occasionally write posts about Schemathesis in `my blog <https://dygalo.dev/>`_.
154
-
155
- Links
156
- -----
157
-
158
- - **Documentation**: https://schemathesis.readthedocs.io/en/stable/
159
- - **Releases**: https://pypi.org/project/schemathesis/
160
- - **Code**: https://github.com/schemathesis/schemathesis
161
- - **Issue tracker**: https://github.com/schemathesis/schemathesis/issues
162
- - **Chat**: https://discord.gg/R9ASRAmHnA
163
-
164
- Additional content:
165
-
166
- - Research paper: `Deriving Semantics-Aware Fuzzers from Web API Schemas <https://arxiv.org/abs/2112.10328>`_ by **@Zac-HD** and **@Stranger6667**
167
- - `An article <https://dygalo.dev/blog/schemathesis-property-based-testing-for-api-schemas/>`_ about Schemathesis by **@Stranger6667**
168
- - `Effective API schemas testing <https://youtu.be/VVLZ25JgjD4>`_ from DevConf.cz by **@Stranger6667**
169
- - `A video <https://www.youtube.com/watch?v=9FHRwrv-xuQ>`_ from EuroPython 2020 by **@hultner**
170
- - `Schemathesis tutorial <https://appdev.consulting.redhat.com/tracks/contract-first/automated-testing-with-schemathesis.html>`_ with an accompanying `video <https://www.youtube.com/watch?v=4r7OC-lBKMg>`_ by Red Hat
171
- - `Using Hypothesis and Schemathesis to Test FastAPI <https://testdriven.io/blog/fastapi-hypothesis/>`_ by **@amalshaji**
172
-
173
- Non-English content:
174
-
175
- - `A tutorial <https://habr.com/ru/company/oleg-bunin/blog/576496/>`_ (RUS) about Schemathesis by **@Stranger6667**
176
-
177
- License
178
- -------
179
-
180
- The code in this project is licensed under `MIT license`_.
181
- By contributing to Schemathesis, you agree that your contributions will be licensed under its MIT license.
182
-
183
- .. |Build| image:: https://github.com/schemathesis/schemathesis/workflows/build/badge.svg
184
- :target: https://github.com/schemathesis/schemathesis/actions
185
- .. |Coverage| image:: https://codecov.io/gh/schemathesis/schemathesis/branch/master/graph/badge.svg
186
- :target: https://codecov.io/gh/schemathesis/schemathesis/branch/master
187
- :alt: codecov.io status for master branch
188
- .. |Version| image:: https://img.shields.io/pypi/v/schemathesis.svg
189
- :target: https://pypi.org/project/schemathesis/
190
- .. |Python versions| image:: https://img.shields.io/pypi/pyversions/schemathesis.svg
191
- :target: https://pypi.org/project/schemathesis/
192
- .. |License| image:: https://img.shields.io/pypi/l/schemathesis.svg
193
- :target: https://opensource.org/licenses/MIT
194
- .. |Chat| image:: https://img.shields.io/discord/938139740912369755
195
- :target: https://discord.gg/R9ASRAmHnA
196
- :alt: Discord
197
- .. |Docs| image:: https://readthedocs.org/projects/schemathesis/badge/?version=stable
198
- :target: https://schemathesis.readthedocs.io/en/stable/?badge=stable
199
- :alt: Documentation Status
200
-
201
- .. _MIT license: https://opensource.org/licenses/MIT
202
-