schemathesis 3.35.3__py3-none-any.whl → 3.35.5__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 (81) hide show
  1. schemathesis/__init__.py +5 -5
  2. schemathesis/_hypothesis.py +12 -6
  3. schemathesis/_override.py +4 -4
  4. schemathesis/auths.py +1 -1
  5. schemathesis/cli/__init__.py +19 -13
  6. schemathesis/cli/callbacks.py +6 -4
  7. schemathesis/cli/cassettes.py +67 -41
  8. schemathesis/cli/context.py +7 -6
  9. schemathesis/cli/junitxml.py +1 -1
  10. schemathesis/cli/options.py +7 -4
  11. schemathesis/cli/output/default.py +5 -5
  12. schemathesis/cli/reporting.py +4 -2
  13. schemathesis/code_samples.py +4 -3
  14. schemathesis/exceptions.py +4 -3
  15. schemathesis/extra/_flask.py +4 -1
  16. schemathesis/extra/pytest_plugin.py +6 -3
  17. schemathesis/failures.py +2 -1
  18. schemathesis/filters.py +2 -2
  19. schemathesis/generation/__init__.py +2 -2
  20. schemathesis/generation/_hypothesis.py +1 -1
  21. schemathesis/generation/coverage.py +5 -5
  22. schemathesis/graphql.py +0 -1
  23. schemathesis/hooks.py +3 -3
  24. schemathesis/lazy.py +10 -7
  25. schemathesis/loaders.py +3 -3
  26. schemathesis/models.py +39 -15
  27. schemathesis/runner/__init__.py +5 -5
  28. schemathesis/runner/events.py +1 -1
  29. schemathesis/runner/impl/context.py +58 -0
  30. schemathesis/runner/impl/core.py +54 -61
  31. schemathesis/runner/impl/solo.py +17 -20
  32. schemathesis/runner/impl/threadpool.py +65 -71
  33. schemathesis/runner/serialization.py +4 -3
  34. schemathesis/sanitization.py +2 -1
  35. schemathesis/schemas.py +18 -20
  36. schemathesis/serializers.py +2 -0
  37. schemathesis/service/client.py +1 -1
  38. schemathesis/service/events.py +4 -1
  39. schemathesis/service/extensions.py +2 -2
  40. schemathesis/service/hosts.py +4 -2
  41. schemathesis/service/models.py +3 -3
  42. schemathesis/service/report.py +3 -3
  43. schemathesis/service/serialization.py +4 -2
  44. schemathesis/specs/graphql/loaders.py +4 -3
  45. schemathesis/specs/graphql/schemas.py +4 -3
  46. schemathesis/specs/openapi/definitions.py +1 -5
  47. schemathesis/specs/openapi/examples.py +92 -2
  48. schemathesis/specs/openapi/expressions/__init__.py +7 -0
  49. schemathesis/specs/openapi/expressions/extractors.py +4 -1
  50. schemathesis/specs/openapi/expressions/nodes.py +5 -3
  51. schemathesis/specs/openapi/links.py +4 -4
  52. schemathesis/specs/openapi/loaders.py +5 -4
  53. schemathesis/specs/openapi/negative/__init__.py +5 -3
  54. schemathesis/specs/openapi/negative/mutations.py +5 -4
  55. schemathesis/specs/openapi/parameters.py +11 -8
  56. schemathesis/specs/openapi/schemas.py +9 -10
  57. schemathesis/specs/openapi/security.py +6 -4
  58. schemathesis/specs/openapi/stateful/__init__.py +2 -2
  59. schemathesis/specs/openapi/stateful/statistic.py +3 -3
  60. schemathesis/specs/openapi/stateful/types.py +3 -2
  61. schemathesis/stateful/__init__.py +3 -3
  62. schemathesis/stateful/config.py +1 -1
  63. schemathesis/stateful/context.py +3 -3
  64. schemathesis/stateful/events.py +3 -3
  65. schemathesis/stateful/runner.py +5 -4
  66. schemathesis/stateful/sink.py +1 -1
  67. schemathesis/stateful/state_machine.py +5 -5
  68. schemathesis/stateful/statistic.py +3 -1
  69. schemathesis/stateful/validation.py +1 -1
  70. schemathesis/transports/__init__.py +2 -2
  71. schemathesis/transports/asgi.py +7 -0
  72. schemathesis/transports/auth.py +2 -1
  73. schemathesis/transports/content_types.py +1 -1
  74. schemathesis/transports/responses.py +2 -1
  75. schemathesis/utils.py +4 -2
  76. {schemathesis-3.35.3.dist-info → schemathesis-3.35.5.dist-info}/METADATA +1 -1
  77. schemathesis-3.35.5.dist-info/RECORD +156 -0
  78. schemathesis-3.35.3.dist-info/RECORD +0 -154
  79. {schemathesis-3.35.3.dist-info → schemathesis-3.35.5.dist-info}/WHEEL +0 -0
  80. {schemathesis-3.35.3.dist-info → schemathesis-3.35.5.dist-info}/entry_points.txt +0 -0
  81. {schemathesis-3.35.3.dist-info → schemathesis-3.35.5.dist-info}/licenses/LICENSE +0 -0
@@ -7,26 +7,32 @@ import time
7
7
  import warnings
8
8
  from dataclasses import dataclass
9
9
  from queue import Queue
10
- from typing import Any, Callable, Generator, Iterable, cast
10
+ from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, cast
11
11
 
12
- import hypothesis
13
12
  from hypothesis.errors import HypothesisWarning
14
13
 
15
14
  from ..._hypothesis import create_test
16
- from ...generation import DataGenerationMethod, GenerationConfig
17
15
  from ...internal.result import Ok
18
- from ...models import CheckFunction, TestResultSet
16
+ from ...models import CheckFunction
19
17
  from ...stateful import Feedback, Stateful
20
- from ...targets import Target
21
18
  from ...transports.auth import get_requests_auth
22
- from ...types import RawAuth
23
19
  from ...utils import capture_hypothesis_output
24
20
  from .. import events
25
21
  from .core import BaseRunner, asgi_test, get_session, handle_schema_error, network_test, run_test, wsgi_test
26
22
 
23
+ if TYPE_CHECKING:
24
+ from .context import RunnerContext
25
+ import hypothesis
26
+
27
+ from ...generation import DataGenerationMethod, GenerationConfig
28
+ from ...models import CheckFunction
29
+ from ...targets import Target
30
+ from ...types import RawAuth
31
+
27
32
 
28
33
  def _run_task(
29
- test_template: Callable,
34
+ *,
35
+ test_func: Callable,
30
36
  tasks_queue: Queue,
31
37
  events_queue: Queue,
32
38
  generator_done: threading.Event,
@@ -35,8 +41,7 @@ def _run_task(
35
41
  data_generation_methods: Iterable[DataGenerationMethod],
36
42
  settings: hypothesis.settings,
37
43
  generation_config: GenerationConfig,
38
- seed: int | None,
39
- results: TestResultSet,
44
+ ctx: RunnerContext,
40
45
  stateful: Stateful | None,
41
46
  stateful_recursion_limit: int,
42
47
  headers: dict[str, Any] | None = None,
@@ -51,10 +56,10 @@ def _run_task(
51
56
  if recursion_level > stateful_recursion_limit:
52
57
  return
53
58
  for _result in maker(
54
- test_template,
59
+ test_func,
55
60
  settings=settings,
56
61
  generation_config=generation_config,
57
- seed=seed,
62
+ seed=ctx.seed,
58
63
  as_strategy_kwargs=as_strategy_kwargs,
59
64
  ):
60
65
  # `result` is always `Ok` here
@@ -66,7 +71,7 @@ def _run_task(
66
71
  checks,
67
72
  data_generation_methods,
68
73
  targets,
69
- results,
74
+ ctx=ctx,
70
75
  recursion_level=recursion_level,
71
76
  feedback=feedback,
72
77
  headers=headers,
@@ -89,9 +94,9 @@ def _run_task(
89
94
  operation = result.ok()
90
95
  test_function = create_test(
91
96
  operation=operation,
92
- test=test_template,
97
+ test=test_func,
93
98
  settings=settings,
94
- seed=seed,
99
+ seed=ctx.seed,
95
100
  data_generation_methods=list(data_generation_methods),
96
101
  generation_config=generation_config,
97
102
  as_strategy_kwargs=as_strategy_kwargs,
@@ -101,7 +106,7 @@ def _run_task(
101
106
  # `feedback.get_stateful_tests`
102
107
  _run_tests(lambda *_, **__: (items,)) # noqa: B023
103
108
  else:
104
- for event in handle_schema_error(result.err(), results, data_generation_methods, 0):
109
+ for event in handle_schema_error(result.err(), ctx, data_generation_methods, 0):
105
110
  events_queue.put(event)
106
111
 
107
112
 
@@ -117,8 +122,7 @@ def thread_task(
117
122
  auth: RawAuth | None,
118
123
  auth_type: str | None,
119
124
  headers: dict[str, Any] | None,
120
- seed: int | None,
121
- results: TestResultSet,
125
+ ctx: RunnerContext,
122
126
  stateful: Stateful | None,
123
127
  stateful_recursion_limit: int,
124
128
  kwargs: Any,
@@ -130,17 +134,16 @@ def thread_task(
130
134
  prepared_auth = get_requests_auth(auth, auth_type)
131
135
  with get_session(prepared_auth) as session:
132
136
  _run_task(
133
- network_test,
134
- tasks_queue,
135
- events_queue,
136
- generator_done,
137
- checks,
138
- targets,
139
- data_generation_methods,
140
- settings,
141
- generation_config,
142
- seed,
143
- results,
137
+ test_func=network_test,
138
+ tasks_queue=tasks_queue,
139
+ events_queue=events_queue,
140
+ generator_done=generator_done,
141
+ checks=checks,
142
+ targets=targets,
143
+ data_generation_methods=data_generation_methods,
144
+ settings=settings,
145
+ generation_config=generation_config,
146
+ ctx=ctx,
144
147
  stateful=stateful,
145
148
  stateful_recursion_limit=stateful_recursion_limit,
146
149
  session=session,
@@ -158,24 +161,22 @@ def wsgi_thread_task(
158
161
  data_generation_methods: Iterable[DataGenerationMethod],
159
162
  settings: hypothesis.settings,
160
163
  generation_config: GenerationConfig,
161
- seed: int | None,
162
- results: TestResultSet,
164
+ ctx: RunnerContext,
163
165
  stateful: Stateful | None,
164
166
  stateful_recursion_limit: int,
165
167
  kwargs: Any,
166
168
  ) -> None:
167
169
  _run_task(
168
- wsgi_test,
169
- tasks_queue,
170
- events_queue,
171
- generator_done,
172
- checks,
173
- targets,
174
- data_generation_methods,
175
- settings,
176
- generation_config,
177
- seed,
178
- results,
170
+ test_func=wsgi_test,
171
+ tasks_queue=tasks_queue,
172
+ events_queue=events_queue,
173
+ generator_done=generator_done,
174
+ checks=checks,
175
+ targets=targets,
176
+ data_generation_methods=data_generation_methods,
177
+ settings=settings,
178
+ generation_config=generation_config,
179
+ ctx=ctx,
179
180
  stateful=stateful,
180
181
  stateful_recursion_limit=stateful_recursion_limit,
181
182
  **kwargs,
@@ -192,24 +193,22 @@ def asgi_thread_task(
192
193
  settings: hypothesis.settings,
193
194
  generation_config: GenerationConfig,
194
195
  headers: dict[str, Any] | None,
195
- seed: int | None,
196
- results: TestResultSet,
196
+ ctx: RunnerContext,
197
197
  stateful: Stateful | None,
198
198
  stateful_recursion_limit: int,
199
199
  kwargs: Any,
200
200
  ) -> None:
201
201
  _run_task(
202
- asgi_test,
203
- tasks_queue,
204
- events_queue,
205
- generator_done,
206
- checks,
207
- targets,
208
- data_generation_methods,
209
- settings,
210
- generation_config,
211
- seed,
212
- results,
202
+ test_func=asgi_test,
203
+ tasks_queue=tasks_queue,
204
+ events_queue=events_queue,
205
+ generator_done=generator_done,
206
+ checks=checks,
207
+ targets=targets,
208
+ data_generation_methods=data_generation_methods,
209
+ settings=settings,
210
+ generation_config=generation_config,
211
+ ctx=ctx,
213
212
  stateful=stateful,
214
213
  stateful_recursion_limit=stateful_recursion_limit,
215
214
  headers=headers,
@@ -228,9 +227,7 @@ class ThreadPoolRunner(BaseRunner):
228
227
 
229
228
  workers_num: int = 2
230
229
 
231
- def _execute(
232
- self, results: TestResultSet, stop_event: threading.Event
233
- ) -> Generator[events.ExecutionEvent, None, None]:
230
+ def _execute(self, ctx: RunnerContext) -> Generator[events.ExecutionEvent, None, None]:
234
231
  """All events come from a queue where different workers push their events."""
235
232
  # Instead of generating all tests at once, we do it when there is a free worker to pick it up
236
233
  # This is extremely important for memory consumption when testing large schemas
@@ -251,7 +248,7 @@ class ThreadPoolRunner(BaseRunner):
251
248
  break
252
249
  # Events are pushed by workers via a separate queue
253
250
  events_queue: Queue = Queue()
254
- workers = self._init_workers(tasks_queue, events_queue, results, generator_done)
251
+ workers = self._init_workers(tasks_queue, events_queue, ctx, generator_done)
255
252
 
256
253
  def stop_workers() -> None:
257
254
  for worker in workers:
@@ -270,12 +267,12 @@ class ThreadPoolRunner(BaseRunner):
270
267
  is_finished = all(not worker.is_alive() for worker in workers)
271
268
  while not events_queue.empty():
272
269
  event = events_queue.get()
273
- if stop_event.is_set() or isinstance(event, events.Interrupted) or self._should_stop(event):
270
+ if ctx.is_stopped or isinstance(event, events.Interrupted) or self._should_stop(event):
274
271
  # We could still have events in the queue, but ignore them to keep the logic simple
275
272
  # for now, could be improved in the future to show more info in such corner cases
276
273
  stop_workers()
277
274
  is_finished = True
278
- if stop_event.is_set():
275
+ if ctx.is_stopped:
279
276
  # Discard the event. The invariant is: the next event after `stream.stop()` is `Finished`
280
277
  break
281
278
  yield event
@@ -292,13 +289,13 @@ class ThreadPoolRunner(BaseRunner):
292
289
  yield events.Interrupted()
293
290
 
294
291
  def _init_workers(
295
- self, tasks_queue: Queue, events_queue: Queue, results: TestResultSet, generator_done: threading.Event
292
+ self, tasks_queue: Queue, events_queue: Queue, ctx: RunnerContext, generator_done: threading.Event
296
293
  ) -> list[threading.Thread]:
297
294
  """Initialize & start workers that will execute tests."""
298
295
  workers = [
299
296
  threading.Thread(
300
297
  target=self._get_task(),
301
- kwargs=self._get_worker_kwargs(tasks_queue, events_queue, results, generator_done),
298
+ kwargs=self._get_worker_kwargs(tasks_queue, events_queue, ctx, generator_done),
302
299
  name=f"schemathesis_{num}",
303
300
  )
304
301
  for num in range(self.workers_num)
@@ -311,7 +308,7 @@ class ThreadPoolRunner(BaseRunner):
311
308
  return thread_task
312
309
 
313
310
  def _get_worker_kwargs(
314
- self, tasks_queue: Queue, events_queue: Queue, results: TestResultSet, generator_done: threading.Event
311
+ self, tasks_queue: Queue, events_queue: Queue, ctx: RunnerContext, generator_done: threading.Event
315
312
  ) -> dict[str, Any]:
316
313
  return {
317
314
  "tasks_queue": tasks_queue,
@@ -324,8 +321,7 @@ class ThreadPoolRunner(BaseRunner):
324
321
  "auth": self.auth,
325
322
  "auth_type": self.auth_type,
326
323
  "headers": self.headers,
327
- "seed": self.seed,
328
- "results": results,
324
+ "ctx": ctx,
329
325
  "stateful": self.stateful,
330
326
  "stateful_recursion_limit": self.stateful_recursion_limit,
331
327
  "data_generation_methods": self.schema.data_generation_methods,
@@ -343,7 +339,7 @@ class ThreadPoolWSGIRunner(ThreadPoolRunner):
343
339
  return wsgi_thread_task
344
340
 
345
341
  def _get_worker_kwargs(
346
- self, tasks_queue: Queue, events_queue: Queue, results: TestResultSet, generator_done: threading.Event
342
+ self, tasks_queue: Queue, events_queue: Queue, ctx: RunnerContext, generator_done: threading.Event
347
343
  ) -> dict[str, Any]:
348
344
  return {
349
345
  "tasks_queue": tasks_queue,
@@ -353,8 +349,7 @@ class ThreadPoolWSGIRunner(ThreadPoolRunner):
353
349
  "targets": self.targets,
354
350
  "settings": self.hypothesis_settings,
355
351
  "generation_config": self.generation_config,
356
- "seed": self.seed,
357
- "results": results,
352
+ "ctx": ctx,
358
353
  "stateful": self.stateful,
359
354
  "stateful_recursion_limit": self.stateful_recursion_limit,
360
355
  "data_generation_methods": self.schema.data_generation_methods,
@@ -374,7 +369,7 @@ class ThreadPoolASGIRunner(ThreadPoolRunner):
374
369
  return asgi_thread_task
375
370
 
376
371
  def _get_worker_kwargs(
377
- self, tasks_queue: Queue, events_queue: Queue, results: TestResultSet, generator_done: threading.Event
372
+ self, tasks_queue: Queue, events_queue: Queue, ctx: RunnerContext, generator_done: threading.Event
378
373
  ) -> dict[str, Any]:
379
374
  return {
380
375
  "tasks_queue": tasks_queue,
@@ -385,8 +380,7 @@ class ThreadPoolASGIRunner(ThreadPoolRunner):
385
380
  "settings": self.hypothesis_settings,
386
381
  "generation_config": self.generation_config,
387
382
  "headers": self.headers,
388
- "seed": self.seed,
389
- "results": results,
383
+ "ctx": ctx,
390
384
  "stateful": self.stateful,
391
385
  "stateful_recursion_limit": self.stateful_recursion_limit,
392
386
  "data_generation_methods": self.schema.data_generation_methods,
@@ -15,7 +15,6 @@ from ..code_samples import get_excluded_headers
15
15
  from ..exceptions import (
16
16
  BodyInGetRequestError,
17
17
  DeadlineExceeded,
18
- FailureContext,
19
18
  InternalError,
20
19
  InvalidRegularExpression,
21
20
  OperationSchemaError,
@@ -27,7 +26,6 @@ from ..exceptions import (
27
26
  format_exception,
28
27
  make_unique_by_key,
29
28
  )
30
- from ..generation import DataGenerationMethod
31
29
  from ..models import Case, Check, Interaction, Request, Response, Status, TestPhase, TestResult, TransitionId
32
30
  from ..transports import deserialize_payload, serialize_payload
33
31
 
@@ -35,6 +33,9 @@ if TYPE_CHECKING:
35
33
  import hypothesis.errors
36
34
  from requests.structures import CaseInsensitiveDict
37
35
 
36
+ from ..failures import FailureContext
37
+ from ..generation import DataGenerationMethod
38
+
38
39
 
39
40
  @dataclass
40
41
  class SerializedCase:
@@ -389,7 +390,7 @@ def _scalar_name_from_error(exception: hypothesis.errors.InvalidArgument) -> str
389
390
  @dataclass
390
391
  class SerializedInteraction:
391
392
  request: Request
392
- response: Response
393
+ response: Response | None
393
394
  checks: list[SerializedCheck]
394
395
  status: Status
395
396
  data_generation_method: DataGenerationMethod
@@ -246,6 +246,7 @@ def sanitize_serialized_case(case: SerializedCase, *, config: Config | None = No
246
246
 
247
247
  def sanitize_serialized_interaction(interaction: SerializedInteraction, *, config: Config | None = None) -> None:
248
248
  sanitize_request(interaction.request, config=config)
249
- sanitize_value(interaction.response.headers, config=config)
249
+ if interaction.response is not None:
250
+ sanitize_value(interaction.response.headers, config=config)
250
251
  for check in interaction.checks:
251
252
  sanitize_serialized_check(check, config=config)
schemathesis/schemas.py CHANGED
@@ -18,10 +18,6 @@ from typing import (
18
18
  )
19
19
  from urllib.parse import quote, unquote, urljoin, urlparse, urlsplit, urlunsplit
20
20
 
21
- import hypothesis
22
- from hypothesis.strategies import SearchStrategy
23
- from pyrate_limiter import Limiter
24
-
25
21
  from ._dependency_versions import IS_PYRATE_LIMITER_ABOVE_3
26
22
  from ._hypothesis import create_test
27
23
  from .auths import AuthStorage
@@ -48,25 +44,29 @@ from .internal.deprecation import warn_filtration_arguments
48
44
  from .internal.output import OutputConfig
49
45
  from .internal.result import Ok, Result
50
46
  from .models import APIOperation, Case
51
- from .stateful import Stateful, StatefulTest
52
- from .stateful.state_machine import APIStateMachine
53
- from .types import (
54
- Body,
55
- Cookies,
56
- Filter,
57
- FormData,
58
- GenericTest,
59
- Headers,
60
- NotSet,
61
- PathParameters,
62
- Query,
63
- Specification,
64
- )
65
47
  from .utils import PARAMETRIZE_MARKER, GivenInput, given_proxy
66
48
 
67
49
  if TYPE_CHECKING:
50
+ import hypothesis
51
+ from hypothesis.strategies import SearchStrategy
52
+ from pyrate_limiter import Limiter
53
+
54
+ from .stateful import Stateful, StatefulTest
55
+ from .stateful.state_machine import APIStateMachine
68
56
  from .transports import Transport
69
57
  from .transports.responses import GenericResponse
58
+ from .types import (
59
+ Body,
60
+ Cookies,
61
+ Filter,
62
+ FormData,
63
+ GenericTest,
64
+ Headers,
65
+ NotSet,
66
+ PathParameters,
67
+ Query,
68
+ Specification,
69
+ )
70
70
 
71
71
 
72
72
  C = TypeVar("C", bound=Case)
@@ -510,7 +510,6 @@ class BaseSchema(Mapping):
510
510
  **kwargs: Any,
511
511
  ) -> SearchStrategy:
512
512
  """Build a strategy for generating test cases for all defined API operations."""
513
- assert len(self) > 0, "No API operations found"
514
513
  strategies = [
515
514
  operation.ok().as_strategy(
516
515
  hooks=hooks,
@@ -548,7 +547,6 @@ class APIOperationMap(Mapping):
548
547
  **kwargs: Any,
549
548
  ) -> SearchStrategy:
550
549
  """Build a strategy for generating test cases for all API operations defined in this subset."""
551
- assert len(self._data) > 0, "No API operations found"
552
550
  strategies = [
553
551
  operation.as_strategy(
554
552
  hooks=hooks,
@@ -43,6 +43,8 @@ class Binary(str):
43
43
 
44
44
  data: bytes
45
45
 
46
+ __slots__ = ("data",)
47
+
46
48
 
47
49
  @dataclass
48
50
  class SerializerContext:
@@ -11,7 +11,6 @@ import requests
11
11
  from requests.adapters import HTTPAdapter, Retry
12
12
 
13
13
  from ..constants import USER_AGENT
14
- from .ci import CIProvider
15
14
  from .constants import CI_PROVIDER_HEADER, REPORT_CORRELATION_ID_HEADER, REQUEST_TIMEOUT, UPLOAD_SOURCE_HEADER
16
15
  from .metadata import Metadata, collect_dependency_versions
17
16
  from .models import (
@@ -29,6 +28,7 @@ from .models import (
29
28
 
30
29
  if TYPE_CHECKING:
31
30
  from ..runner import probes
31
+ from .ci import CIProvider
32
32
 
33
33
 
34
34
  def response_hook(response: requests.Response, **_kwargs: Any) -> None:
@@ -1,9 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
4
5
 
5
6
  from ..exceptions import format_exception
6
- from . import ci
7
+
8
+ if TYPE_CHECKING:
9
+ from . import ci
7
10
 
8
11
 
9
12
  class Event:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import base64
4
4
  import re
5
5
  from ipaddress import IPv4Network, IPv6Network
6
- from typing import TYPE_CHECKING, Any, Callable, Optional
6
+ from typing import TYPE_CHECKING, Any, Callable
7
7
 
8
8
  from ..graphql import nodes
9
9
  from ..internal.result import Err, Ok, Result
@@ -75,7 +75,7 @@ def _apply_media_types_extension(extension: MediaTypesExtension) -> None:
75
75
  _apply_simple_extension(extension, extension.media_types, media_types.register_media_type)
76
76
 
77
77
 
78
- def _find_built_in_strategy(name: str) -> Optional[st.SearchStrategy]:
78
+ def _find_built_in_strategy(name: str) -> st.SearchStrategy | None:
79
79
  """Find a built-in Hypothesis strategy by its name."""
80
80
  from hypothesis import provisional as pr
81
81
  from hypothesis import strategies as st
@@ -6,14 +6,16 @@ import enum
6
6
  import tempfile
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
- from typing import Any
9
+ from typing import TYPE_CHECKING, Any
10
10
 
11
11
  import tomli
12
12
  import tomli_w
13
13
 
14
- from ..types import PathLike
15
14
  from .constants import DEFAULT_HOSTNAME, DEFAULT_HOSTS_PATH, HOSTS_FORMAT_VERSION
16
15
 
16
+ if TYPE_CHECKING:
17
+ from ..types import PathLike
18
+
17
19
 
18
20
  @dataclass
19
21
  class HostData:
@@ -224,11 +224,11 @@ Extension = Union[
224
224
  def extension_from_dict(data: dict[str, Any]) -> Extension:
225
225
  if data["type"] == "schema_patches":
226
226
  return SchemaPatchesExtension(patches=data["patches"])
227
- elif data["type"] == "string_formats":
227
+ if data["type"] == "string_formats":
228
228
  return OpenApiStringFormatsExtension.from_dict(formats=data["items"])
229
- elif data["type"] == "scalars":
229
+ if data["type"] == "scalars":
230
230
  return GraphQLScalarsExtension.from_dict(scalars=data["items"])
231
- elif data["type"] == "media_types":
231
+ if data["type"] == "media_types":
232
232
  return MediaTypesExtension.from_dict(media_types=data["items"])
233
233
  return UnknownExtension(type=data["type"])
234
234
 
@@ -12,21 +12,21 @@ from io import BytesIO
12
12
  from queue import Queue
13
13
  from typing import TYPE_CHECKING, Any
14
14
 
15
- import click
16
-
17
15
  from ..cli.handlers import EventHandler
18
16
  from ..runner.events import Initialized, InternalError, Interrupted
19
17
  from . import ci, events, usage
20
18
  from .constants import REPORT_FORMAT_VERSION, STOP_MARKER, WORKER_JOIN_TIMEOUT
21
- from .hosts import HostData
22
19
  from .metadata import Metadata
23
20
  from .models import UploadResponse
24
21
  from .serialization import serialize_event
25
22
 
26
23
  if TYPE_CHECKING:
24
+ import click
25
+
27
26
  from ..cli.context import ExecutionContext
28
27
  from ..runner.events import ExecutionEvent
29
28
  from .client import ServiceClient
29
+ from .hosts import HostData
30
30
 
31
31
 
32
32
  @dataclass
@@ -1,12 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import asdict
4
- from typing import Any, Callable, Dict, Optional, TypeVar, cast
4
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, cast
5
5
 
6
6
  from ..internal.transformation import merge_recursively
7
7
  from ..runner import events
8
8
  from ..runner.serialization import _serialize_check
9
- from ..stateful import events as stateful_events
9
+
10
+ if TYPE_CHECKING:
11
+ from ..stateful import events as stateful_events
10
12
 
11
13
  S = TypeVar("S", bound=events.ExecutionEvent)
12
14
  SerializeFunc = Callable[[S], Optional[Dict[str, Any]]]
@@ -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,
@@ -139,6 +139,7 @@ def from_url(
139
139
  interval=WAIT_FOR_SCHEMA_INTERVAL,
140
140
  )
141
141
  def _load_schema(_uri: str, **_kwargs: Any) -> requests.Response:
142
+ _kwargs.setdefault("timeout", DEFAULT_RESPONSE_TIMEOUT / 1000)
142
143
  return requests.post(_uri, **kwargs)
143
144
 
144
145
  else:
@@ -356,8 +357,8 @@ def from_asgi(
356
357
 
357
358
 
358
359
  def get_loader_for_app(app: Any) -> Callable:
359
- from starlette.applications import Starlette
360
+ from ...transports.asgi import is_asgi_app
360
361
 
361
- if isinstance(app, Starlette):
362
+ if is_asgi_app(app):
362
363
  return from_asgi
363
364
  return from_wsgi
@@ -22,12 +22,10 @@ from urllib.parse import urlsplit, urlunsplit
22
22
 
23
23
  import graphql
24
24
  from hypothesis import strategies as st
25
- from hypothesis.strategies import SearchStrategy
26
25
  from hypothesis_graphql import strategies as gql_st
27
26
  from requests.structures import CaseInsensitiveDict
28
27
 
29
28
  from ... import auths
30
- from ...auths import AuthStorage
31
29
  from ...checks import not_a_server_error
32
30
  from ...constants import NOT_SET
33
31
  from ...exceptions import OperationNotFound, OperationSchemaError
@@ -42,13 +40,16 @@ from ...hooks import (
42
40
  from ...internal.result import Ok, Result
43
41
  from ...models import APIOperation, Case, CheckFunction, OperationDefinition
44
42
  from ...schemas import APIOperationMap, BaseSchema
45
- from ...stateful import Stateful, StatefulTest
46
43
  from ...types import Body, Cookies, Headers, NotSet, PathParameters, Query
47
44
  from ..openapi.constants import LOCATION_TO_CONTAINER
48
45
  from ._cache import OperationCache
49
46
  from .scalars import CUSTOM_SCALARS, get_extra_scalar_strategies
50
47
 
51
48
  if TYPE_CHECKING:
49
+ from hypothesis.strategies import SearchStrategy
50
+
51
+ from ...auths import AuthStorage
52
+ from ...stateful import Stateful, StatefulTest
52
53
  from ...transports.responses import GenericResponse
53
54
 
54
55
 
@@ -1907,11 +1907,7 @@ _VALIDATORS = [
1907
1907
  "OPENAPI_31_VALIDATOR",
1908
1908
  ]
1909
1909
 
1910
- __all__ = [
1911
- "SWAGGER_20",
1912
- "OPENAPI_30",
1913
- "OPENAPI_31",
1914
- ] + _VALIDATORS
1910
+ __all__ = ["SWAGGER_20", "OPENAPI_30", "OPENAPI_31", *_VALIDATORS]
1915
1911
 
1916
1912
  _imports = {
1917
1913
  "SWAGGER_20_VALIDATOR": lambda: make_validator(SWAGGER_20),