schemathesis 3.25.5__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 -1766
  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/{cli → engine/phases}/probes.py +63 -70
  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 +153 -39
  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 +483 -367
  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.5.dist-info → schemathesis-4.0.0a1.dist-info}/WHEEL +1 -1
  144. {schemathesis-3.25.5.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 -55
  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 -765
  156. schemathesis/cli/output/short.py +0 -40
  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 -1231
  182. schemathesis/parameters.py +0 -86
  183. schemathesis/runner/__init__.py +0 -555
  184. schemathesis/runner/events.py +0 -309
  185. schemathesis/runner/impl/__init__.py +0 -3
  186. schemathesis/runner/impl/core.py +0 -986
  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 -315
  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 -184
  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.5.dist-info/METADATA +0 -356
  219. schemathesis-3.25.5.dist-info/RECORD +0 -134
  220. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
  221. {schemathesis-3.25.5.dist-info → schemathesis-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,65 +0,0 @@
1
- from __future__ import annotations
2
- import sys
3
- from typing import Any
4
-
5
- import click
6
- from click.types import StringParamType
7
-
8
- from .. import cli, hooks
9
-
10
-
11
- def collect(args: list[str] | None = None) -> dict[str, Any] | None:
12
- """Collect anonymized CLI usage data."""
13
- context: click.Context | None = click.get_current_context(silent=True)
14
- if context is not None and not sys.argv[0].endswith("pytest"):
15
- args = args or sys.argv[2:]
16
- parameters, _, types = parse_cli_args(context, args)
17
- parameters_data: dict[str, dict[str, Any]] = {}
18
- used_headers: list[str] = []
19
- schema = parameters["schema"]
20
- app = parameters.get("app")
21
- if not schema:
22
- schema_kind = None
23
- else:
24
- schema_kind = cli.callbacks.parse_schema_kind(schema, app).name
25
- usage = {
26
- "schema_kind": schema_kind,
27
- "parameters": parameters_data,
28
- "used_headers": used_headers,
29
- "hooks": hooks.collect_statistic(),
30
- }
31
- types_iter = iter(types)
32
- for option, value in parameters.items():
33
- option_type = next(types_iter)
34
- if isinstance(option_type, click.Argument):
35
- continue
36
- if option_type.multiple:
37
- # Forward the iterator to the next option type
38
- for _ in range(len(value) - 1):
39
- next(types_iter)
40
- entry = _collect_option(option, option_type, used_headers, value)
41
- if entry:
42
- parameters_data[option] = entry
43
- return usage
44
- return None
45
-
46
-
47
- def _collect_option(option: str, option_type: click.Parameter, used_headers: list[str], value: Any) -> dict[str, Any]:
48
- entry = {}
49
- if isinstance(option_type.type, (StringParamType, click.types.File)):
50
- if option == "headers" and value:
51
- used_headers.extend(header.split(":", 1)[0] for header in value)
52
- else:
53
- # Free-form values are replaced with their number of occurrences, to avoid sending sensitive info
54
- if option_type.multiple:
55
- entry["count"] = len(value)
56
- else:
57
- entry["count"] = 1
58
- else:
59
- entry["value"] = value
60
- return entry
61
-
62
-
63
- def parse_cli_args(context: click.Context, args: list[str]) -> tuple[dict[str, Any], list, list[click.Parameter]]:
64
- parser = cli.run.make_parser(context)
65
- return parser.parse_args(args=args)
@@ -1,344 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import pathlib
5
- from functools import lru_cache
6
- from json import JSONDecodeError
7
- from typing import IO, TYPE_CHECKING, Any, Callable, Dict, NoReturn, cast
8
-
9
- from ...code_samples import CodeSampleStyle
10
- from ...constants import WAIT_FOR_SCHEMA_INTERVAL
11
- from ...exceptions import SchemaError, SchemaErrorType
12
- from ...generation import (
13
- DEFAULT_DATA_GENERATION_METHODS,
14
- DataGenerationMethod,
15
- DataGenerationMethodInput,
16
- GenerationConfig,
17
- )
18
- from ...hooks import HookContext, dispatch
19
- from ...internal.validation import require_relative_url
20
- from ...loaders import load_schema_from_url
21
- from ...throttling import build_limiter
22
- from ...transports.headers import setup_default_headers
23
- from ...types import PathLike
24
-
25
- if TYPE_CHECKING:
26
- from graphql import DocumentNode
27
- from pyrate_limiter import Limiter
28
-
29
- from ...transports.responses import GenericResponse
30
- from .schemas import GraphQLSchema
31
-
32
-
33
- @lru_cache
34
- def get_introspection_query() -> str:
35
- import graphql
36
-
37
- return graphql.get_introspection_query()
38
-
39
-
40
- @lru_cache
41
- def get_introspection_query_ast() -> DocumentNode:
42
- import graphql
43
-
44
- query = get_introspection_query()
45
- return graphql.parse(query)
46
-
47
-
48
- def from_path(
49
- path: PathLike,
50
- *,
51
- app: Any = None,
52
- base_url: str | None = None,
53
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
54
- generation_config: GenerationConfig | None = None,
55
- code_sample_style: str = CodeSampleStyle.default().name,
56
- rate_limit: str | None = None,
57
- encoding: str = "utf8",
58
- sanitize_output: bool = True,
59
- ) -> GraphQLSchema:
60
- """Load GraphQL schema via a file from an OS path.
61
-
62
- :param path: A path to the schema file.
63
- :param encoding: The name of the encoding used to decode the file.
64
- """
65
- with open(path, encoding=encoding) as fd:
66
- return from_file(
67
- fd,
68
- app=app,
69
- base_url=base_url,
70
- data_generation_methods=data_generation_methods,
71
- code_sample_style=code_sample_style,
72
- location=pathlib.Path(path).absolute().as_uri(),
73
- rate_limit=rate_limit,
74
- sanitize_output=sanitize_output,
75
- )
76
-
77
-
78
- def extract_schema_from_response(response: GenericResponse) -> dict[str, Any]:
79
- from requests import Response
80
-
81
- try:
82
- if isinstance(response, Response):
83
- decoded = response.json()
84
- else:
85
- decoded = response.json
86
- except JSONDecodeError as exc:
87
- raise SchemaError(
88
- SchemaErrorType.UNEXPECTED_CONTENT_TYPE,
89
- "Received unsupported content while expecting a JSON payload for GraphQL",
90
- ) from exc
91
- return decoded
92
-
93
-
94
- def from_url(
95
- url: str,
96
- *,
97
- app: Any = None,
98
- base_url: str | None = None,
99
- port: int | None = None,
100
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
101
- generation_config: GenerationConfig | None = None,
102
- code_sample_style: str = CodeSampleStyle.default().name,
103
- wait_for_schema: float | None = None,
104
- rate_limit: str | None = None,
105
- sanitize_output: bool = True,
106
- **kwargs: Any,
107
- ) -> GraphQLSchema:
108
- """Load GraphQL schema from the network.
109
-
110
- :param url: Schema URL.
111
- :param Optional[str] base_url: Base URL to send requests to.
112
- :param Optional[int] port: An optional port if you don't want to pass the ``base_url`` parameter, but only to change
113
- port in ``url``.
114
- :param app: A WSGI app instance.
115
- :return: GraphQLSchema
116
- """
117
- import backoff
118
- import requests
119
-
120
- setup_default_headers(kwargs)
121
- kwargs.setdefault("json", {"query": get_introspection_query()})
122
- if port:
123
- from yarl import URL
124
-
125
- url = str(URL(url).with_port(port))
126
- if not base_url:
127
- base_url = url
128
-
129
- if wait_for_schema is not None:
130
-
131
- @backoff.on_exception( # type: ignore
132
- backoff.constant,
133
- requests.exceptions.ConnectionError,
134
- max_time=wait_for_schema,
135
- interval=WAIT_FOR_SCHEMA_INTERVAL,
136
- )
137
- def _load_schema(_uri: str, **_kwargs: Any) -> requests.Response:
138
- return requests.post(_uri, **kwargs)
139
-
140
- else:
141
- _load_schema = requests.post
142
-
143
- response = load_schema_from_url(lambda: _load_schema(url, **kwargs))
144
- raw_schema = extract_schema_from_response(response)
145
- return from_dict(
146
- raw_schema=raw_schema,
147
- location=url,
148
- base_url=base_url,
149
- app=app,
150
- data_generation_methods=data_generation_methods,
151
- code_sample_style=code_sample_style,
152
- rate_limit=rate_limit,
153
- sanitize_output=sanitize_output,
154
- )
155
-
156
-
157
- def from_file(
158
- file: IO[str] | str,
159
- *,
160
- app: Any = None,
161
- base_url: str | None = None,
162
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
163
- generation_config: GenerationConfig | None = None,
164
- code_sample_style: str = CodeSampleStyle.default().name,
165
- location: str | None = None,
166
- rate_limit: str | None = None,
167
- sanitize_output: bool = True,
168
- ) -> GraphQLSchema:
169
- """Load GraphQL schema from a file descriptor or a string.
170
-
171
- :param file: Could be a file descriptor, string or bytes.
172
- """
173
- import graphql
174
-
175
- if isinstance(file, str):
176
- data = file
177
- else:
178
- data = file.read()
179
- try:
180
- document = graphql.build_schema(data)
181
- result = graphql.execute(document, get_introspection_query_ast())
182
- # TYPES: We don't pass `is_awaitable` above, therefore `result` is of the `ExecutionResult` type
183
- result = cast(graphql.ExecutionResult, result)
184
- # TYPES:
185
- # - `document` is a valid schema, because otherwise `build_schema` will rise an error;
186
- # - `INTROSPECTION_QUERY` is a valid query - it is known upfront;
187
- # Therefore the execution result is always valid at this point and `result.data` is not `None`
188
- raw_schema = cast(Dict[str, Any], result.data)
189
- except Exception as exc:
190
- try:
191
- raw_schema = json.loads(data)
192
- if not isinstance(raw_schema, dict) or "__schema" not in raw_schema:
193
- _on_invalid_schema(exc)
194
- except json.JSONDecodeError:
195
- _on_invalid_schema(exc, extras=[entry for entry in str(exc).splitlines() if entry])
196
- return from_dict(
197
- raw_schema,
198
- app=app,
199
- base_url=base_url,
200
- data_generation_methods=data_generation_methods,
201
- code_sample_style=code_sample_style,
202
- location=location,
203
- rate_limit=rate_limit,
204
- sanitize_output=sanitize_output,
205
- )
206
-
207
-
208
- def _on_invalid_schema(exc: Exception, extras: list[str] | None = None) -> NoReturn:
209
- raise SchemaError(
210
- SchemaErrorType.GRAPHQL_INVALID_SCHEMA,
211
- "The provided API schema does not appear to be a valid GraphQL schema",
212
- extras=extras or [],
213
- ) from exc
214
-
215
-
216
- def from_dict(
217
- raw_schema: dict[str, Any],
218
- *,
219
- app: Any = None,
220
- base_url: str | None = None,
221
- location: str | None = None,
222
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
223
- generation_config: GenerationConfig | None = None,
224
- code_sample_style: str = CodeSampleStyle.default().name,
225
- rate_limit: str | None = None,
226
- sanitize_output: bool = True,
227
- ) -> GraphQLSchema:
228
- """Load GraphQL schema from a Python dictionary.
229
-
230
- :param dict raw_schema: A schema to load.
231
- :param Optional[str] location: Optional schema location. Either a full URL or a filesystem path.
232
- :param Optional[str] base_url: Base URL to send requests to.
233
- :param app: A WSGI app instance.
234
- :return: GraphQLSchema
235
- """
236
- from .schemas import GraphQLSchema
237
-
238
- _code_sample_style = CodeSampleStyle.from_str(code_sample_style)
239
- hook_context = HookContext()
240
- if "data" in raw_schema:
241
- raw_schema = raw_schema["data"]
242
- dispatch("before_load_schema", hook_context, raw_schema)
243
- rate_limiter: Limiter | None = None
244
- if rate_limit is not None:
245
- rate_limiter = build_limiter(rate_limit)
246
- instance = GraphQLSchema(
247
- raw_schema,
248
- location=location,
249
- base_url=base_url,
250
- app=app,
251
- data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
252
- code_sample_style=_code_sample_style,
253
- rate_limiter=rate_limiter,
254
- sanitize_output=sanitize_output,
255
- ) # type: ignore
256
- dispatch("after_load_schema", hook_context, instance)
257
- return instance
258
-
259
-
260
- def from_wsgi(
261
- schema_path: str,
262
- app: Any,
263
- *,
264
- base_url: str | None = None,
265
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
266
- generation_config: GenerationConfig | None = None,
267
- code_sample_style: str = CodeSampleStyle.default().name,
268
- rate_limit: str | None = None,
269
- sanitize_output: bool = True,
270
- **kwargs: Any,
271
- ) -> GraphQLSchema:
272
- """Load GraphQL schema from a WSGI app.
273
-
274
- :param str schema_path: An in-app relative URL to the schema.
275
- :param app: A WSGI app instance.
276
- :param Optional[str] base_url: Base URL to send requests to.
277
- :return: GraphQLSchema
278
- """
279
- from werkzeug import Client
280
-
281
- from ...transports.responses import WSGIResponse
282
-
283
- require_relative_url(schema_path)
284
- setup_default_headers(kwargs)
285
- kwargs.setdefault("json", {"query": get_introspection_query()})
286
- client = Client(app, WSGIResponse)
287
- response = load_schema_from_url(lambda: client.post(schema_path, **kwargs))
288
- raw_schema = extract_schema_from_response(response)
289
- return from_dict(
290
- raw_schema=raw_schema,
291
- location=schema_path,
292
- base_url=base_url,
293
- app=app,
294
- data_generation_methods=data_generation_methods,
295
- code_sample_style=code_sample_style,
296
- rate_limit=rate_limit,
297
- sanitize_output=sanitize_output,
298
- )
299
-
300
-
301
- def from_asgi(
302
- schema_path: str,
303
- app: Any,
304
- *,
305
- base_url: str | None = None,
306
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
307
- generation_config: GenerationConfig | None = None,
308
- code_sample_style: str = CodeSampleStyle.default().name,
309
- rate_limit: str | None = None,
310
- sanitize_output: bool = True,
311
- **kwargs: Any,
312
- ) -> GraphQLSchema:
313
- """Load GraphQL schema from an ASGI app.
314
-
315
- :param str schema_path: An in-app relative URL to the schema.
316
- :param app: An ASGI app instance.
317
- :param Optional[str] base_url: Base URL to send requests to.
318
- """
319
- from starlette_testclient import TestClient as ASGIClient
320
-
321
- require_relative_url(schema_path)
322
- setup_default_headers(kwargs)
323
- kwargs.setdefault("json", {"query": get_introspection_query()})
324
- client = ASGIClient(app)
325
- response = load_schema_from_url(lambda: client.post(schema_path, **kwargs))
326
- raw_schema = extract_schema_from_response(response)
327
- return from_dict(
328
- raw_schema=raw_schema,
329
- location=schema_path,
330
- base_url=base_url,
331
- app=app,
332
- data_generation_methods=data_generation_methods,
333
- code_sample_style=code_sample_style,
334
- rate_limit=rate_limit,
335
- sanitize_output=sanitize_output,
336
- )
337
-
338
-
339
- def get_loader_for_app(app: Any) -> Callable:
340
- from starlette.applications import Starlette
341
-
342
- if isinstance(app, Starlette):
343
- return from_asgi
344
- return from_wsgi
@@ -1,49 +0,0 @@
1
- from __future__ import annotations
2
- import re
3
-
4
- from ...types import Filter
5
-
6
-
7
- def should_skip_method(method: str, pattern: Filter | None) -> bool:
8
- if pattern is None:
9
- return False
10
- patterns = _ensure_tuple(pattern)
11
- return method.upper() not in map(str.upper, patterns)
12
-
13
-
14
- def should_skip_endpoint(endpoint: str, pattern: Filter | None) -> bool:
15
- if pattern is None:
16
- return False
17
- return not _match_any_pattern(endpoint, pattern)
18
-
19
-
20
- def should_skip_by_tag(tags: list[str] | None, pattern: Filter | None) -> bool:
21
- if pattern is None:
22
- return False
23
- if not tags:
24
- return True
25
- patterns = _ensure_tuple(pattern)
26
- return not any(re.search(item, tag) for item in patterns for tag in tags)
27
-
28
-
29
- def should_skip_by_operation_id(operation_id: str | None, pattern: Filter | None) -> bool:
30
- if pattern is None:
31
- return False
32
- if not operation_id:
33
- return True
34
- return not _match_any_pattern(operation_id, pattern)
35
-
36
-
37
- def should_skip_deprecated(is_deprecated: bool, skip_deprecated_operations: bool) -> bool:
38
- return skip_deprecated_operations and is_deprecated
39
-
40
-
41
- def _match_any_pattern(target: str, pattern: Filter) -> bool:
42
- patterns = _ensure_tuple(pattern)
43
- return any(re.search(item, target) for item in patterns)
44
-
45
-
46
- def _ensure_tuple(item: Filter) -> list | set | tuple:
47
- if not isinstance(item, (list, set, tuple)):
48
- return (item,)
49
- return item