schemathesis 3.39.16__py3-none-any.whl → 4.0.0__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 (255) hide show
  1. schemathesis/__init__.py +41 -79
  2. schemathesis/auths.py +111 -122
  3. schemathesis/checks.py +169 -60
  4. schemathesis/cli/__init__.py +15 -2117
  5. schemathesis/cli/commands/__init__.py +85 -0
  6. schemathesis/cli/commands/data.py +10 -0
  7. schemathesis/cli/commands/run/__init__.py +590 -0
  8. schemathesis/cli/commands/run/context.py +204 -0
  9. schemathesis/cli/commands/run/events.py +60 -0
  10. schemathesis/cli/commands/run/executor.py +157 -0
  11. schemathesis/cli/commands/run/filters.py +53 -0
  12. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  13. schemathesis/cli/commands/run/handlers/base.py +18 -0
  14. schemathesis/cli/commands/run/handlers/cassettes.py +474 -0
  15. schemathesis/cli/commands/run/handlers/junitxml.py +55 -0
  16. schemathesis/cli/commands/run/handlers/output.py +1628 -0
  17. schemathesis/cli/commands/run/loaders.py +114 -0
  18. schemathesis/cli/commands/run/validation.py +246 -0
  19. schemathesis/cli/constants.py +5 -58
  20. schemathesis/cli/core.py +19 -0
  21. schemathesis/cli/ext/fs.py +16 -0
  22. schemathesis/cli/ext/groups.py +84 -0
  23. schemathesis/cli/{options.py → ext/options.py} +36 -34
  24. schemathesis/config/__init__.py +189 -0
  25. schemathesis/config/_auth.py +51 -0
  26. schemathesis/config/_checks.py +268 -0
  27. schemathesis/config/_diff_base.py +99 -0
  28. schemathesis/config/_env.py +21 -0
  29. schemathesis/config/_error.py +156 -0
  30. schemathesis/config/_generation.py +149 -0
  31. schemathesis/config/_health_check.py +24 -0
  32. schemathesis/config/_operations.py +327 -0
  33. schemathesis/config/_output.py +171 -0
  34. schemathesis/config/_parameters.py +19 -0
  35. schemathesis/config/_phases.py +187 -0
  36. schemathesis/config/_projects.py +527 -0
  37. schemathesis/config/_rate_limit.py +17 -0
  38. schemathesis/config/_report.py +120 -0
  39. schemathesis/config/_validator.py +9 -0
  40. schemathesis/config/_warnings.py +25 -0
  41. schemathesis/config/schema.json +885 -0
  42. schemathesis/core/__init__.py +67 -0
  43. schemathesis/core/compat.py +32 -0
  44. schemathesis/core/control.py +2 -0
  45. schemathesis/core/curl.py +58 -0
  46. schemathesis/core/deserialization.py +65 -0
  47. schemathesis/core/errors.py +459 -0
  48. schemathesis/core/failures.py +315 -0
  49. schemathesis/core/fs.py +19 -0
  50. schemathesis/core/hooks.py +20 -0
  51. schemathesis/core/loaders.py +104 -0
  52. schemathesis/core/marks.py +66 -0
  53. schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
  54. schemathesis/core/output/__init__.py +46 -0
  55. schemathesis/core/output/sanitization.py +54 -0
  56. schemathesis/{throttling.py → core/rate_limit.py} +16 -17
  57. schemathesis/core/registries.py +31 -0
  58. schemathesis/core/transforms.py +113 -0
  59. schemathesis/core/transport.py +223 -0
  60. schemathesis/core/validation.py +54 -0
  61. schemathesis/core/version.py +7 -0
  62. schemathesis/engine/__init__.py +28 -0
  63. schemathesis/engine/context.py +118 -0
  64. schemathesis/engine/control.py +36 -0
  65. schemathesis/engine/core.py +169 -0
  66. schemathesis/engine/errors.py +464 -0
  67. schemathesis/engine/events.py +258 -0
  68. schemathesis/engine/phases/__init__.py +88 -0
  69. schemathesis/{runner → engine/phases}/probes.py +52 -68
  70. schemathesis/engine/phases/stateful/__init__.py +68 -0
  71. schemathesis/engine/phases/stateful/_executor.py +356 -0
  72. schemathesis/engine/phases/stateful/context.py +85 -0
  73. schemathesis/engine/phases/unit/__init__.py +212 -0
  74. schemathesis/engine/phases/unit/_executor.py +416 -0
  75. schemathesis/engine/phases/unit/_pool.py +82 -0
  76. schemathesis/engine/recorder.py +247 -0
  77. schemathesis/errors.py +43 -0
  78. schemathesis/filters.py +17 -98
  79. schemathesis/generation/__init__.py +5 -33
  80. schemathesis/generation/case.py +317 -0
  81. schemathesis/generation/coverage.py +282 -175
  82. schemathesis/generation/hypothesis/__init__.py +36 -0
  83. schemathesis/generation/hypothesis/builder.py +800 -0
  84. schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
  85. schemathesis/generation/hypothesis/given.py +66 -0
  86. schemathesis/generation/hypothesis/reporting.py +14 -0
  87. schemathesis/generation/hypothesis/strategies.py +16 -0
  88. schemathesis/generation/meta.py +115 -0
  89. schemathesis/generation/metrics.py +93 -0
  90. schemathesis/generation/modes.py +20 -0
  91. schemathesis/generation/overrides.py +116 -0
  92. schemathesis/generation/stateful/__init__.py +37 -0
  93. schemathesis/generation/stateful/state_machine.py +278 -0
  94. schemathesis/graphql/__init__.py +15 -0
  95. schemathesis/graphql/checks.py +109 -0
  96. schemathesis/graphql/loaders.py +284 -0
  97. schemathesis/hooks.py +80 -101
  98. schemathesis/openapi/__init__.py +13 -0
  99. schemathesis/openapi/checks.py +455 -0
  100. schemathesis/openapi/generation/__init__.py +0 -0
  101. schemathesis/openapi/generation/filters.py +72 -0
  102. schemathesis/openapi/loaders.py +313 -0
  103. schemathesis/pytest/__init__.py +5 -0
  104. schemathesis/pytest/control_flow.py +7 -0
  105. schemathesis/pytest/lazy.py +281 -0
  106. schemathesis/pytest/loaders.py +36 -0
  107. schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +128 -108
  108. schemathesis/python/__init__.py +0 -0
  109. schemathesis/python/asgi.py +12 -0
  110. schemathesis/python/wsgi.py +12 -0
  111. schemathesis/schemas.py +537 -273
  112. schemathesis/specs/graphql/__init__.py +0 -1
  113. schemathesis/specs/graphql/_cache.py +1 -2
  114. schemathesis/specs/graphql/scalars.py +42 -6
  115. schemathesis/specs/graphql/schemas.py +141 -137
  116. schemathesis/specs/graphql/validation.py +11 -17
  117. schemathesis/specs/openapi/__init__.py +6 -1
  118. schemathesis/specs/openapi/_cache.py +1 -2
  119. schemathesis/specs/openapi/_hypothesis.py +142 -156
  120. schemathesis/specs/openapi/checks.py +368 -257
  121. schemathesis/specs/openapi/converter.py +4 -4
  122. schemathesis/specs/openapi/definitions.py +1 -1
  123. schemathesis/specs/openapi/examples.py +23 -21
  124. schemathesis/specs/openapi/expressions/__init__.py +31 -19
  125. schemathesis/specs/openapi/expressions/extractors.py +1 -4
  126. schemathesis/specs/openapi/expressions/lexer.py +1 -1
  127. schemathesis/specs/openapi/expressions/nodes.py +36 -41
  128. schemathesis/specs/openapi/expressions/parser.py +1 -1
  129. schemathesis/specs/openapi/formats.py +35 -7
  130. schemathesis/specs/openapi/media_types.py +53 -12
  131. schemathesis/specs/openapi/negative/__init__.py +7 -4
  132. schemathesis/specs/openapi/negative/mutations.py +6 -5
  133. schemathesis/specs/openapi/parameters.py +7 -10
  134. schemathesis/specs/openapi/patterns.py +94 -31
  135. schemathesis/specs/openapi/references.py +12 -53
  136. schemathesis/specs/openapi/schemas.py +233 -307
  137. schemathesis/specs/openapi/security.py +1 -1
  138. schemathesis/specs/openapi/serialization.py +12 -6
  139. schemathesis/specs/openapi/stateful/__init__.py +268 -133
  140. schemathesis/specs/openapi/stateful/control.py +87 -0
  141. schemathesis/specs/openapi/stateful/links.py +209 -0
  142. schemathesis/transport/__init__.py +142 -0
  143. schemathesis/transport/asgi.py +26 -0
  144. schemathesis/transport/prepare.py +124 -0
  145. schemathesis/transport/requests.py +244 -0
  146. schemathesis/{_xml.py → transport/serialization.py} +69 -11
  147. schemathesis/transport/wsgi.py +171 -0
  148. schemathesis-4.0.0.dist-info/METADATA +204 -0
  149. schemathesis-4.0.0.dist-info/RECORD +164 -0
  150. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/entry_points.txt +1 -1
  151. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/licenses/LICENSE +1 -1
  152. schemathesis/_compat.py +0 -74
  153. schemathesis/_dependency_versions.py +0 -19
  154. schemathesis/_hypothesis.py +0 -717
  155. schemathesis/_override.py +0 -50
  156. schemathesis/_patches.py +0 -21
  157. schemathesis/_rate_limiter.py +0 -7
  158. schemathesis/cli/callbacks.py +0 -466
  159. schemathesis/cli/cassettes.py +0 -561
  160. schemathesis/cli/context.py +0 -75
  161. schemathesis/cli/debug.py +0 -27
  162. schemathesis/cli/handlers.py +0 -19
  163. schemathesis/cli/junitxml.py +0 -124
  164. schemathesis/cli/output/__init__.py +0 -1
  165. schemathesis/cli/output/default.py +0 -920
  166. schemathesis/cli/output/short.py +0 -59
  167. schemathesis/cli/reporting.py +0 -79
  168. schemathesis/cli/sanitization.py +0 -26
  169. schemathesis/code_samples.py +0 -151
  170. schemathesis/constants.py +0 -54
  171. schemathesis/contrib/__init__.py +0 -11
  172. schemathesis/contrib/openapi/__init__.py +0 -11
  173. schemathesis/contrib/openapi/fill_missing_examples.py +0 -24
  174. schemathesis/contrib/openapi/formats/__init__.py +0 -9
  175. schemathesis/contrib/openapi/formats/uuid.py +0 -16
  176. schemathesis/contrib/unique_data.py +0 -41
  177. schemathesis/exceptions.py +0 -571
  178. schemathesis/experimental/__init__.py +0 -109
  179. schemathesis/extra/_aiohttp.py +0 -28
  180. schemathesis/extra/_flask.py +0 -13
  181. schemathesis/extra/_server.py +0 -18
  182. schemathesis/failures.py +0 -284
  183. schemathesis/fixups/__init__.py +0 -37
  184. schemathesis/fixups/fast_api.py +0 -41
  185. schemathesis/fixups/utf8_bom.py +0 -28
  186. schemathesis/generation/_methods.py +0 -44
  187. schemathesis/graphql.py +0 -3
  188. schemathesis/internal/__init__.py +0 -7
  189. schemathesis/internal/checks.py +0 -86
  190. schemathesis/internal/copy.py +0 -32
  191. schemathesis/internal/datetime.py +0 -5
  192. schemathesis/internal/deprecation.py +0 -37
  193. schemathesis/internal/diff.py +0 -15
  194. schemathesis/internal/extensions.py +0 -27
  195. schemathesis/internal/jsonschema.py +0 -36
  196. schemathesis/internal/output.py +0 -68
  197. schemathesis/internal/transformation.py +0 -26
  198. schemathesis/internal/validation.py +0 -34
  199. schemathesis/lazy.py +0 -474
  200. schemathesis/loaders.py +0 -122
  201. schemathesis/models.py +0 -1341
  202. schemathesis/parameters.py +0 -90
  203. schemathesis/runner/__init__.py +0 -605
  204. schemathesis/runner/events.py +0 -389
  205. schemathesis/runner/impl/__init__.py +0 -3
  206. schemathesis/runner/impl/context.py +0 -88
  207. schemathesis/runner/impl/core.py +0 -1280
  208. schemathesis/runner/impl/solo.py +0 -80
  209. schemathesis/runner/impl/threadpool.py +0 -391
  210. schemathesis/runner/serialization.py +0 -544
  211. schemathesis/sanitization.py +0 -252
  212. schemathesis/serializers.py +0 -328
  213. schemathesis/service/__init__.py +0 -18
  214. schemathesis/service/auth.py +0 -11
  215. schemathesis/service/ci.py +0 -202
  216. schemathesis/service/client.py +0 -133
  217. schemathesis/service/constants.py +0 -38
  218. schemathesis/service/events.py +0 -61
  219. schemathesis/service/extensions.py +0 -224
  220. schemathesis/service/hosts.py +0 -111
  221. schemathesis/service/metadata.py +0 -71
  222. schemathesis/service/models.py +0 -258
  223. schemathesis/service/report.py +0 -255
  224. schemathesis/service/serialization.py +0 -173
  225. schemathesis/service/usage.py +0 -66
  226. schemathesis/specs/graphql/loaders.py +0 -364
  227. schemathesis/specs/openapi/expressions/context.py +0 -16
  228. schemathesis/specs/openapi/links.py +0 -389
  229. schemathesis/specs/openapi/loaders.py +0 -707
  230. schemathesis/specs/openapi/stateful/statistic.py +0 -198
  231. schemathesis/specs/openapi/stateful/types.py +0 -14
  232. schemathesis/specs/openapi/validation.py +0 -26
  233. schemathesis/stateful/__init__.py +0 -147
  234. schemathesis/stateful/config.py +0 -97
  235. schemathesis/stateful/context.py +0 -135
  236. schemathesis/stateful/events.py +0 -274
  237. schemathesis/stateful/runner.py +0 -309
  238. schemathesis/stateful/sink.py +0 -68
  239. schemathesis/stateful/state_machine.py +0 -328
  240. schemathesis/stateful/statistic.py +0 -22
  241. schemathesis/stateful/validation.py +0 -100
  242. schemathesis/targets.py +0 -77
  243. schemathesis/transports/__init__.py +0 -369
  244. schemathesis/transports/asgi.py +0 -7
  245. schemathesis/transports/auth.py +0 -38
  246. schemathesis/transports/headers.py +0 -36
  247. schemathesis/transports/responses.py +0 -57
  248. schemathesis/types.py +0 -44
  249. schemathesis/utils.py +0 -164
  250. schemathesis-3.39.16.dist-info/METADATA +0 -293
  251. schemathesis-3.39.16.dist-info/RECORD +0 -160
  252. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
  253. /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
  254. /schemathesis/{internal → core}/result.py +0 -0
  255. {schemathesis-3.39.16.dist-info → schemathesis-4.0.0.dist-info}/WHEEL +0 -0
@@ -1,59 +0,0 @@
1
- import click
2
-
3
- from ...runner import events
4
- from ...stateful import events as stateful_events
5
- from ..context import ExecutionContext
6
- from ..handlers import EventHandler
7
- from . import default
8
-
9
-
10
- def handle_before_execution(context: ExecutionContext, event: events.BeforeExecution) -> None:
11
- if event.recursion_level > 0:
12
- context.operations_count += 1 # type: ignore
13
-
14
-
15
- def handle_after_execution(context: ExecutionContext, event: events.AfterExecution) -> None:
16
- context.operations_processed += 1
17
- context.results.append(event.result)
18
- context.hypothesis_output.extend(event.hypothesis_output)
19
- default.display_execution_result(context, event.status.value)
20
-
21
-
22
- def handle_stateful_event(context: ExecutionContext, event: events.StatefulEvent) -> None:
23
- if isinstance(event.data, stateful_events.RunStarted):
24
- click.echo()
25
- default.handle_stateful_event(context, event)
26
-
27
-
28
- class ShortOutputStyleHandler(EventHandler):
29
- def handle_event(self, context: ExecutionContext, event: events.ExecutionEvent) -> None:
30
- """Short output style shows single symbols in the progress bar.
31
-
32
- Otherwise, identical to the default output style.
33
- """
34
- if isinstance(event, events.Initialized):
35
- default.handle_initialized(context, event)
36
- elif isinstance(event, events.BeforeProbing):
37
- default.handle_before_probing(context, event)
38
- elif isinstance(event, events.AfterProbing):
39
- default.handle_after_probing(context, event)
40
- elif isinstance(event, events.BeforeAnalysis):
41
- default.handle_before_analysis(context, event)
42
- elif isinstance(event, events.AfterAnalysis):
43
- default.handle_after_analysis(context, event)
44
- elif isinstance(event, events.BeforeExecution):
45
- handle_before_execution(context, event)
46
- elif isinstance(event, events.AfterExecution):
47
- handle_after_execution(context, event)
48
- elif isinstance(event, events.Finished):
49
- if context.operations_count == context.operations_processed:
50
- click.echo()
51
- default.handle_finished(context, event)
52
- elif isinstance(event, events.Interrupted):
53
- default.handle_interrupted(context, event)
54
- elif isinstance(event, events.InternalError):
55
- default.handle_internal_error(context, event)
56
- elif isinstance(event, events.StatefulEvent):
57
- handle_stateful_event(context, event)
58
- elif isinstance(event, events.AfterStatefulExecution):
59
- default.handle_after_stateful_execution(context, event)
@@ -1,79 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from itertools import groupby
4
- from typing import TYPE_CHECKING, Callable, Generator, Iterator
5
-
6
- import click
7
-
8
- from ..exceptions import RuntimeErrorType
9
- from ..runner.serialization import SerializedCheck, deduplicate_failures
10
-
11
- if TYPE_CHECKING:
12
- from ..code_samples import CodeSampleStyle
13
-
14
- TEST_CASE_ID_TITLE = "Test Case ID"
15
-
16
-
17
- def group_by_case(
18
- checks: list[SerializedCheck], code_sample_style: CodeSampleStyle
19
- ) -> Generator[tuple[str, Iterator[SerializedCheck]], None, None]:
20
- checks = deduplicate_failures(checks)
21
- checks = sorted(checks, key=lambda c: _by_unique_key(c, code_sample_style))
22
- for (sample, _, _), gen in groupby(checks, lambda c: _by_unique_key(c, code_sample_style)):
23
- yield (sample, gen)
24
-
25
-
26
- def _by_unique_key(check: SerializedCheck, code_sample_style: CodeSampleStyle) -> tuple[str, int, str]:
27
- return (
28
- code_sample_style.generate(
29
- method=check.example.method,
30
- url=check.example.url,
31
- body=check.example.deserialize_body(),
32
- headers=check.example.headers,
33
- verify=check.example.verify,
34
- extra_headers=check.example.extra_headers,
35
- ),
36
- 0 if not check.response else check.response.status_code,
37
- "SCHEMATHESIS-INTERNAL-NO-RESPONSE"
38
- if not check.response
39
- else check.response.body or "SCHEMATHESIS-INTERNAL-EMPTY-BODY",
40
- )
41
-
42
-
43
- def split_traceback(traceback: str) -> list[str]:
44
- return [entry for entry in traceback.splitlines() if entry]
45
-
46
-
47
- def bold(option: str) -> str:
48
- return click.style(option, bold=True)
49
-
50
-
51
- def get_runtime_error_suggestion(error_type: RuntimeErrorType, bold: Callable[[str], str] = bold) -> str | None:
52
- DISABLE_SSL_SUGGESTION = f"Bypass SSL verification with {bold('`--request-tls-verify=false`')}."
53
- DISABLE_SCHEMA_VALIDATION_SUGGESTION = (
54
- f"Bypass validation using {bold('`--validate-schema=false`')}. Caution: May cause unexpected errors."
55
- )
56
-
57
- def _format_health_check_suggestion(label: str) -> str:
58
- return f"Bypass this health check using {bold(f'`--hypothesis-suppress-health-check={label}`')}."
59
-
60
- RUNTIME_ERROR_SUGGESTIONS = {
61
- RuntimeErrorType.CONNECTION_SSL: DISABLE_SSL_SUGGESTION,
62
- RuntimeErrorType.HYPOTHESIS_DEADLINE_EXCEEDED: (
63
- f"Adjust the deadline using {bold('`--hypothesis-deadline=MILLIS`')} or "
64
- f"disable with {bold('`--hypothesis-deadline=None`')}."
65
- ),
66
- RuntimeErrorType.HYPOTHESIS_UNSATISFIABLE: "Examine the schema for inconsistencies and consider simplifying it.",
67
- RuntimeErrorType.SCHEMA_BODY_IN_GET_REQUEST: DISABLE_SCHEMA_VALIDATION_SUGGESTION,
68
- RuntimeErrorType.SCHEMA_INVALID_REGULAR_EXPRESSION: "Ensure your regex is compatible with Python's syntax.\n"
69
- "For guidance, visit: https://docs.python.org/3/library/re.html",
70
- RuntimeErrorType.HYPOTHESIS_UNSUPPORTED_GRAPHQL_SCALAR: "Define a custom strategy for it.\n"
71
- "For guidance, visit: https://schemathesis.readthedocs.io/en/stable/graphql.html#custom-scalars",
72
- RuntimeErrorType.HYPOTHESIS_HEALTH_CHECK_DATA_TOO_LARGE: _format_health_check_suggestion("data_too_large"),
73
- RuntimeErrorType.HYPOTHESIS_HEALTH_CHECK_FILTER_TOO_MUCH: _format_health_check_suggestion("filter_too_much"),
74
- RuntimeErrorType.HYPOTHESIS_HEALTH_CHECK_TOO_SLOW: _format_health_check_suggestion("too_slow"),
75
- RuntimeErrorType.HYPOTHESIS_HEALTH_CHECK_LARGE_BASE_EXAMPLE: _format_health_check_suggestion(
76
- "large_base_example"
77
- ),
78
- }
79
- return RUNTIME_ERROR_SUGGESTIONS.get(error_type)
@@ -1,26 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
- from typing import TYPE_CHECKING
5
-
6
- from ..runner import events
7
- from ..sanitization import sanitize_serialized_check, sanitize_serialized_interaction
8
- from .handlers import EventHandler
9
-
10
- if TYPE_CHECKING:
11
- from .context import ExecutionContext
12
-
13
-
14
- @dataclass
15
- class SanitizationHandler(EventHandler):
16
- def handle_event(self, context: ExecutionContext, event: events.ExecutionEvent) -> None:
17
- if isinstance(event, events.AfterExecution):
18
- for check in event.result.checks:
19
- sanitize_serialized_check(check)
20
- for interaction in event.result.interactions:
21
- sanitize_serialized_interaction(interaction)
22
- elif isinstance(event, events.AfterStatefulExecution):
23
- for check in event.result.checks:
24
- sanitize_serialized_check(check)
25
- for interaction in event.result.interactions:
26
- sanitize_serialized_interaction(interaction)
@@ -1,151 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from enum import Enum
4
- from functools import lru_cache
5
- from shlex import quote
6
- from typing import TYPE_CHECKING
7
-
8
- from .constants import SCHEMATHESIS_TEST_CASE_HEADER
9
-
10
- if TYPE_CHECKING:
11
- from requests.structures import CaseInsensitiveDict
12
-
13
- from .types import Headers
14
-
15
-
16
- @lru_cache
17
- def get_excluded_headers() -> CaseInsensitiveDict:
18
- from requests.structures import CaseInsensitiveDict
19
- from requests.utils import default_headers
20
-
21
- # These headers are added automatically by Schemathesis or `requests`.
22
- # Do not show them in code samples to make them more readable
23
-
24
- return CaseInsensitiveDict(
25
- {
26
- "Content-Length": None,
27
- "Transfer-Encoding": None,
28
- SCHEMATHESIS_TEST_CASE_HEADER: None,
29
- **default_headers(),
30
- }
31
- )
32
-
33
-
34
- class CodeSampleStyle(str, Enum):
35
- """Controls the style of code samples for failure reproduction."""
36
-
37
- python = "python"
38
- curl = "curl"
39
-
40
- @property
41
- def verbose_name(self) -> str:
42
- return {
43
- self.curl: "cURL command",
44
- self.python: "Python code",
45
- }[self]
46
-
47
- @classmethod
48
- def default(cls) -> CodeSampleStyle:
49
- return cls.curl
50
-
51
- @classmethod
52
- def from_str(cls, value: str) -> CodeSampleStyle:
53
- try:
54
- return cls[value]
55
- except KeyError:
56
- available_styles = ", ".join(cls)
57
- raise ValueError(
58
- f"Invalid value for code sample style: {value}. Available styles: {available_styles}"
59
- ) from None
60
-
61
- def generate(
62
- self,
63
- *,
64
- method: str,
65
- url: str,
66
- body: str | bytes | None,
67
- headers: Headers | None,
68
- verify: bool,
69
- extra_headers: Headers | None = None,
70
- ) -> str:
71
- """Generate a code snippet for making HTTP requests."""
72
- handlers = {
73
- self.curl: _generate_curl,
74
- self.python: _generate_requests,
75
- }
76
- return handlers[self](
77
- method=method, url=url, body=body, headers=_filter_headers(headers, extra_headers), verify=verify
78
- )
79
-
80
-
81
- def _filter_headers(headers: Headers | None, extra: Headers | None = None) -> Headers:
82
- headers = headers.copy() if headers else {}
83
- if extra is not None:
84
- for key, value in extra.items():
85
- if key not in get_excluded_headers():
86
- headers[key] = value
87
- return headers
88
-
89
-
90
- def _generate_curl(
91
- *,
92
- method: str,
93
- url: str,
94
- body: str | bytes | None,
95
- headers: Headers,
96
- verify: bool,
97
- ) -> str:
98
- """Create a cURL command to reproduce an HTTP request."""
99
- command = f"curl -X {method}"
100
- for key, value in headers.items():
101
- header = f"{key}: {value}"
102
- command += f" -H {quote(header)}"
103
- if body:
104
- if isinstance(body, bytes):
105
- body = body.decode("utf-8", errors="replace")
106
- command += f" -d {quote(body)}"
107
- if not verify:
108
- command += " --insecure"
109
- return f"{command} {quote(url)}"
110
-
111
-
112
- def _generate_requests(
113
- *,
114
- method: str,
115
- url: str,
116
- body: str | bytes | None,
117
- headers: Headers,
118
- verify: bool,
119
- ) -> str:
120
- """Create a Python code to reproduce an HTTP request."""
121
- url = _escape_single_quotes(url)
122
- command = f"requests.{method.lower()}('{url}'"
123
- if body:
124
- command += f", data={body!r}"
125
- if headers:
126
- command += f", headers={headers!r}"
127
- if not verify:
128
- command += ", verify=False"
129
- command += ")"
130
- return command
131
-
132
-
133
- def _escape_single_quotes(url: str) -> str:
134
- """Escape single quotes in a string, so it is usable as in generated Python code.
135
-
136
- The usual ``str.replace`` is not suitable as it may convert already escaped quotes to not-escaped.
137
- """
138
- result = []
139
- escape = False
140
- for char in url:
141
- if escape:
142
- result.append(char)
143
- escape = False
144
- elif char == "\\":
145
- result.append(char)
146
- escape = True
147
- elif char == "'":
148
- result.append("\\'")
149
- else:
150
- result.append(char)
151
- return "".join(result)
schemathesis/constants.py DELETED
@@ -1,54 +0,0 @@
1
- from importlib import metadata
2
-
3
- from .types import NotSet
4
-
5
- try:
6
- SCHEMATHESIS_VERSION = metadata.version(__package__)
7
- except metadata.PackageNotFoundError:
8
- # Local run without installation
9
- SCHEMATHESIS_VERSION = "dev"
10
-
11
- NOT_SET = NotSet()
12
- USER_AGENT = f"schemathesis/{SCHEMATHESIS_VERSION}"
13
- SCHEMATHESIS_TEST_CASE_HEADER = "X-Schemathesis-TestCaseId"
14
- HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER = ":memory:"
15
- DISCORD_LINK = "https://discord.gg/R9ASRAmHnA"
16
- # Maximum test running time
17
- DEFAULT_DEADLINE = 15000
18
- DEFAULT_RESPONSE_TIMEOUT = 10000
19
- DEFAULT_STATEFUL_RECURSION_LIMIT = 5
20
- HTTP_METHODS = frozenset({"get", "put", "post", "delete", "options", "head", "patch", "trace"})
21
- RECURSIVE_REFERENCE_ERROR_MESSAGE = (
22
- "Currently, Schemathesis can't generate data for this operation due to "
23
- "recursive references in the operation definition. See more information in "
24
- "this issue - https://github.com/schemathesis/schemathesis/issues/947"
25
- )
26
- GIVEN_AND_EXPLICIT_EXAMPLES_ERROR_MESSAGE = (
27
- "Unsupported test setup. Tests using `@schema.given` cannot be combined with explicit schema examples in the same "
28
- "function. Separate these tests into distinct functions to avoid conflicts."
29
- )
30
- SERIALIZERS_SUGGESTION_MESSAGE = (
31
- "You can register your own serializer with `schemathesis.serializer` "
32
- "and Schemathesis will be able to make API calls with this media type. \n"
33
- "See https://schemathesis.readthedocs.io/en/stable/how.html#payload-serialization for more information."
34
- )
35
- NO_LINKS_ERROR_MESSAGE = (
36
- "Stateful testing requires at least one OpenAPI link in the schema, but no links detected. "
37
- "Please add OpenAPI links to enable stateful testing or use stateless tests instead. \n"
38
- "See https://schemathesis.readthedocs.io/en/stable/stateful.html#how-to-specify-connections for more information."
39
- )
40
- EXTENSIONS_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/stable/extending.html"
41
- ISSUE_TRACKER_URL = (
42
- "https://github.com/schemathesis/schemathesis/issues/new?"
43
- "labels=Status%3A%20Needs%20Triage%2C+Type%3A+Bug&template=bug_report.md&title=%5BBUG%5D"
44
- )
45
- FLAKY_FAILURE_MESSAGE = "[FLAKY] Schemathesis was not able to reliably reproduce this failure"
46
- BOM_MARK = "\ufeff"
47
- WAIT_FOR_SCHEMA_INTERVAL = 0.05
48
- HOOKS_MODULE_ENV_VAR = "SCHEMATHESIS_HOOKS"
49
- API_NAME_ENV_VAR = "SCHEMATHESIS_API_NAME"
50
- BASE_URL_ENV_VAR = "SCHEMATHESIS_BASE_URL"
51
- WAIT_FOR_SCHEMA_ENV_VAR = "SCHEMATHESIS_WAIT_FOR_SCHEMA"
52
-
53
- TRUE_VALUES = ("y", "yes", "t", "true", "on", "1")
54
- FALSE_VALUES = ("n", "no", "f", "false", "off", "0")
@@ -1,11 +0,0 @@
1
- from . import openapi, unique_data
2
-
3
-
4
- def install() -> None:
5
- openapi.install()
6
- unique_data.install()
7
-
8
-
9
- def uninstall() -> None:
10
- openapi.uninstall()
11
- unique_data.uninstall()
@@ -1,11 +0,0 @@
1
- from . import fill_missing_examples, formats
2
-
3
-
4
- def install() -> None:
5
- formats.install()
6
- fill_missing_examples.install()
7
-
8
-
9
- def uninstall() -> None:
10
- formats.uninstall()
11
- fill_missing_examples.uninstall()
@@ -1,24 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- from ...hooks import HookContext, register, unregister
6
-
7
- if TYPE_CHECKING:
8
- from ...models import Case
9
-
10
-
11
- def install() -> None:
12
- register(before_add_examples)
13
-
14
-
15
- def uninstall() -> None:
16
- unregister(before_add_examples)
17
-
18
-
19
- def before_add_examples(context: HookContext, examples: list[Case]) -> None:
20
- if not examples and context.operation is not None:
21
- from ...generation import add_single_example
22
-
23
- strategy = context.operation.as_strategy()
24
- add_single_example(strategy, examples)
@@ -1,9 +0,0 @@
1
- from . import uuid
2
-
3
-
4
- def install() -> None:
5
- uuid.install()
6
-
7
-
8
- def uninstall() -> None:
9
- uuid.uninstall()
@@ -1,16 +0,0 @@
1
- # Open API 2.0 / 3.0 do not include `uuid` in the list of built-in formats, hence it lives in `contrib`.
2
- FORMAT_NAME = "uuid"
3
-
4
-
5
- def install() -> None:
6
- from hypothesis import strategies as st
7
-
8
- from ....specs import openapi
9
-
10
- openapi.format(FORMAT_NAME, st.uuids().map(str))
11
-
12
-
13
- def uninstall() -> None:
14
- from ....specs import openapi
15
-
16
- openapi.unregister_string_format("uuid")
@@ -1,41 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import warnings
4
- from typing import TYPE_CHECKING
5
-
6
- from ..hooks import HookContext, register, unregister
7
-
8
- if TYPE_CHECKING:
9
- from hypothesis import strategies as st
10
-
11
- from ..models import Case
12
-
13
-
14
- def install() -> None:
15
- warnings.warn(
16
- "The `schemathesis.contrib.unique_data` hook is **DEPRECATED**. The concept of this feature does not fit the core principles of Hypothesis where "
17
- "strategies are configurable on a per-example basis but this feature implies uniqueness across examples. "
18
- "This leads to cryptic error messages about external state and flaky test runs, "
19
- "therefore it will be removed in Schemathesis 4.0",
20
- DeprecationWarning,
21
- stacklevel=1,
22
- )
23
- register(before_generate_case)
24
-
25
-
26
- def uninstall() -> None:
27
- unregister(before_generate_case)
28
-
29
-
30
- def before_generate_case(context: HookContext, strategy: st.SearchStrategy[Case]) -> st.SearchStrategy[Case]:
31
- seen = set()
32
-
33
- def is_not_seen(case: Case) -> bool:
34
- # Calculate hash just once as it is costly
35
- hashed = hash(case)
36
- if hashed not in seen:
37
- seen.add(hashed)
38
- return True
39
- return False
40
-
41
- return strategy.filter(is_not_seen)