schemathesis 3.25.6__py3-none-any.whl → 3.39.7__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 (146) hide show
  1. schemathesis/__init__.py +6 -6
  2. schemathesis/_compat.py +2 -2
  3. schemathesis/_dependency_versions.py +4 -2
  4. schemathesis/_hypothesis.py +369 -56
  5. schemathesis/_lazy_import.py +1 -0
  6. schemathesis/_override.py +5 -4
  7. schemathesis/_patches.py +21 -0
  8. schemathesis/_rate_limiter.py +7 -0
  9. schemathesis/_xml.py +75 -22
  10. schemathesis/auths.py +78 -16
  11. schemathesis/checks.py +21 -9
  12. schemathesis/cli/__init__.py +783 -432
  13. schemathesis/cli/__main__.py +4 -0
  14. schemathesis/cli/callbacks.py +58 -13
  15. schemathesis/cli/cassettes.py +233 -47
  16. schemathesis/cli/constants.py +8 -2
  17. schemathesis/cli/context.py +22 -5
  18. schemathesis/cli/debug.py +2 -1
  19. schemathesis/cli/handlers.py +4 -1
  20. schemathesis/cli/junitxml.py +103 -22
  21. schemathesis/cli/options.py +15 -4
  22. schemathesis/cli/output/default.py +258 -112
  23. schemathesis/cli/output/short.py +23 -8
  24. schemathesis/cli/reporting.py +79 -0
  25. schemathesis/cli/sanitization.py +6 -0
  26. schemathesis/code_samples.py +5 -3
  27. schemathesis/constants.py +1 -0
  28. schemathesis/contrib/openapi/__init__.py +1 -1
  29. schemathesis/contrib/openapi/fill_missing_examples.py +3 -1
  30. schemathesis/contrib/openapi/formats/uuid.py +2 -1
  31. schemathesis/contrib/unique_data.py +3 -3
  32. schemathesis/exceptions.py +76 -65
  33. schemathesis/experimental/__init__.py +35 -0
  34. schemathesis/extra/_aiohttp.py +1 -0
  35. schemathesis/extra/_flask.py +4 -1
  36. schemathesis/extra/_server.py +1 -0
  37. schemathesis/extra/pytest_plugin.py +17 -25
  38. schemathesis/failures.py +77 -9
  39. schemathesis/filters.py +185 -8
  40. schemathesis/fixups/__init__.py +1 -0
  41. schemathesis/fixups/fast_api.py +2 -2
  42. schemathesis/fixups/utf8_bom.py +1 -2
  43. schemathesis/generation/__init__.py +20 -36
  44. schemathesis/generation/_hypothesis.py +59 -0
  45. schemathesis/generation/_methods.py +44 -0
  46. schemathesis/generation/coverage.py +931 -0
  47. schemathesis/graphql.py +0 -1
  48. schemathesis/hooks.py +89 -12
  49. schemathesis/internal/checks.py +84 -0
  50. schemathesis/internal/copy.py +22 -3
  51. schemathesis/internal/deprecation.py +6 -2
  52. schemathesis/internal/diff.py +15 -0
  53. schemathesis/internal/extensions.py +27 -0
  54. schemathesis/internal/jsonschema.py +2 -1
  55. schemathesis/internal/output.py +68 -0
  56. schemathesis/internal/result.py +1 -1
  57. schemathesis/internal/transformation.py +11 -0
  58. schemathesis/lazy.py +138 -25
  59. schemathesis/loaders.py +7 -5
  60. schemathesis/models.py +318 -211
  61. schemathesis/parameters.py +4 -0
  62. schemathesis/runner/__init__.py +50 -15
  63. schemathesis/runner/events.py +65 -5
  64. schemathesis/runner/impl/context.py +104 -0
  65. schemathesis/runner/impl/core.py +388 -177
  66. schemathesis/runner/impl/solo.py +19 -29
  67. schemathesis/runner/impl/threadpool.py +70 -79
  68. schemathesis/runner/probes.py +11 -9
  69. schemathesis/runner/serialization.py +150 -17
  70. schemathesis/sanitization.py +5 -1
  71. schemathesis/schemas.py +170 -102
  72. schemathesis/serializers.py +7 -2
  73. schemathesis/service/ci.py +1 -0
  74. schemathesis/service/client.py +39 -6
  75. schemathesis/service/events.py +5 -1
  76. schemathesis/service/extensions.py +224 -0
  77. schemathesis/service/hosts.py +6 -2
  78. schemathesis/service/metadata.py +25 -0
  79. schemathesis/service/models.py +211 -2
  80. schemathesis/service/report.py +6 -6
  81. schemathesis/service/serialization.py +45 -71
  82. schemathesis/service/usage.py +1 -0
  83. schemathesis/specs/graphql/_cache.py +26 -0
  84. schemathesis/specs/graphql/loaders.py +25 -5
  85. schemathesis/specs/graphql/nodes.py +1 -0
  86. schemathesis/specs/graphql/scalars.py +2 -2
  87. schemathesis/specs/graphql/schemas.py +130 -100
  88. schemathesis/specs/graphql/validation.py +1 -2
  89. schemathesis/specs/openapi/__init__.py +1 -0
  90. schemathesis/specs/openapi/_cache.py +123 -0
  91. schemathesis/specs/openapi/_hypothesis.py +78 -60
  92. schemathesis/specs/openapi/checks.py +504 -25
  93. schemathesis/specs/openapi/converter.py +31 -4
  94. schemathesis/specs/openapi/definitions.py +10 -17
  95. schemathesis/specs/openapi/examples.py +126 -12
  96. schemathesis/specs/openapi/expressions/__init__.py +37 -2
  97. schemathesis/specs/openapi/expressions/context.py +1 -1
  98. schemathesis/specs/openapi/expressions/extractors.py +26 -0
  99. schemathesis/specs/openapi/expressions/lexer.py +20 -18
  100. schemathesis/specs/openapi/expressions/nodes.py +29 -6
  101. schemathesis/specs/openapi/expressions/parser.py +26 -5
  102. schemathesis/specs/openapi/formats.py +44 -0
  103. schemathesis/specs/openapi/links.py +125 -42
  104. schemathesis/specs/openapi/loaders.py +77 -36
  105. schemathesis/specs/openapi/media_types.py +34 -0
  106. schemathesis/specs/openapi/negative/__init__.py +6 -3
  107. schemathesis/specs/openapi/negative/mutations.py +21 -6
  108. schemathesis/specs/openapi/parameters.py +39 -25
  109. schemathesis/specs/openapi/patterns.py +137 -0
  110. schemathesis/specs/openapi/references.py +37 -7
  111. schemathesis/specs/openapi/schemas.py +360 -241
  112. schemathesis/specs/openapi/security.py +25 -7
  113. schemathesis/specs/openapi/serialization.py +1 -0
  114. schemathesis/specs/openapi/stateful/__init__.py +198 -70
  115. schemathesis/specs/openapi/stateful/statistic.py +198 -0
  116. schemathesis/specs/openapi/stateful/types.py +14 -0
  117. schemathesis/specs/openapi/utils.py +6 -1
  118. schemathesis/specs/openapi/validation.py +1 -0
  119. schemathesis/stateful/__init__.py +35 -21
  120. schemathesis/stateful/config.py +97 -0
  121. schemathesis/stateful/context.py +135 -0
  122. schemathesis/stateful/events.py +274 -0
  123. schemathesis/stateful/runner.py +309 -0
  124. schemathesis/stateful/sink.py +68 -0
  125. schemathesis/stateful/state_machine.py +67 -38
  126. schemathesis/stateful/statistic.py +22 -0
  127. schemathesis/stateful/validation.py +100 -0
  128. schemathesis/targets.py +33 -1
  129. schemathesis/throttling.py +25 -5
  130. schemathesis/transports/__init__.py +354 -0
  131. schemathesis/transports/asgi.py +7 -0
  132. schemathesis/transports/auth.py +25 -2
  133. schemathesis/transports/content_types.py +3 -1
  134. schemathesis/transports/headers.py +2 -1
  135. schemathesis/transports/responses.py +9 -4
  136. schemathesis/types.py +9 -0
  137. schemathesis/utils.py +11 -16
  138. schemathesis-3.39.7.dist-info/METADATA +293 -0
  139. schemathesis-3.39.7.dist-info/RECORD +160 -0
  140. {schemathesis-3.25.6.dist-info → schemathesis-3.39.7.dist-info}/WHEEL +1 -1
  141. schemathesis/specs/openapi/filters.py +0 -49
  142. schemathesis/specs/openapi/stateful/links.py +0 -92
  143. schemathesis-3.25.6.dist-info/METADATA +0 -356
  144. schemathesis-3.25.6.dist-info/RECORD +0 -134
  145. {schemathesis-3.25.6.dist-info → schemathesis-3.39.7.dist-info}/entry_points.txt +0 -0
  146. {schemathesis-3.25.6.dist-info → schemathesis-3.39.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,14 @@
1
1
  from __future__ import annotations
2
+
2
3
  from dataclasses import asdict
3
- from typing import Any, Callable, Dict, Optional, TypeVar, cast
4
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, cast
4
5
 
5
- from ..models import Response
6
- from ..runner import events
7
- from ..runner.serialization import SerializedCase
8
6
  from ..internal.transformation import merge_recursively
7
+ from ..runner import events
8
+ from ..runner.serialization import _serialize_check
9
+
10
+ if TYPE_CHECKING:
11
+ from ..stateful import events as stateful_events
9
12
 
10
13
  S = TypeVar("S", bound=events.ExecutionEvent)
11
14
  SerializeFunc = Callable[[S], Optional[Dict[str, Any]]]
@@ -28,32 +31,19 @@ def serialize_after_probing(event: events.AfterProbing) -> dict[str, Any] | None
28
31
  return {"probes": [probe.serialize() for probe in probes]}
29
32
 
30
33
 
31
- def serialize_before_execution(event: events.BeforeExecution) -> dict[str, Any] | None:
32
- return {
33
- "correlation_id": event.correlation_id,
34
- "verbose_name": event.verbose_name,
35
- "data_generation_method": event.data_generation_method,
36
- }
34
+ def serialize_before_analysis(_: events.BeforeAnalysis) -> None:
35
+ return None
37
36
 
38
37
 
39
- def _serialize_case(case: SerializedCase) -> dict[str, Any]:
40
- return {
41
- "verbose_name": case.verbose_name,
42
- "path_template": case.path_template,
43
- "path_parameters": stringify_path_parameters(case.path_parameters),
44
- "query": prepare_query(case.query),
45
- "cookies": case.cookies,
46
- "media_type": case.media_type,
47
- }
38
+ def serialize_after_analysis(event: events.AfterAnalysis) -> dict[str, Any] | None:
39
+ return event._serialize()
48
40
 
49
41
 
50
- def _serialize_response(response: Response) -> dict[str, Any]:
42
+ def serialize_before_execution(event: events.BeforeExecution) -> dict[str, Any] | None:
51
43
  return {
52
- "status_code": response.status_code,
53
- "headers": response.headers,
54
- "body": response.body,
55
- "encoding": response.encoding,
56
- "elapsed": response.elapsed,
44
+ "correlation_id": event.correlation_id,
45
+ "verbose_name": event.verbose_name,
46
+ "data_generation_method": event.data_generation_method,
57
47
  }
58
48
 
59
49
 
@@ -65,27 +55,7 @@ def serialize_after_execution(event: events.AfterExecution) -> dict[str, Any] |
65
55
  "elapsed_time": event.elapsed_time,
66
56
  "data_generation_method": event.data_generation_method,
67
57
  "result": {
68
- "checks": [
69
- {
70
- "name": check.name,
71
- "value": check.value,
72
- "request": {
73
- "method": check.request.method,
74
- "uri": check.request.uri,
75
- "body": check.request.body,
76
- "headers": check.request.headers,
77
- },
78
- "response": _serialize_response(check.response) if check.response is not None else None,
79
- "example": _serialize_case(check.example),
80
- "message": check.message,
81
- "context": asdict(check.context) if check.context is not None else None, # type: ignore
82
- "history": [
83
- {"case": _serialize_case(entry.case), "response": _serialize_response(entry.response)}
84
- for entry in check.history
85
- ],
86
- }
87
- for check in event.result.checks
88
- ],
58
+ "checks": [_serialize_check(check) for check in event.result.checks],
89
59
  "errors": [asdict(error) for error in event.result.errors],
90
60
  "skip_reason": event.result.skip_reason,
91
61
  },
@@ -123,14 +93,35 @@ def serialize_finished(event: events.Finished) -> dict[str, Any] | None:
123
93
  }
124
94
 
125
95
 
96
+ def serialize_stateful_event(event: events.StatefulEvent) -> dict[str, Any] | None:
97
+ return _serialize_stateful_event(event.data)
98
+
99
+
100
+ def _serialize_stateful_event(event: stateful_events.StatefulEvent) -> dict[str, Any] | None:
101
+ return {"data": {event.__class__.__name__: event.asdict()}}
102
+
103
+
104
+ def serialize_after_stateful_execution(event: events.AfterStatefulExecution) -> dict[str, Any] | None:
105
+ return {
106
+ "status": event.status,
107
+ "data_generation_method": event.data_generation_method,
108
+ "result": asdict(event.result),
109
+ "elapsed_time": event.elapsed_time,
110
+ }
111
+
112
+
126
113
  SERIALIZER_MAP = {
127
114
  events.Initialized: serialize_initialized,
128
115
  events.BeforeProbing: serialize_before_probing,
129
116
  events.AfterProbing: serialize_after_probing,
117
+ events.BeforeAnalysis: serialize_before_analysis,
118
+ events.AfterAnalysis: serialize_after_analysis,
130
119
  events.BeforeExecution: serialize_before_execution,
131
120
  events.AfterExecution: serialize_after_execution,
132
121
  events.Interrupted: serialize_interrupted,
133
122
  events.InternalError: serialize_internal_error,
123
+ events.StatefulEvent: serialize_stateful_event,
124
+ events.AfterStatefulExecution: serialize_after_stateful_execution,
134
125
  events.Finished: serialize_finished,
135
126
  }
136
127
 
@@ -141,10 +132,14 @@ def serialize_event(
141
132
  on_initialized: SerializeFunc | None = None,
142
133
  on_before_probing: SerializeFunc | None = None,
143
134
  on_after_probing: SerializeFunc | None = None,
135
+ on_before_analysis: SerializeFunc | None = None,
136
+ on_after_analysis: SerializeFunc | None = None,
144
137
  on_before_execution: SerializeFunc | None = None,
145
138
  on_after_execution: SerializeFunc | None = None,
146
139
  on_interrupted: SerializeFunc | None = None,
147
140
  on_internal_error: SerializeFunc | None = None,
141
+ on_stateful_event: SerializeFunc | None = None,
142
+ on_after_stateful_execution: SerializeFunc | None = None,
148
143
  on_finished: SerializeFunc | None = None,
149
144
  extra: dict[str, Any] | None = None,
150
145
  ) -> dict[str, dict[str, Any] | None]:
@@ -154,10 +149,14 @@ def serialize_event(
154
149
  events.Initialized: on_initialized,
155
150
  events.BeforeProbing: on_before_probing,
156
151
  events.AfterProbing: on_after_probing,
152
+ events.BeforeAnalysis: on_before_analysis,
153
+ events.AfterAnalysis: on_after_analysis,
157
154
  events.BeforeExecution: on_before_execution,
158
155
  events.AfterExecution: on_after_execution,
159
156
  events.Interrupted: on_interrupted,
160
157
  events.InternalError: on_internal_error,
158
+ events.StatefulEvent: on_stateful_event,
159
+ events.AfterStatefulExecution: on_after_stateful_execution,
161
160
  events.Finished: on_finished,
162
161
  }.get(event.__class__)
163
162
  if serializer is None:
@@ -172,28 +171,3 @@ def serialize_event(
172
171
  data = merge_recursively(data, extra)
173
172
  # Externally tagged structure
174
173
  return {event.__class__.__name__: data}
175
-
176
-
177
- def stringify_path_parameters(path_parameters: dict[str, Any] | None) -> dict[str, str]:
178
- """Cast all path parameter values to strings.
179
-
180
- Path parameter values may be of arbitrary type, but to display them properly they should be casted to strings.
181
- """
182
- return {key: str(value) for key, value in (path_parameters or {}).items()}
183
-
184
-
185
- def prepare_query(query: dict[str, Any] | None) -> dict[str, list[str]]:
186
- """Convert all query values to list of strings.
187
-
188
- Query parameters may be generated in different shapes, including integers, strings, list of strings, etc.
189
- It can also be an object, if the schema contains an object, but `style` and `explode` combo is not applicable.
190
- """
191
-
192
- def to_list_of_strings(value: Any) -> list[str]:
193
- if isinstance(value, list):
194
- return list(map(str, value))
195
- if isinstance(value, str):
196
- return [value]
197
- return [str(value)]
198
-
199
- return {key: to_list_of_strings(value) for key, value in (query or {}).items()}
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  import sys
3
4
  from typing import Any
4
5
 
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ...models import APIOperation
8
+ from ...schemas import APIOperationMap
9
+
10
+
11
+ @dataclass
12
+ class OperationCache:
13
+ _maps: dict[str, APIOperationMap] = field(default_factory=dict)
14
+ _operations: dict[str, APIOperation] = field(default_factory=dict)
15
+
16
+ def get_map(self, key: str) -> APIOperationMap | None:
17
+ return self._maps.get(key)
18
+
19
+ def insert_map(self, key: str, value: APIOperationMap) -> None:
20
+ self._maps[key] = value
21
+
22
+ def get_operation(self, key: str) -> APIOperation | None:
23
+ return self._operations.get(key)
24
+
25
+ def insert_operation(self, key: str, value: APIOperation) -> None:
26
+ self._operations[key] = value
@@ -7,7 +7,7 @@ from json import JSONDecodeError
7
7
  from typing import IO, TYPE_CHECKING, Any, Callable, Dict, NoReturn, cast
8
8
 
9
9
  from ...code_samples import CodeSampleStyle
10
- from ...constants import WAIT_FOR_SCHEMA_INTERVAL
10
+ from ...constants import DEFAULT_RESPONSE_TIMEOUT, WAIT_FOR_SCHEMA_INTERVAL
11
11
  from ...exceptions import SchemaError, SchemaErrorType
12
12
  from ...generation import (
13
13
  DEFAULT_DATA_GENERATION_METHODS,
@@ -16,11 +16,12 @@ from ...generation import (
16
16
  GenerationConfig,
17
17
  )
18
18
  from ...hooks import HookContext, dispatch
19
+ from ...internal.output import OutputConfig
19
20
  from ...internal.validation import require_relative_url
20
21
  from ...loaders import load_schema_from_url
21
22
  from ...throttling import build_limiter
22
23
  from ...transports.headers import setup_default_headers
23
- from ...types import PathLike
24
+ from ...types import PathLike, Specification
24
25
 
25
26
  if TYPE_CHECKING:
26
27
  from graphql import DocumentNode
@@ -52,6 +53,7 @@ def from_path(
52
53
  base_url: str | None = None,
53
54
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
54
55
  generation_config: GenerationConfig | None = None,
56
+ output_config: OutputConfig | None = None,
55
57
  code_sample_style: str = CodeSampleStyle.default().name,
56
58
  rate_limit: str | None = None,
57
59
  encoding: str = "utf8",
@@ -69,6 +71,8 @@ def from_path(
69
71
  base_url=base_url,
70
72
  data_generation_methods=data_generation_methods,
71
73
  code_sample_style=code_sample_style,
74
+ generation_config=generation_config,
75
+ output_config=output_config,
72
76
  location=pathlib.Path(path).absolute().as_uri(),
73
77
  rate_limit=rate_limit,
74
78
  sanitize_output=sanitize_output,
@@ -135,11 +139,12 @@ def from_url(
135
139
  interval=WAIT_FOR_SCHEMA_INTERVAL,
136
140
  )
137
141
  def _load_schema(_uri: str, **_kwargs: Any) -> requests.Response:
138
- return requests.post(_uri, **kwargs)
142
+ return requests.post(_uri, **_kwargs)
139
143
 
140
144
  else:
141
145
  _load_schema = requests.post
142
146
 
147
+ kwargs.setdefault("timeout", DEFAULT_RESPONSE_TIMEOUT / 1000)
143
148
  response = load_schema_from_url(lambda: _load_schema(url, **kwargs))
144
149
  raw_schema = extract_schema_from_response(response)
145
150
  return from_dict(
@@ -161,6 +166,7 @@ def from_file(
161
166
  base_url: str | None = None,
162
167
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
163
168
  generation_config: GenerationConfig | None = None,
169
+ output_config: OutputConfig | None = None,
164
170
  code_sample_style: str = CodeSampleStyle.default().name,
165
171
  location: str | None = None,
166
172
  rate_limit: str | None = None,
@@ -198,6 +204,8 @@ def from_file(
198
204
  app=app,
199
205
  base_url=base_url,
200
206
  data_generation_methods=data_generation_methods,
207
+ generation_config=generation_config,
208
+ output_config=output_config,
201
209
  code_sample_style=code_sample_style,
202
210
  location=location,
203
211
  rate_limit=rate_limit,
@@ -221,6 +229,7 @@ def from_dict(
221
229
  location: str | None = None,
222
230
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
223
231
  generation_config: GenerationConfig | None = None,
232
+ output_config: OutputConfig | None = None,
224
233
  code_sample_style: str = CodeSampleStyle.default().name,
225
234
  rate_limit: str | None = None,
226
235
  sanitize_output: bool = True,
@@ -233,6 +242,7 @@ def from_dict(
233
242
  :param app: A WSGI app instance.
234
243
  :return: GraphQLSchema
235
244
  """
245
+ from ... import transports
236
246
  from .schemas import GraphQLSchema
237
247
 
238
248
  _code_sample_style = CodeSampleStyle.from_str(code_sample_style)
@@ -245,13 +255,17 @@ def from_dict(
245
255
  rate_limiter = build_limiter(rate_limit)
246
256
  instance = GraphQLSchema(
247
257
  raw_schema,
258
+ specification=Specification.GRAPHQL,
248
259
  location=location,
249
260
  base_url=base_url,
250
261
  app=app,
251
262
  data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
263
+ generation_config=generation_config or GenerationConfig(),
264
+ output_config=output_config or OutputConfig(),
252
265
  code_sample_style=_code_sample_style,
253
266
  rate_limiter=rate_limiter,
254
267
  sanitize_output=sanitize_output,
268
+ transport=transports.get(app),
255
269
  ) # type: ignore
256
270
  dispatch("after_load_schema", hook_context, instance)
257
271
  return instance
@@ -264,6 +278,7 @@ def from_wsgi(
264
278
  base_url: str | None = None,
265
279
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
266
280
  generation_config: GenerationConfig | None = None,
281
+ output_config: OutputConfig | None = None,
267
282
  code_sample_style: str = CodeSampleStyle.default().name,
268
283
  rate_limit: str | None = None,
269
284
  sanitize_output: bool = True,
@@ -292,6 +307,8 @@ def from_wsgi(
292
307
  base_url=base_url,
293
308
  app=app,
294
309
  data_generation_methods=data_generation_methods,
310
+ generation_config=generation_config,
311
+ output_config=output_config,
295
312
  code_sample_style=code_sample_style,
296
313
  rate_limit=rate_limit,
297
314
  sanitize_output=sanitize_output,
@@ -305,6 +322,7 @@ def from_asgi(
305
322
  base_url: str | None = None,
306
323
  data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
307
324
  generation_config: GenerationConfig | None = None,
325
+ output_config: OutputConfig | None = None,
308
326
  code_sample_style: str = CodeSampleStyle.default().name,
309
327
  rate_limit: str | None = None,
310
328
  sanitize_output: bool = True,
@@ -330,6 +348,8 @@ def from_asgi(
330
348
  base_url=base_url,
331
349
  app=app,
332
350
  data_generation_methods=data_generation_methods,
351
+ generation_config=generation_config,
352
+ output_config=output_config,
333
353
  code_sample_style=code_sample_style,
334
354
  rate_limit=rate_limit,
335
355
  sanitize_output=sanitize_output,
@@ -337,8 +357,8 @@ def from_asgi(
337
357
 
338
358
 
339
359
  def get_loader_for_app(app: Any) -> Callable:
340
- from starlette.applications import Starlette
360
+ from ...transports.asgi import is_asgi_app
341
361
 
342
- if isinstance(app, Starlette):
362
+ if is_asgi_app(app):
343
363
  return from_asgi
344
364
  return from_wsgi
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from functools import lru_cache
4
4
  from typing import TYPE_CHECKING
5
5
 
6
-
7
6
  from ...exceptions import UsageError
8
7
 
9
8
  if TYPE_CHECKING:
@@ -31,9 +30,10 @@ def scalar(name: str, strategy: st.SearchStrategy[graphql.ValueNode]) -> None:
31
30
  @lru_cache
32
31
  def get_extra_scalar_strategies() -> dict[str, st.SearchStrategy]:
33
32
  """Get all extra GraphQL strategies."""
34
- from . import nodes
35
33
  from hypothesis import strategies as st
36
34
 
35
+ from . import nodes
36
+
37
37
  dates = st.dates().map(str)
38
38
  times = st.times().map("%sZ".__mod__)
39
39