schemathesis 3.25.6__py3-none-any.whl → 4.0.0a1__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 (221) hide show
  1. schemathesis/__init__.py +27 -65
  2. schemathesis/auths.py +102 -82
  3. schemathesis/checks.py +126 -46
  4. schemathesis/cli/__init__.py +11 -1760
  5. schemathesis/cli/__main__.py +4 -0
  6. schemathesis/cli/commands/__init__.py +37 -0
  7. schemathesis/cli/commands/run/__init__.py +662 -0
  8. schemathesis/cli/commands/run/checks.py +80 -0
  9. schemathesis/cli/commands/run/context.py +117 -0
  10. schemathesis/cli/commands/run/events.py +35 -0
  11. schemathesis/cli/commands/run/executor.py +138 -0
  12. schemathesis/cli/commands/run/filters.py +194 -0
  13. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  14. schemathesis/cli/commands/run/handlers/base.py +18 -0
  15. schemathesis/cli/commands/run/handlers/cassettes.py +494 -0
  16. schemathesis/cli/commands/run/handlers/junitxml.py +54 -0
  17. schemathesis/cli/commands/run/handlers/output.py +746 -0
  18. schemathesis/cli/commands/run/hypothesis.py +105 -0
  19. schemathesis/cli/commands/run/loaders.py +129 -0
  20. schemathesis/cli/{callbacks.py → commands/run/validation.py} +103 -174
  21. schemathesis/cli/constants.py +5 -52
  22. schemathesis/cli/core.py +17 -0
  23. schemathesis/cli/ext/fs.py +14 -0
  24. schemathesis/cli/ext/groups.py +55 -0
  25. schemathesis/cli/{options.py → ext/options.py} +39 -10
  26. schemathesis/cli/hooks.py +36 -0
  27. schemathesis/contrib/__init__.py +1 -3
  28. schemathesis/contrib/openapi/__init__.py +1 -3
  29. schemathesis/contrib/openapi/fill_missing_examples.py +3 -5
  30. schemathesis/core/__init__.py +58 -0
  31. schemathesis/core/compat.py +25 -0
  32. schemathesis/core/control.py +2 -0
  33. schemathesis/core/curl.py +58 -0
  34. schemathesis/core/deserialization.py +65 -0
  35. schemathesis/core/errors.py +370 -0
  36. schemathesis/core/failures.py +285 -0
  37. schemathesis/core/fs.py +19 -0
  38. schemathesis/{_lazy_import.py → core/lazy_import.py} +1 -0
  39. schemathesis/core/loaders.py +104 -0
  40. schemathesis/core/marks.py +66 -0
  41. schemathesis/{transports/content_types.py → core/media_types.py} +17 -13
  42. schemathesis/core/output/__init__.py +69 -0
  43. schemathesis/core/output/sanitization.py +197 -0
  44. schemathesis/core/rate_limit.py +60 -0
  45. schemathesis/core/registries.py +31 -0
  46. schemathesis/{internal → core}/result.py +1 -1
  47. schemathesis/core/transforms.py +113 -0
  48. schemathesis/core/transport.py +108 -0
  49. schemathesis/core/validation.py +38 -0
  50. schemathesis/core/version.py +7 -0
  51. schemathesis/engine/__init__.py +30 -0
  52. schemathesis/engine/config.py +59 -0
  53. schemathesis/engine/context.py +119 -0
  54. schemathesis/engine/control.py +36 -0
  55. schemathesis/engine/core.py +157 -0
  56. schemathesis/engine/errors.py +394 -0
  57. schemathesis/engine/events.py +337 -0
  58. schemathesis/engine/phases/__init__.py +66 -0
  59. schemathesis/{runner → engine/phases}/probes.py +50 -67
  60. schemathesis/engine/phases/stateful/__init__.py +65 -0
  61. schemathesis/engine/phases/stateful/_executor.py +326 -0
  62. schemathesis/engine/phases/stateful/context.py +85 -0
  63. schemathesis/engine/phases/unit/__init__.py +174 -0
  64. schemathesis/engine/phases/unit/_executor.py +321 -0
  65. schemathesis/engine/phases/unit/_pool.py +74 -0
  66. schemathesis/engine/recorder.py +241 -0
  67. schemathesis/errors.py +31 -0
  68. schemathesis/experimental/__init__.py +18 -14
  69. schemathesis/filters.py +103 -14
  70. schemathesis/generation/__init__.py +21 -37
  71. schemathesis/generation/case.py +190 -0
  72. schemathesis/generation/coverage.py +931 -0
  73. schemathesis/generation/hypothesis/__init__.py +30 -0
  74. schemathesis/generation/hypothesis/builder.py +585 -0
  75. schemathesis/generation/hypothesis/examples.py +50 -0
  76. schemathesis/generation/hypothesis/given.py +66 -0
  77. schemathesis/generation/hypothesis/reporting.py +14 -0
  78. schemathesis/generation/hypothesis/strategies.py +16 -0
  79. schemathesis/generation/meta.py +115 -0
  80. schemathesis/generation/modes.py +28 -0
  81. schemathesis/generation/overrides.py +96 -0
  82. schemathesis/generation/stateful/__init__.py +20 -0
  83. schemathesis/{stateful → generation/stateful}/state_machine.py +68 -81
  84. schemathesis/generation/targets.py +69 -0
  85. schemathesis/graphql/__init__.py +15 -0
  86. schemathesis/graphql/checks.py +115 -0
  87. schemathesis/graphql/loaders.py +131 -0
  88. schemathesis/hooks.py +99 -67
  89. schemathesis/openapi/__init__.py +13 -0
  90. schemathesis/openapi/checks.py +412 -0
  91. schemathesis/openapi/generation/__init__.py +0 -0
  92. schemathesis/openapi/generation/filters.py +63 -0
  93. schemathesis/openapi/loaders.py +178 -0
  94. schemathesis/pytest/__init__.py +5 -0
  95. schemathesis/pytest/control_flow.py +7 -0
  96. schemathesis/pytest/lazy.py +273 -0
  97. schemathesis/pytest/loaders.py +12 -0
  98. schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +106 -127
  99. schemathesis/python/__init__.py +0 -0
  100. schemathesis/python/asgi.py +12 -0
  101. schemathesis/python/wsgi.py +12 -0
  102. schemathesis/schemas.py +537 -261
  103. schemathesis/specs/graphql/__init__.py +0 -1
  104. schemathesis/specs/graphql/_cache.py +25 -0
  105. schemathesis/specs/graphql/nodes.py +1 -0
  106. schemathesis/specs/graphql/scalars.py +7 -5
  107. schemathesis/specs/graphql/schemas.py +215 -187
  108. schemathesis/specs/graphql/validation.py +11 -18
  109. schemathesis/specs/openapi/__init__.py +7 -1
  110. schemathesis/specs/openapi/_cache.py +122 -0
  111. schemathesis/specs/openapi/_hypothesis.py +146 -165
  112. schemathesis/specs/openapi/checks.py +565 -67
  113. schemathesis/specs/openapi/converter.py +33 -6
  114. schemathesis/specs/openapi/definitions.py +11 -18
  115. schemathesis/specs/openapi/examples.py +139 -23
  116. schemathesis/specs/openapi/expressions/__init__.py +37 -2
  117. schemathesis/specs/openapi/expressions/context.py +4 -6
  118. schemathesis/specs/openapi/expressions/extractors.py +23 -0
  119. schemathesis/specs/openapi/expressions/lexer.py +20 -18
  120. schemathesis/specs/openapi/expressions/nodes.py +38 -14
  121. schemathesis/specs/openapi/expressions/parser.py +26 -5
  122. schemathesis/specs/openapi/formats.py +45 -0
  123. schemathesis/specs/openapi/links.py +65 -165
  124. schemathesis/specs/openapi/media_types.py +32 -0
  125. schemathesis/specs/openapi/negative/__init__.py +7 -3
  126. schemathesis/specs/openapi/negative/mutations.py +24 -8
  127. schemathesis/specs/openapi/parameters.py +46 -30
  128. schemathesis/specs/openapi/patterns.py +137 -0
  129. schemathesis/specs/openapi/references.py +47 -57
  130. schemathesis/specs/openapi/schemas.py +478 -369
  131. schemathesis/specs/openapi/security.py +25 -7
  132. schemathesis/specs/openapi/serialization.py +11 -6
  133. schemathesis/specs/openapi/stateful/__init__.py +185 -73
  134. schemathesis/specs/openapi/utils.py +6 -1
  135. schemathesis/transport/__init__.py +104 -0
  136. schemathesis/transport/asgi.py +26 -0
  137. schemathesis/transport/prepare.py +99 -0
  138. schemathesis/transport/requests.py +221 -0
  139. schemathesis/{_xml.py → transport/serialization.py} +143 -28
  140. schemathesis/transport/wsgi.py +165 -0
  141. schemathesis-4.0.0a1.dist-info/METADATA +297 -0
  142. schemathesis-4.0.0a1.dist-info/RECORD +152 -0
  143. {schemathesis-3.25.6.dist-info → schemathesis-4.0.0a1.dist-info}/WHEEL +1 -1
  144. {schemathesis-3.25.6.dist-info → schemathesis-4.0.0a1.dist-info}/entry_points.txt +1 -1
  145. schemathesis/_compat.py +0 -74
  146. schemathesis/_dependency_versions.py +0 -17
  147. schemathesis/_hypothesis.py +0 -246
  148. schemathesis/_override.py +0 -49
  149. schemathesis/cli/cassettes.py +0 -375
  150. schemathesis/cli/context.py +0 -58
  151. schemathesis/cli/debug.py +0 -26
  152. schemathesis/cli/handlers.py +0 -16
  153. schemathesis/cli/junitxml.py +0 -43
  154. schemathesis/cli/output/__init__.py +0 -1
  155. schemathesis/cli/output/default.py +0 -790
  156. schemathesis/cli/output/short.py +0 -44
  157. schemathesis/cli/sanitization.py +0 -20
  158. schemathesis/code_samples.py +0 -149
  159. schemathesis/constants.py +0 -55
  160. schemathesis/contrib/openapi/formats/__init__.py +0 -9
  161. schemathesis/contrib/openapi/formats/uuid.py +0 -15
  162. schemathesis/contrib/unique_data.py +0 -41
  163. schemathesis/exceptions.py +0 -560
  164. schemathesis/extra/_aiohttp.py +0 -27
  165. schemathesis/extra/_flask.py +0 -10
  166. schemathesis/extra/_server.py +0 -17
  167. schemathesis/failures.py +0 -209
  168. schemathesis/fixups/__init__.py +0 -36
  169. schemathesis/fixups/fast_api.py +0 -41
  170. schemathesis/fixups/utf8_bom.py +0 -29
  171. schemathesis/graphql.py +0 -4
  172. schemathesis/internal/__init__.py +0 -7
  173. schemathesis/internal/copy.py +0 -13
  174. schemathesis/internal/datetime.py +0 -5
  175. schemathesis/internal/deprecation.py +0 -34
  176. schemathesis/internal/jsonschema.py +0 -35
  177. schemathesis/internal/transformation.py +0 -15
  178. schemathesis/internal/validation.py +0 -34
  179. schemathesis/lazy.py +0 -361
  180. schemathesis/loaders.py +0 -120
  181. schemathesis/models.py +0 -1234
  182. schemathesis/parameters.py +0 -86
  183. schemathesis/runner/__init__.py +0 -570
  184. schemathesis/runner/events.py +0 -329
  185. schemathesis/runner/impl/__init__.py +0 -3
  186. schemathesis/runner/impl/core.py +0 -1035
  187. schemathesis/runner/impl/solo.py +0 -90
  188. schemathesis/runner/impl/threadpool.py +0 -400
  189. schemathesis/runner/serialization.py +0 -411
  190. schemathesis/sanitization.py +0 -248
  191. schemathesis/serializers.py +0 -323
  192. schemathesis/service/__init__.py +0 -18
  193. schemathesis/service/auth.py +0 -11
  194. schemathesis/service/ci.py +0 -201
  195. schemathesis/service/client.py +0 -100
  196. schemathesis/service/constants.py +0 -38
  197. schemathesis/service/events.py +0 -57
  198. schemathesis/service/hosts.py +0 -107
  199. schemathesis/service/metadata.py +0 -46
  200. schemathesis/service/models.py +0 -49
  201. schemathesis/service/report.py +0 -255
  202. schemathesis/service/serialization.py +0 -199
  203. schemathesis/service/usage.py +0 -65
  204. schemathesis/specs/graphql/loaders.py +0 -344
  205. schemathesis/specs/openapi/filters.py +0 -49
  206. schemathesis/specs/openapi/loaders.py +0 -667
  207. schemathesis/specs/openapi/stateful/links.py +0 -92
  208. schemathesis/specs/openapi/validation.py +0 -25
  209. schemathesis/stateful/__init__.py +0 -133
  210. schemathesis/targets.py +0 -45
  211. schemathesis/throttling.py +0 -41
  212. schemathesis/transports/__init__.py +0 -5
  213. schemathesis/transports/auth.py +0 -15
  214. schemathesis/transports/headers.py +0 -35
  215. schemathesis/transports/responses.py +0 -52
  216. schemathesis/types.py +0 -35
  217. schemathesis/utils.py +0 -169
  218. schemathesis-3.25.6.dist-info/METADATA +0 -356
  219. schemathesis-3.25.6.dist-info/RECORD +0 -134
  220. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
  221. {schemathesis-3.25.6.dist-info → schemathesis-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
schemathesis/failures.py DELETED
@@ -1,209 +0,0 @@
1
- from __future__ import annotations
2
- import textwrap
3
- from dataclasses import dataclass
4
- from json import JSONDecodeError
5
- from typing import Any, TYPE_CHECKING
6
-
7
- if TYPE_CHECKING:
8
- from graphql.error import GraphQLFormattedError
9
- from jsonschema import ValidationError
10
-
11
-
12
- class FailureContext:
13
- """Additional data specific to certain failure kind."""
14
-
15
- # Short description of what happened
16
- title: str
17
- # A longer one
18
- message: str
19
- type: str
20
-
21
- def unique_by_key(self, check_message: str | None) -> tuple[str, ...]:
22
- """A key to distinguish different failure contexts."""
23
- return (check_message or self.message,)
24
-
25
-
26
- @dataclass(repr=False)
27
- class ValidationErrorContext(FailureContext):
28
- """Additional information about JSON Schema validation errors."""
29
-
30
- validation_message: str
31
- schema_path: list[str | int]
32
- schema: dict[str, Any] | bool
33
- instance_path: list[str | int]
34
- instance: None | bool | float | str | list | dict[str, Any]
35
- message: str
36
- title: str = "Response violates schema"
37
- type: str = "json_schema"
38
-
39
- def unique_by_key(self, check_message: str | None) -> tuple[str, ...]:
40
- # Deduplicate by JSON Schema path. All errors that happened on this sub-schema will be deduplicated
41
- return ("/".join(map(str, self.schema_path)),)
42
-
43
- @classmethod
44
- def from_exception(cls, exc: ValidationError) -> ValidationErrorContext:
45
- from .exceptions import truncated_json
46
-
47
- schema = textwrap.indent(truncated_json(exc.schema, max_lines=20), prefix=" ")
48
- value = textwrap.indent(truncated_json(exc.instance, max_lines=20), prefix=" ")
49
- message = f"{exc.message}\n\nSchema:\n\n{schema}\n\nValue:\n\n{value}"
50
- return cls(
51
- message=message,
52
- validation_message=exc.message,
53
- schema_path=list(exc.absolute_schema_path),
54
- schema=exc.schema,
55
- instance_path=list(exc.absolute_path),
56
- instance=exc.instance,
57
- )
58
-
59
-
60
- @dataclass(repr=False)
61
- class JSONDecodeErrorContext(FailureContext):
62
- """Failed to decode JSON."""
63
-
64
- validation_message: str
65
- document: str
66
- position: int
67
- lineno: int
68
- colno: int
69
- message: str
70
- title: str = "JSON deserialization error"
71
- type: str = "json_decode"
72
-
73
- def unique_by_key(self, check_message: str | None) -> tuple[str, ...]:
74
- # Treat different JSON decoding failures as the same issue
75
- # Payloads often contain dynamic data and distinguishing it by the error location still would not be sufficient
76
- # as it may be different on different dynamic payloads
77
- return (self.title,)
78
-
79
- @classmethod
80
- def from_exception(cls, exc: JSONDecodeError) -> JSONDecodeErrorContext:
81
- return cls(
82
- message=str(exc),
83
- validation_message=exc.msg,
84
- document=exc.doc,
85
- position=exc.pos,
86
- lineno=exc.lineno,
87
- colno=exc.colno,
88
- )
89
-
90
-
91
- @dataclass(repr=False)
92
- class ServerError(FailureContext):
93
- status_code: int
94
- title: str = "Server error"
95
- message: str = ""
96
- type: str = "server_error"
97
-
98
-
99
- @dataclass(repr=False)
100
- class MissingContentType(FailureContext):
101
- """Content type header is missing."""
102
-
103
- media_types: list[str]
104
- message: str
105
- title: str = "Missing Content-Type header"
106
- type: str = "missing_content_type"
107
-
108
-
109
- @dataclass(repr=False)
110
- class UndefinedContentType(FailureContext):
111
- """Response has Content-Type that is not documented in the schema."""
112
-
113
- content_type: str
114
- defined_content_types: list[str]
115
- message: str
116
- title: str = "Undocumented Content-Type"
117
- type: str = "undefined_content_type"
118
-
119
-
120
- @dataclass(repr=False)
121
- class UndefinedStatusCode(FailureContext):
122
- """Response has a status code that is not defined in the schema."""
123
-
124
- # Response's status code
125
- status_code: int
126
- # Status codes as defined in schema
127
- defined_status_codes: list[str]
128
- # Defined status code with expanded wildcards
129
- allowed_status_codes: list[int]
130
- message: str
131
- title: str = "Undocumented HTTP status code"
132
- type: str = "undefined_status_code"
133
-
134
-
135
- @dataclass(repr=False)
136
- class MissingHeaders(FailureContext):
137
- """Some required headers are missing."""
138
-
139
- missing_headers: list[str]
140
- message: str
141
- title: str = "Missing required headers"
142
- type: str = "missing_headers"
143
-
144
-
145
- @dataclass(repr=False)
146
- class MalformedMediaType(FailureContext):
147
- """Media type name is malformed.
148
-
149
- Example: `application-json` instead of `application/json`
150
- """
151
-
152
- actual: str
153
- defined: str
154
- message: str
155
- title: str = "Malformed media type"
156
- type: str = "malformed_media_type"
157
-
158
-
159
- @dataclass(repr=False)
160
- class ResponseTimeExceeded(FailureContext):
161
- """Response took longer than expected."""
162
-
163
- elapsed: float
164
- deadline: int
165
- message: str
166
- title: str = "Response time limit exceeded"
167
- type: str = "response_time_exceeded"
168
-
169
- def unique_by_key(self, check_message: str | None) -> tuple[str, ...]:
170
- return (self.title,)
171
-
172
-
173
- @dataclass(repr=False)
174
- class RequestTimeout(FailureContext):
175
- """Request took longer than timeout."""
176
-
177
- timeout: int
178
- message: str
179
- title: str = "Response timeout"
180
- type: str = "request_timeout"
181
-
182
-
183
- @dataclass(repr=False)
184
- class UnexpectedGraphQLResponse(FailureContext):
185
- """GraphQL response is not a JSON object."""
186
-
187
- message: str
188
- title: str = "Unexpected GraphQL Response"
189
- type: str = "graphql_unexpected_response"
190
-
191
-
192
- @dataclass(repr=False)
193
- class GraphQLClientError(FailureContext):
194
- """GraphQL query has not been executed."""
195
-
196
- message: str
197
- errors: list[GraphQLFormattedError]
198
- title: str = "GraphQL client error"
199
- type: str = "graphql_client_error"
200
-
201
-
202
- @dataclass(repr=False)
203
- class GraphQLServerError(FailureContext):
204
- """GraphQL response indicates at least one server error."""
205
-
206
- message: str
207
- errors: list[GraphQLFormattedError]
208
- title: str = "GraphQL server error"
209
- type: str = "graphql_server_error"
@@ -1,36 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Iterable
3
-
4
- from . import fast_api, utf8_bom
5
-
6
- ALL_FIXUPS = {"fast_api": fast_api, "utf8_bom": utf8_bom}
7
- ALL_FIXUP_NAMES = list(ALL_FIXUPS.keys())
8
-
9
-
10
- def install(fixups: Iterable[str] | None = None) -> None:
11
- """Install fixups.
12
-
13
- Without the first argument installs all available fixups.
14
-
15
- :param fixups: Names of fixups to install.
16
- """
17
- fixups = fixups or ALL_FIXUP_NAMES
18
- for name in fixups:
19
- ALL_FIXUPS[name].install() # type: ignore
20
-
21
-
22
- def uninstall(fixups: Iterable[str] | None = None) -> None:
23
- """Uninstall fixups.
24
-
25
- Without the first argument uninstalls all available fixups.
26
-
27
- :param fixups: Names of fixups to uninstall.
28
- """
29
- fixups = fixups or ALL_FIXUP_NAMES
30
- for name in fixups:
31
- ALL_FIXUPS[name].uninstall() # type: ignore
32
-
33
-
34
- def is_installed(name: str) -> bool:
35
- """Check whether fixup is installed."""
36
- return ALL_FIXUPS[name].is_installed()
@@ -1,41 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Any
3
-
4
- from ..hooks import HookContext
5
- from ..hooks import is_installed as global_is_installed
6
- from ..hooks import register, unregister
7
- from ..internal.jsonschema import traverse_schema
8
-
9
-
10
- def install() -> None:
11
- register(before_load_schema)
12
-
13
-
14
- def uninstall() -> None:
15
- unregister(before_load_schema)
16
-
17
-
18
- def is_installed() -> bool:
19
- return global_is_installed("before_load_schema", before_load_schema)
20
-
21
-
22
- def before_load_schema(context: HookContext, schema: dict[str, Any]) -> None:
23
- adjust_schema(schema)
24
-
25
-
26
- def adjust_schema(schema: dict[str, Any]) -> None:
27
- traverse_schema(schema, _handle_boundaries)
28
-
29
-
30
- def _handle_boundaries(schema: dict[str, Any]) -> dict[str, Any]:
31
- """Convert Draft 7 keywords to Draft 4 compatible versions.
32
-
33
- FastAPI uses ``pydantic``, which generates Draft 7 compatible schemas.
34
- """
35
- for boundary_name, boundary_exclusive_name in (("maximum", "exclusiveMaximum"), ("minimum", "exclusiveMinimum")):
36
- value = schema.get(boundary_exclusive_name)
37
- # `bool` check is needed, since in Python `True` is an instance of `int`
38
- if isinstance(value, (int, float)) and not isinstance(value, bool):
39
- schema[boundary_exclusive_name] = True
40
- schema[boundary_name] = value
41
- return schema
@@ -1,29 +0,0 @@
1
- from typing import TYPE_CHECKING
2
-
3
- from ..constants import BOM_MARK
4
- from ..hooks import HookContext
5
- from ..hooks import is_installed as global_is_installed
6
- from ..hooks import register, unregister
7
-
8
- if TYPE_CHECKING:
9
- from ..models import Case
10
- from ..transports.responses import GenericResponse
11
-
12
-
13
- def install() -> None:
14
- register(after_call)
15
-
16
-
17
- def uninstall() -> None:
18
- unregister(after_call)
19
-
20
-
21
- def is_installed() -> bool:
22
- return global_is_installed("after_call", after_call)
23
-
24
-
25
- def after_call(context: HookContext, case: "Case", response: "GenericResponse") -> None:
26
- from requests import Response
27
-
28
- if isinstance(response, Response) and response.encoding == "utf-8" and response.text[0:1] == BOM_MARK:
29
- response.encoding = "utf-8-sig"
schemathesis/graphql.py DELETED
@@ -1,4 +0,0 @@
1
- # Public API
2
- from .specs.graphql import nodes # noqa: F401
3
- from .specs.graphql.loaders import from_asgi, from_dict, from_file, from_path, from_url, from_wsgi # noqa: F401
4
- from .specs.graphql.scalars import scalar # noqa: F401
@@ -1,7 +0,0 @@
1
- """A private API to work with Schemathesis internals."""
2
-
3
-
4
- def clear_cache() -> None:
5
- from ..specs.openapi import _hypothesis
6
-
7
- _hypothesis.clear_cache()
@@ -1,13 +0,0 @@
1
- from typing import Any
2
-
3
-
4
- def fast_deepcopy(value: Any) -> Any:
5
- """A specialized version of `deepcopy` that copies only `dict` and `list`.
6
-
7
- It is on average 3x faster than `deepcopy` and given the amount of calls, it is an important optimization.
8
- """
9
- if isinstance(value, dict):
10
- return {key: fast_deepcopy(v) for key, v in value.items()}
11
- if isinstance(value, list):
12
- return [fast_deepcopy(v) for v in value]
13
- return value
@@ -1,5 +0,0 @@
1
- from datetime import datetime, timezone
2
-
3
-
4
- def current_datetime() -> str:
5
- return datetime.now(timezone.utc).astimezone().isoformat()
@@ -1,34 +0,0 @@
1
- import warnings
2
- from typing import Callable, Any
3
-
4
-
5
- def _warn_deprecation(*, kind: str, thing: str, removed_in: str, replacement: str) -> None:
6
- warnings.warn(
7
- f"{kind} `{thing}` is deprecated and will be removed in Schemathesis {removed_in}. "
8
- f"Use `{replacement}` instead.",
9
- DeprecationWarning,
10
- stacklevel=1,
11
- )
12
-
13
-
14
- def deprecated_property(*, removed_in: str, replacement: str) -> Callable:
15
- def wrapper(prop: Callable) -> Callable:
16
- @property # type: ignore
17
- def inner(self: Any) -> Any:
18
- _warn_deprecation(kind="Property", thing=prop.__name__, removed_in=removed_in, replacement=replacement)
19
- return prop(self)
20
-
21
- return inner
22
-
23
- return wrapper
24
-
25
-
26
- def deprecated_function(*, removed_in: str, replacement: str) -> Callable:
27
- def wrapper(func: Callable) -> Callable:
28
- def inner(*args: Any, **kwargs: Any) -> Any:
29
- _warn_deprecation(kind="Function", thing=func.__name__, removed_in=removed_in, replacement=replacement)
30
- return func(*args, **kwargs)
31
-
32
- return inner
33
-
34
- return wrapper
@@ -1,35 +0,0 @@
1
- from __future__ import annotations
2
- from typing import overload, Dict, Union, Any, List, Callable
3
-
4
- JsonValue = Union[Dict[str, Any], List, str, float, int]
5
-
6
-
7
- @overload
8
- def traverse_schema(schema: dict[str, Any], callback: Callable, *args: Any, **kwargs: Any) -> dict[str, Any]:
9
- pass
10
-
11
-
12
- @overload
13
- def traverse_schema(schema: list, callback: Callable, *args: Any, **kwargs: Any) -> list:
14
- pass
15
-
16
-
17
- @overload
18
- def traverse_schema(schema: str, callback: Callable, *args: Any, **kwargs: Any) -> str:
19
- pass
20
-
21
-
22
- @overload
23
- def traverse_schema(schema: float, callback: Callable, *args: Any, **kwargs: Any) -> float:
24
- pass
25
-
26
-
27
- def traverse_schema(schema: JsonValue, callback: Callable[..., dict[str, Any]], *args: Any, **kwargs: Any) -> JsonValue:
28
- """Apply callback recursively to the given schema."""
29
- if isinstance(schema, dict):
30
- schema = callback(schema, *args, **kwargs)
31
- for key, sub_item in schema.items():
32
- schema[key] = traverse_schema(sub_item, callback, *args, **kwargs)
33
- elif isinstance(schema, list):
34
- schema = [traverse_schema(sub_item, callback, *args, **kwargs) for sub_item in schema]
35
- return schema
@@ -1,15 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Any
3
-
4
-
5
- def merge_recursively(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]:
6
- """Merge two dictionaries recursively."""
7
- for key in b:
8
- if key in a:
9
- if isinstance(a[key], dict) and isinstance(b[key], dict):
10
- merge_recursively(a[key], b[key])
11
- else:
12
- a[key] = b[key]
13
- else:
14
- a[key] = b[key]
15
- return a
@@ -1,34 +0,0 @@
1
- import pathlib
2
- import re
3
- from typing import Any
4
-
5
-
6
- def require_relative_url(url: str) -> None:
7
- """Raise an error if the URL is not relative."""
8
- from yarl import URL
9
-
10
- if URL(url).is_absolute():
11
- raise ValueError("Schema path should be relative for WSGI/ASGI loaders")
12
-
13
-
14
- def file_exists(path: str) -> bool:
15
- try:
16
- return pathlib.Path(path).is_file()
17
- except OSError:
18
- # For example, path could be too long
19
- return False
20
-
21
-
22
- def is_filename(value: str) -> bool:
23
- """Detect if the input string is a filename by checking its extension."""
24
- return bool(pathlib.Path(value).suffix)
25
-
26
-
27
- SURROGATE_PAIR_RE = re.compile(r"[\ud800-\udfff]")
28
- has_surrogate_pair = SURROGATE_PAIR_RE.search
29
-
30
-
31
- def is_illegal_surrogate(item: Any) -> bool:
32
- if isinstance(item, list):
33
- return any(isinstance(item_, str) and bool(has_surrogate_pair(item_)) for item_ in item)
34
- return isinstance(item, str) and bool(has_surrogate_pair(item))