schemathesis 3.15.4__py3-none-any.whl → 4.4.2__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 (251) hide show
  1. schemathesis/__init__.py +53 -25
  2. schemathesis/auths.py +507 -0
  3. schemathesis/checks.py +190 -25
  4. schemathesis/cli/__init__.py +27 -1219
  5. schemathesis/cli/__main__.py +4 -0
  6. schemathesis/cli/commands/__init__.py +133 -0
  7. schemathesis/cli/commands/data.py +10 -0
  8. schemathesis/cli/commands/run/__init__.py +602 -0
  9. schemathesis/cli/commands/run/context.py +228 -0
  10. schemathesis/cli/commands/run/events.py +60 -0
  11. schemathesis/cli/commands/run/executor.py +157 -0
  12. schemathesis/cli/commands/run/filters.py +53 -0
  13. schemathesis/cli/commands/run/handlers/__init__.py +46 -0
  14. schemathesis/cli/commands/run/handlers/base.py +45 -0
  15. schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
  16. schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
  17. schemathesis/cli/commands/run/handlers/output.py +1750 -0
  18. schemathesis/cli/commands/run/loaders.py +118 -0
  19. schemathesis/cli/commands/run/validation.py +256 -0
  20. schemathesis/cli/constants.py +5 -0
  21. schemathesis/cli/core.py +19 -0
  22. schemathesis/cli/ext/fs.py +16 -0
  23. schemathesis/cli/ext/groups.py +203 -0
  24. schemathesis/cli/ext/options.py +81 -0
  25. schemathesis/config/__init__.py +202 -0
  26. schemathesis/config/_auth.py +51 -0
  27. schemathesis/config/_checks.py +268 -0
  28. schemathesis/config/_diff_base.py +101 -0
  29. schemathesis/config/_env.py +21 -0
  30. schemathesis/config/_error.py +163 -0
  31. schemathesis/config/_generation.py +157 -0
  32. schemathesis/config/_health_check.py +24 -0
  33. schemathesis/config/_operations.py +335 -0
  34. schemathesis/config/_output.py +171 -0
  35. schemathesis/config/_parameters.py +19 -0
  36. schemathesis/config/_phases.py +253 -0
  37. schemathesis/config/_projects.py +543 -0
  38. schemathesis/config/_rate_limit.py +17 -0
  39. schemathesis/config/_report.py +120 -0
  40. schemathesis/config/_validator.py +9 -0
  41. schemathesis/config/_warnings.py +89 -0
  42. schemathesis/config/schema.json +975 -0
  43. schemathesis/core/__init__.py +72 -0
  44. schemathesis/core/adapter.py +34 -0
  45. schemathesis/core/compat.py +32 -0
  46. schemathesis/core/control.py +2 -0
  47. schemathesis/core/curl.py +100 -0
  48. schemathesis/core/deserialization.py +210 -0
  49. schemathesis/core/errors.py +588 -0
  50. schemathesis/core/failures.py +316 -0
  51. schemathesis/core/fs.py +19 -0
  52. schemathesis/core/hooks.py +20 -0
  53. schemathesis/core/jsonschema/__init__.py +13 -0
  54. schemathesis/core/jsonschema/bundler.py +183 -0
  55. schemathesis/core/jsonschema/keywords.py +40 -0
  56. schemathesis/core/jsonschema/references.py +222 -0
  57. schemathesis/core/jsonschema/types.py +41 -0
  58. schemathesis/core/lazy_import.py +15 -0
  59. schemathesis/core/loaders.py +107 -0
  60. schemathesis/core/marks.py +66 -0
  61. schemathesis/core/media_types.py +79 -0
  62. schemathesis/core/output/__init__.py +46 -0
  63. schemathesis/core/output/sanitization.py +54 -0
  64. schemathesis/core/parameters.py +45 -0
  65. schemathesis/core/rate_limit.py +60 -0
  66. schemathesis/core/registries.py +34 -0
  67. schemathesis/core/result.py +27 -0
  68. schemathesis/core/schema_analysis.py +17 -0
  69. schemathesis/core/shell.py +203 -0
  70. schemathesis/core/transforms.py +144 -0
  71. schemathesis/core/transport.py +223 -0
  72. schemathesis/core/validation.py +73 -0
  73. schemathesis/core/version.py +7 -0
  74. schemathesis/engine/__init__.py +28 -0
  75. schemathesis/engine/context.py +152 -0
  76. schemathesis/engine/control.py +44 -0
  77. schemathesis/engine/core.py +201 -0
  78. schemathesis/engine/errors.py +446 -0
  79. schemathesis/engine/events.py +284 -0
  80. schemathesis/engine/observations.py +42 -0
  81. schemathesis/engine/phases/__init__.py +108 -0
  82. schemathesis/engine/phases/analysis.py +28 -0
  83. schemathesis/engine/phases/probes.py +172 -0
  84. schemathesis/engine/phases/stateful/__init__.py +68 -0
  85. schemathesis/engine/phases/stateful/_executor.py +364 -0
  86. schemathesis/engine/phases/stateful/context.py +85 -0
  87. schemathesis/engine/phases/unit/__init__.py +220 -0
  88. schemathesis/engine/phases/unit/_executor.py +459 -0
  89. schemathesis/engine/phases/unit/_pool.py +82 -0
  90. schemathesis/engine/recorder.py +254 -0
  91. schemathesis/errors.py +47 -0
  92. schemathesis/filters.py +395 -0
  93. schemathesis/generation/__init__.py +25 -0
  94. schemathesis/generation/case.py +478 -0
  95. schemathesis/generation/coverage.py +1528 -0
  96. schemathesis/generation/hypothesis/__init__.py +121 -0
  97. schemathesis/generation/hypothesis/builder.py +992 -0
  98. schemathesis/generation/hypothesis/examples.py +56 -0
  99. schemathesis/generation/hypothesis/given.py +66 -0
  100. schemathesis/generation/hypothesis/reporting.py +285 -0
  101. schemathesis/generation/meta.py +227 -0
  102. schemathesis/generation/metrics.py +93 -0
  103. schemathesis/generation/modes.py +20 -0
  104. schemathesis/generation/overrides.py +127 -0
  105. schemathesis/generation/stateful/__init__.py +37 -0
  106. schemathesis/generation/stateful/state_machine.py +294 -0
  107. schemathesis/graphql/__init__.py +15 -0
  108. schemathesis/graphql/checks.py +109 -0
  109. schemathesis/graphql/loaders.py +285 -0
  110. schemathesis/hooks.py +270 -91
  111. schemathesis/openapi/__init__.py +13 -0
  112. schemathesis/openapi/checks.py +467 -0
  113. schemathesis/openapi/generation/__init__.py +0 -0
  114. schemathesis/openapi/generation/filters.py +72 -0
  115. schemathesis/openapi/loaders.py +315 -0
  116. schemathesis/pytest/__init__.py +5 -0
  117. schemathesis/pytest/control_flow.py +7 -0
  118. schemathesis/pytest/lazy.py +341 -0
  119. schemathesis/pytest/loaders.py +36 -0
  120. schemathesis/pytest/plugin.py +357 -0
  121. schemathesis/python/__init__.py +0 -0
  122. schemathesis/python/asgi.py +12 -0
  123. schemathesis/python/wsgi.py +12 -0
  124. schemathesis/schemas.py +682 -257
  125. schemathesis/specs/graphql/__init__.py +0 -1
  126. schemathesis/specs/graphql/nodes.py +26 -2
  127. schemathesis/specs/graphql/scalars.py +77 -12
  128. schemathesis/specs/graphql/schemas.py +367 -148
  129. schemathesis/specs/graphql/validation.py +33 -0
  130. schemathesis/specs/openapi/__init__.py +9 -1
  131. schemathesis/specs/openapi/_hypothesis.py +555 -318
  132. schemathesis/specs/openapi/adapter/__init__.py +10 -0
  133. schemathesis/specs/openapi/adapter/parameters.py +729 -0
  134. schemathesis/specs/openapi/adapter/protocol.py +59 -0
  135. schemathesis/specs/openapi/adapter/references.py +19 -0
  136. schemathesis/specs/openapi/adapter/responses.py +368 -0
  137. schemathesis/specs/openapi/adapter/security.py +144 -0
  138. schemathesis/specs/openapi/adapter/v2.py +30 -0
  139. schemathesis/specs/openapi/adapter/v3_0.py +30 -0
  140. schemathesis/specs/openapi/adapter/v3_1.py +30 -0
  141. schemathesis/specs/openapi/analysis.py +96 -0
  142. schemathesis/specs/openapi/checks.py +748 -82
  143. schemathesis/specs/openapi/converter.py +176 -37
  144. schemathesis/specs/openapi/definitions.py +599 -4
  145. schemathesis/specs/openapi/examples.py +581 -165
  146. schemathesis/specs/openapi/expressions/__init__.py +52 -5
  147. schemathesis/specs/openapi/expressions/extractors.py +25 -0
  148. schemathesis/specs/openapi/expressions/lexer.py +34 -31
  149. schemathesis/specs/openapi/expressions/nodes.py +97 -46
  150. schemathesis/specs/openapi/expressions/parser.py +35 -13
  151. schemathesis/specs/openapi/formats.py +122 -0
  152. schemathesis/specs/openapi/media_types.py +75 -0
  153. schemathesis/specs/openapi/negative/__init__.py +93 -73
  154. schemathesis/specs/openapi/negative/mutations.py +294 -103
  155. schemathesis/specs/openapi/negative/utils.py +0 -9
  156. schemathesis/specs/openapi/patterns.py +458 -0
  157. schemathesis/specs/openapi/references.py +60 -81
  158. schemathesis/specs/openapi/schemas.py +647 -666
  159. schemathesis/specs/openapi/serialization.py +53 -30
  160. schemathesis/specs/openapi/stateful/__init__.py +403 -68
  161. schemathesis/specs/openapi/stateful/control.py +87 -0
  162. schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
  163. schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
  164. schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
  165. schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
  166. schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
  167. schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
  168. schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
  169. schemathesis/specs/openapi/stateful/inference.py +254 -0
  170. schemathesis/specs/openapi/stateful/links.py +219 -78
  171. schemathesis/specs/openapi/types/__init__.py +3 -0
  172. schemathesis/specs/openapi/types/common.py +23 -0
  173. schemathesis/specs/openapi/types/v2.py +129 -0
  174. schemathesis/specs/openapi/types/v3.py +134 -0
  175. schemathesis/specs/openapi/utils.py +7 -6
  176. schemathesis/specs/openapi/warnings.py +75 -0
  177. schemathesis/transport/__init__.py +224 -0
  178. schemathesis/transport/asgi.py +26 -0
  179. schemathesis/transport/prepare.py +126 -0
  180. schemathesis/transport/requests.py +278 -0
  181. schemathesis/transport/serialization.py +329 -0
  182. schemathesis/transport/wsgi.py +175 -0
  183. schemathesis-4.4.2.dist-info/METADATA +213 -0
  184. schemathesis-4.4.2.dist-info/RECORD +192 -0
  185. {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
  186. schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
  187. {schemathesis-3.15.4.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
  188. schemathesis/_compat.py +0 -57
  189. schemathesis/_hypothesis.py +0 -123
  190. schemathesis/auth.py +0 -214
  191. schemathesis/cli/callbacks.py +0 -240
  192. schemathesis/cli/cassettes.py +0 -351
  193. schemathesis/cli/context.py +0 -38
  194. schemathesis/cli/debug.py +0 -21
  195. schemathesis/cli/handlers.py +0 -11
  196. schemathesis/cli/junitxml.py +0 -41
  197. schemathesis/cli/options.py +0 -70
  198. schemathesis/cli/output/__init__.py +0 -1
  199. schemathesis/cli/output/default.py +0 -521
  200. schemathesis/cli/output/short.py +0 -40
  201. schemathesis/constants.py +0 -88
  202. schemathesis/exceptions.py +0 -257
  203. schemathesis/extra/_aiohttp.py +0 -27
  204. schemathesis/extra/_flask.py +0 -10
  205. schemathesis/extra/_server.py +0 -16
  206. schemathesis/extra/pytest_plugin.py +0 -251
  207. schemathesis/failures.py +0 -145
  208. schemathesis/fixups/__init__.py +0 -29
  209. schemathesis/fixups/fast_api.py +0 -30
  210. schemathesis/graphql.py +0 -5
  211. schemathesis/internal.py +0 -6
  212. schemathesis/lazy.py +0 -301
  213. schemathesis/models.py +0 -1113
  214. schemathesis/parameters.py +0 -91
  215. schemathesis/runner/__init__.py +0 -470
  216. schemathesis/runner/events.py +0 -242
  217. schemathesis/runner/impl/__init__.py +0 -3
  218. schemathesis/runner/impl/core.py +0 -791
  219. schemathesis/runner/impl/solo.py +0 -85
  220. schemathesis/runner/impl/threadpool.py +0 -367
  221. schemathesis/runner/serialization.py +0 -206
  222. schemathesis/serializers.py +0 -253
  223. schemathesis/service/__init__.py +0 -18
  224. schemathesis/service/auth.py +0 -10
  225. schemathesis/service/client.py +0 -62
  226. schemathesis/service/constants.py +0 -25
  227. schemathesis/service/events.py +0 -39
  228. schemathesis/service/handler.py +0 -46
  229. schemathesis/service/hosts.py +0 -74
  230. schemathesis/service/metadata.py +0 -42
  231. schemathesis/service/models.py +0 -21
  232. schemathesis/service/serialization.py +0 -184
  233. schemathesis/service/worker.py +0 -39
  234. schemathesis/specs/graphql/loaders.py +0 -215
  235. schemathesis/specs/openapi/constants.py +0 -7
  236. schemathesis/specs/openapi/expressions/context.py +0 -12
  237. schemathesis/specs/openapi/expressions/pointers.py +0 -29
  238. schemathesis/specs/openapi/filters.py +0 -44
  239. schemathesis/specs/openapi/links.py +0 -303
  240. schemathesis/specs/openapi/loaders.py +0 -453
  241. schemathesis/specs/openapi/parameters.py +0 -430
  242. schemathesis/specs/openapi/security.py +0 -129
  243. schemathesis/specs/openapi/validation.py +0 -24
  244. schemathesis/stateful.py +0 -358
  245. schemathesis/targets.py +0 -32
  246. schemathesis/types.py +0 -38
  247. schemathesis/utils.py +0 -475
  248. schemathesis-3.15.4.dist-info/METADATA +0 -202
  249. schemathesis-3.15.4.dist-info/RECORD +0 -99
  250. schemathesis-3.15.4.dist-info/entry_points.txt +0 -7
  251. /schemathesis/{extra → cli/ext}/__init__.py +0 -0
@@ -1,184 +0,0 @@
1
- from typing import Any, Callable, Dict, List, Optional, TypeVar, cast
2
-
3
- import attr
4
-
5
- from ..models import Response
6
- from ..runner import events
7
- from ..runner.serialization import SerializedCase, deduplicate_checks
8
- from ..utils import merge
9
-
10
- S = TypeVar("S", bound=events.ExecutionEvent)
11
- SerializeFunc = Callable[[S], Optional[Dict[str, Any]]]
12
-
13
-
14
- def serialize_initialized(event: events.Initialized) -> Optional[Dict[str, Any]]:
15
- return {
16
- "schema": event.schema,
17
- "operations_count": event.operations_count,
18
- "location": event.location or "",
19
- "base_url": event.base_url,
20
- }
21
-
22
-
23
- def serialize_before_execution(event: events.BeforeExecution) -> Optional[Dict[str, Any]]:
24
- return {
25
- "correlation_id": event.correlation_id,
26
- "verbose_name": event.verbose_name,
27
- "data_generation_method": event.data_generation_method,
28
- }
29
-
30
-
31
- def _serialize_case(case: SerializedCase) -> Dict[str, Any]:
32
- return {
33
- "verbose_name": case.verbose_name,
34
- "path_template": case.path_template,
35
- "path_parameters": stringify_path_parameters(case.path_parameters),
36
- "query": prepare_query(case.query),
37
- "cookies": case.cookies,
38
- "media_type": case.media_type,
39
- }
40
-
41
-
42
- def _serialize_response(response: Response) -> Dict[str, Any]:
43
- return {
44
- "status_code": response.status_code,
45
- "headers": response.headers,
46
- "body": response.body,
47
- "encoding": response.encoding,
48
- "elapsed": response.elapsed,
49
- }
50
-
51
-
52
- def serialize_after_execution(event: events.AfterExecution) -> Optional[Dict[str, Any]]:
53
- return {
54
- "correlation_id": event.correlation_id,
55
- "verbose_name": event.verbose_name,
56
- "data_generation_method": event.data_generation_method,
57
- "result": {
58
- "checks": [
59
- {
60
- "name": check.name,
61
- "value": check.value,
62
- "request": {
63
- "method": check.request.method,
64
- "uri": check.request.uri,
65
- "body": check.request.body,
66
- "headers": check.request.headers,
67
- },
68
- "response": _serialize_response(check.response) if check.response is not None else None,
69
- "example": _serialize_case(check.example),
70
- "message": check.message,
71
- "context": attr.asdict(check.context) if check.context is not None else None,
72
- "history": [
73
- {"case": _serialize_case(entry.case), "response": _serialize_response(entry.response)}
74
- for entry in check.history
75
- ],
76
- }
77
- for check in deduplicate_checks(event.result.checks)
78
- ],
79
- "errors": [
80
- {
81
- "exception": error.exception,
82
- "exception_with_traceback": error.exception_with_traceback,
83
- "example": None if error.example is None else _serialize_case(error.example),
84
- }
85
- for error in event.result.errors
86
- ],
87
- },
88
- }
89
-
90
-
91
- def serialize_interrupted(_: events.Interrupted) -> Optional[Dict[str, Any]]:
92
- return None
93
-
94
-
95
- def serialize_internal_error(event: events.InternalError) -> Optional[Dict[str, Any]]:
96
- return {
97
- "message": event.message,
98
- "exception_type": event.exception_type,
99
- "exception_with_traceback": event.exception_with_traceback,
100
- }
101
-
102
-
103
- def serialize_finished(event: events.Finished) -> Optional[Dict[str, Any]]:
104
- return {
105
- "generic_errors": [
106
- {
107
- "exception": error.exception,
108
- "exception_with_traceback": error.exception_with_traceback,
109
- "title": error.title,
110
- }
111
- for error in event.generic_errors
112
- ]
113
- }
114
-
115
-
116
- SERIALIZER_MAP = {
117
- events.Initialized: serialize_initialized,
118
- events.BeforeExecution: serialize_before_execution,
119
- events.AfterExecution: serialize_after_execution,
120
- events.Interrupted: serialize_interrupted,
121
- events.InternalError: serialize_internal_error,
122
- events.Finished: serialize_finished,
123
- }
124
-
125
-
126
- def serialize_event(
127
- event: events.ExecutionEvent,
128
- *,
129
- on_initialized: Optional[SerializeFunc] = None,
130
- on_before_execution: Optional[SerializeFunc] = None,
131
- on_after_execution: Optional[SerializeFunc] = None,
132
- on_interrupted: Optional[SerializeFunc] = None,
133
- on_internal_error: Optional[SerializeFunc] = None,
134
- on_finished: Optional[SerializeFunc] = None,
135
- extra: Optional[Dict[str, Any]] = None,
136
- ) -> Dict[str, Optional[Dict[str, Any]]]:
137
- """Turn an event into JSON-serializable structure."""
138
- # Due to https://github.com/python-attrs/attrs/issues/864 it is easier to implement filtration manually
139
- # Use the explicitly provided serializer for this event and fallback to default one if it is not provided
140
- serializer = {
141
- events.Initialized: on_initialized,
142
- events.BeforeExecution: on_before_execution,
143
- events.AfterExecution: on_after_execution,
144
- events.Interrupted: on_interrupted,
145
- events.InternalError: on_internal_error,
146
- events.Finished: on_finished,
147
- }.get(event.__class__)
148
- if serializer is None:
149
- serializer = cast(SerializeFunc, SERIALIZER_MAP[event.__class__])
150
- data = serializer(event)
151
- if extra is not None:
152
- # If `extra` is present, then merge it with the serialized data. If serialized data is empty, then replace it
153
- # with `extra` value
154
- if data is None:
155
- data = extra
156
- else:
157
- data = merge(data, extra)
158
- # Externally tagged structure
159
- return {event.__class__.__name__: data}
160
-
161
-
162
- def stringify_path_parameters(path_parameters: Optional[Dict[str, Any]]) -> Dict[str, str]:
163
- """Cast all path parameter values to strings.
164
-
165
- Path parameter values may be of arbitrary type, but to display them properly they should be casted to strings.
166
- """
167
- return {key: str(value) for key, value in (path_parameters or {}).items()}
168
-
169
-
170
- def prepare_query(query: Optional[Dict[str, Any]]) -> Dict[str, List[str]]:
171
- """Convert all query values to list of strings.
172
-
173
- Query parameters may be generated in different shapes, including integers, strings, list of strings, etc.
174
- It can also be an object, if the schema contains an object, but `style` and `explode` combo is not applicable.
175
- """
176
-
177
- def to_list_of_strings(value: Any) -> List[str]:
178
- if isinstance(value, list):
179
- return list(map(str, value))
180
- if isinstance(value, str):
181
- return [value]
182
- return [str(value)]
183
-
184
- return {key: to_list_of_strings(value) for key, value in (query or {}).items()}
@@ -1,39 +0,0 @@
1
- from queue import Queue
2
-
3
- from . import events
4
- from .client import ServiceClient
5
- from .constants import STOP_MARKER
6
- from .models import TestRun
7
- from .serialization import serialize_event
8
-
9
-
10
- def start(client: ServiceClient, test_run: TestRun, in_queue: Queue, out_queue: Queue) -> None:
11
- """Initialize a new run and start consuming events."""
12
- try:
13
- consume_events(client, in_queue, test_run.run_id)
14
- # Reached a terminal event or a stop marker.
15
- # In the case of stop marker, it is still a successful result for the handler itself as the error happened in
16
- # a different handler
17
- out_queue.put(events.Completed(short_url=test_run.short_url))
18
- except Exception as exc:
19
- out_queue.put(events.Error(exc))
20
-
21
-
22
- def consume_events(client: ServiceClient, in_queue: Queue, run_id: str) -> None:
23
- """Main working loop that sends data to Schemathesis.io."""
24
- try:
25
- while True:
26
- event = in_queue.get()
27
- if event is STOP_MARKER:
28
- # It is an equivalent of an internal error in some other handler.
29
- # In the happy path scenario, the worker will exit on any terminal event
30
- client.finish_test_run(run_id)
31
- break
32
- data = serialize_event(event)
33
- client.send_event(run_id, data)
34
- if event.is_terminal:
35
- break
36
- except Exception:
37
- # Internal error on our side, try to finish the test run
38
- client.finish_test_run(run_id)
39
- raise
@@ -1,215 +0,0 @@
1
- import pathlib
2
- from typing import IO, Any, Callable, Dict, Optional, Union, cast
3
-
4
- import graphql
5
- import requests
6
- from graphql import ExecutionResult
7
- from starlette.applications import Starlette
8
- from starlette.testclient import TestClient as ASGIClient
9
- from werkzeug import Client
10
- from yarl import URL
11
-
12
- from ...constants import DEFAULT_DATA_GENERATION_METHODS, CodeSampleStyle
13
- from ...exceptions import HTTPError
14
- from ...hooks import HookContext, dispatch
15
- from ...types import DataGenerationMethodInput, PathLike
16
- from ...utils import WSGIResponse, prepare_data_generation_methods, require_relative_url, setup_headers
17
- from .schemas import GraphQLSchema
18
-
19
- INTROSPECTION_QUERY = graphql.get_introspection_query()
20
- INTROSPECTION_QUERY_AST = graphql.parse(INTROSPECTION_QUERY)
21
-
22
-
23
- def from_path(
24
- path: PathLike,
25
- *,
26
- app: Any = None,
27
- base_url: Optional[str] = None,
28
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
29
- code_sample_style: str = CodeSampleStyle.default().name,
30
- encoding: str = "utf8",
31
- ) -> GraphQLSchema:
32
- """Load GraphQL schema via a file from an OS path.
33
-
34
- :param path: A path to the schema file.
35
- :param encoding: The name of the encoding used to decode the file.
36
- """
37
- with open(path, encoding=encoding) as fd:
38
- return from_file(
39
- fd,
40
- app=app,
41
- base_url=base_url,
42
- data_generation_methods=data_generation_methods,
43
- code_sample_style=code_sample_style,
44
- location=pathlib.Path(path).absolute().as_uri(),
45
- )
46
-
47
-
48
- def from_url(
49
- url: str,
50
- *,
51
- app: Any = None,
52
- base_url: Optional[str] = None,
53
- port: Optional[int] = None,
54
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
55
- code_sample_style: str = CodeSampleStyle.default().name,
56
- **kwargs: Any,
57
- ) -> GraphQLSchema:
58
- """Load GraphQL schema from the network.
59
-
60
- :param url: Schema URL.
61
- :param Optional[str] base_url: Base URL to send requests to.
62
- :param Optional[int] port: An optional port if you don't want to pass the ``base_url`` parameter, but only to change
63
- port in ``url``.
64
- :param app: A WSGI app instance.
65
- :return: GraphQLSchema
66
- """
67
- setup_headers(kwargs)
68
- kwargs.setdefault("json", {"query": INTROSPECTION_QUERY})
69
- if not base_url and port:
70
- base_url = str(URL(url).with_port(port))
71
- response = requests.post(url, **kwargs)
72
- HTTPError.raise_for_status(response)
73
- decoded = response.json()
74
- return from_dict(
75
- raw_schema=decoded["data"],
76
- location=url,
77
- base_url=base_url,
78
- app=app,
79
- data_generation_methods=data_generation_methods,
80
- code_sample_style=code_sample_style,
81
- )
82
-
83
-
84
- def from_file(
85
- file: Union[IO[str], str],
86
- *,
87
- app: Any = None,
88
- base_url: Optional[str] = None,
89
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
90
- code_sample_style: str = CodeSampleStyle.default().name,
91
- location: Optional[str] = None,
92
- ) -> GraphQLSchema:
93
- """Load GraphQL schema from a file descriptor or a string.
94
-
95
- :param file: Could be a file descriptor, string or bytes.
96
- """
97
- if isinstance(file, str):
98
- data = file
99
- else:
100
- data = file.read()
101
- document = graphql.build_schema(data)
102
- result = graphql.execute(document, INTROSPECTION_QUERY_AST)
103
- # TYPES: We don't pass `is_awaitable` above, therefore `result` is of the `ExecutionResult` type
104
- result = cast(ExecutionResult, result)
105
- # TYPES:
106
- # - `document` is a valid schema, because otherwise `build_schema` will rise an error;
107
- # - `INTROSPECTION_QUERY` is a valid query - it is known upfront;
108
- # Therefore the execution result is always valid at this point and `result.data` is not `None`
109
- raw_schema = cast(Dict[str, Any], result.data)
110
- return from_dict(
111
- raw_schema,
112
- app=app,
113
- base_url=base_url,
114
- data_generation_methods=data_generation_methods,
115
- code_sample_style=code_sample_style,
116
- location=location,
117
- )
118
-
119
-
120
- def from_dict(
121
- raw_schema: Dict[str, Any],
122
- *,
123
- app: Any = None,
124
- base_url: Optional[str] = None,
125
- location: Optional[str] = None,
126
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
127
- code_sample_style: str = CodeSampleStyle.default().name,
128
- ) -> GraphQLSchema:
129
- """Load GraphQL schema from a Python dictionary.
130
-
131
- :param dict raw_schema: A schema to load.
132
- :param Optional[str] location: Optional schema location. Either a full URL or a filesystem path.
133
- :param Optional[str] base_url: Base URL to send requests to.
134
- :param app: A WSGI app instance.
135
- :return: GraphQLSchema
136
- """
137
- _code_sample_style = CodeSampleStyle.from_str(code_sample_style)
138
- dispatch("before_load_schema", HookContext(), raw_schema)
139
- return GraphQLSchema(
140
- raw_schema,
141
- location=location,
142
- base_url=base_url,
143
- app=app,
144
- data_generation_methods=prepare_data_generation_methods(data_generation_methods),
145
- code_sample_style=_code_sample_style,
146
- ) # type: ignore
147
-
148
-
149
- def from_wsgi(
150
- schema_path: str,
151
- app: Any,
152
- *,
153
- base_url: Optional[str] = None,
154
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
155
- code_sample_style: str = CodeSampleStyle.default().name,
156
- **kwargs: Any,
157
- ) -> GraphQLSchema:
158
- """Load GraphQL schema from a WSGI app.
159
-
160
- :param str schema_path: An in-app relative URL to the schema.
161
- :param app: A WSGI app instance.
162
- :param Optional[str] base_url: Base URL to send requests to.
163
- :return: GraphQLSchema
164
- """
165
- require_relative_url(schema_path)
166
- setup_headers(kwargs)
167
- kwargs.setdefault("json", {"query": INTROSPECTION_QUERY})
168
- client = Client(app, WSGIResponse)
169
- response = client.post(schema_path, **kwargs)
170
- HTTPError.check_response(response, schema_path)
171
- return from_dict(
172
- raw_schema=response.json["data"],
173
- location=schema_path,
174
- base_url=base_url,
175
- app=app,
176
- data_generation_methods=data_generation_methods,
177
- code_sample_style=code_sample_style,
178
- )
179
-
180
-
181
- def from_asgi(
182
- schema_path: str,
183
- app: Any,
184
- *,
185
- base_url: Optional[str] = None,
186
- data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
187
- code_sample_style: str = CodeSampleStyle.default().name,
188
- **kwargs: Any,
189
- ) -> GraphQLSchema:
190
- """Load GraphQL schema from an ASGI app.
191
-
192
- :param str schema_path: An in-app relative URL to the schema.
193
- :param app: An ASGI app instance.
194
- :param Optional[str] base_url: Base URL to send requests to.
195
- """
196
- require_relative_url(schema_path)
197
- setup_headers(kwargs)
198
- kwargs.setdefault("json", {"query": INTROSPECTION_QUERY})
199
- client = ASGIClient(app)
200
- response = client.post(schema_path, **kwargs)
201
- HTTPError.check_response(response, schema_path)
202
- return from_dict(
203
- response.json()["data"],
204
- location=schema_path,
205
- base_url=base_url,
206
- app=app,
207
- data_generation_methods=data_generation_methods,
208
- code_sample_style=code_sample_style,
209
- )
210
-
211
-
212
- def get_loader_for_app(app: Any) -> Callable:
213
- if isinstance(app, Starlette):
214
- return from_asgi
215
- return from_wsgi
@@ -1,7 +0,0 @@
1
- LOCATION_TO_CONTAINER = {
2
- "path": "path_parameters",
3
- "query": "query",
4
- "header": "headers",
5
- "cookie": "cookies",
6
- "body": "body",
7
- }
@@ -1,12 +0,0 @@
1
- import attr
2
-
3
- from ....models import Case
4
- from ....utils import GenericResponse
5
-
6
-
7
- @attr.s(slots=True) # pragma: no mutate
8
- class ExpressionContext:
9
- """Context in what an expression are evaluated."""
10
-
11
- response: GenericResponse = attr.ib() # pragma: no mutate
12
- case: Case = attr.ib() # pragma: no mutate
@@ -1,29 +0,0 @@
1
- from typing import Any, Dict, List, Optional, Union
2
-
3
-
4
- def resolve(document: Any, pointer: str) -> Optional[Union[Dict, List, str, int, float]]:
5
- """Implementation is adapted from Rust's `serde-json` crate.
6
-
7
- Ref: https://github.com/serde-rs/json/blob/master/src/value/mod.rs#L751
8
- """
9
- if not pointer:
10
- return document
11
- if not pointer.startswith("/"):
12
- return None
13
-
14
- def replace(value: str) -> str:
15
- return value.replace("~1", "/").replace("~0", "~")
16
-
17
- tokens = map(replace, pointer.split("/")[1:])
18
- target = document
19
- for token in tokens:
20
- if isinstance(target, dict):
21
- target = target.get(token)
22
- elif isinstance(target, list):
23
- try:
24
- target = target[int(token)]
25
- except IndexError:
26
- return None
27
- else:
28
- return None
29
- return target
@@ -1,44 +0,0 @@
1
- import re
2
- from typing import List, Optional
3
-
4
- from ...types import Filter
5
- from ...utils import force_tuple
6
-
7
-
8
- def should_skip_method(method: str, pattern: Optional[Filter]) -> bool:
9
- if pattern is None:
10
- return False
11
- patterns = force_tuple(pattern)
12
- return method.upper() not in map(str.upper, patterns)
13
-
14
-
15
- def should_skip_endpoint(endpoint: str, pattern: Optional[Filter]) -> bool:
16
- if pattern is None:
17
- return False
18
- return not _match_any_pattern(endpoint, pattern)
19
-
20
-
21
- def should_skip_by_tag(tags: Optional[List[str]], pattern: Optional[Filter]) -> bool:
22
- if pattern is None:
23
- return False
24
- if not tags:
25
- return True
26
- patterns = force_tuple(pattern)
27
- return not any(re.search(item, tag) for item in patterns for tag in tags)
28
-
29
-
30
- def should_skip_by_operation_id(operation_id: Optional[str], pattern: Optional[Filter]) -> bool:
31
- if pattern is None:
32
- return False
33
- if not operation_id:
34
- return True
35
- return not _match_any_pattern(operation_id, pattern)
36
-
37
-
38
- def should_skip_deprecated(is_deprecated: bool, skip_deprecated_operations: bool) -> bool:
39
- return skip_deprecated_operations and is_deprecated
40
-
41
-
42
- def _match_any_pattern(target: str, pattern: Filter) -> bool:
43
- patterns = force_tuple(pattern)
44
- return any(re.search(item, target) for item in patterns)