schemathesis 3.39.15__py3-none-any.whl → 4.0.0__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.
- schemathesis/__init__.py +41 -79
- schemathesis/auths.py +111 -122
- schemathesis/checks.py +169 -60
- schemathesis/cli/__init__.py +15 -2117
- schemathesis/cli/commands/__init__.py +85 -0
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +590 -0
- schemathesis/cli/commands/run/context.py +204 -0
- schemathesis/cli/commands/run/events.py +60 -0
- schemathesis/cli/commands/run/executor.py +157 -0
- schemathesis/cli/commands/run/filters.py +53 -0
- schemathesis/cli/commands/run/handlers/__init__.py +46 -0
- schemathesis/cli/commands/run/handlers/base.py +18 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +474 -0
- schemathesis/cli/commands/run/handlers/junitxml.py +55 -0
- schemathesis/cli/commands/run/handlers/output.py +1628 -0
- schemathesis/cli/commands/run/loaders.py +114 -0
- schemathesis/cli/commands/run/validation.py +246 -0
- schemathesis/cli/constants.py +5 -58
- schemathesis/cli/core.py +19 -0
- schemathesis/cli/ext/fs.py +16 -0
- schemathesis/cli/ext/groups.py +84 -0
- schemathesis/cli/{options.py → ext/options.py} +36 -34
- schemathesis/config/__init__.py +189 -0
- schemathesis/config/_auth.py +51 -0
- schemathesis/config/_checks.py +268 -0
- schemathesis/config/_diff_base.py +99 -0
- schemathesis/config/_env.py +21 -0
- schemathesis/config/_error.py +156 -0
- schemathesis/config/_generation.py +149 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +327 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +187 -0
- schemathesis/config/_projects.py +527 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +120 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/_warnings.py +25 -0
- schemathesis/config/schema.json +885 -0
- schemathesis/core/__init__.py +67 -0
- schemathesis/core/compat.py +32 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +58 -0
- schemathesis/core/deserialization.py +65 -0
- schemathesis/core/errors.py +459 -0
- schemathesis/core/failures.py +315 -0
- schemathesis/core/fs.py +19 -0
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/loaders.py +104 -0
- schemathesis/core/marks.py +66 -0
- schemathesis/{transports/content_types.py → core/media_types.py} +14 -12
- schemathesis/core/output/__init__.py +46 -0
- schemathesis/core/output/sanitization.py +54 -0
- schemathesis/{throttling.py → core/rate_limit.py} +16 -17
- schemathesis/core/registries.py +31 -0
- schemathesis/core/transforms.py +113 -0
- schemathesis/core/transport.py +223 -0
- schemathesis/core/validation.py +54 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +28 -0
- schemathesis/engine/context.py +118 -0
- schemathesis/engine/control.py +36 -0
- schemathesis/engine/core.py +169 -0
- schemathesis/engine/errors.py +464 -0
- schemathesis/engine/events.py +258 -0
- schemathesis/engine/phases/__init__.py +88 -0
- schemathesis/{runner → engine/phases}/probes.py +52 -68
- schemathesis/engine/phases/stateful/__init__.py +68 -0
- schemathesis/engine/phases/stateful/_executor.py +356 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +212 -0
- schemathesis/engine/phases/unit/_executor.py +416 -0
- schemathesis/engine/phases/unit/_pool.py +82 -0
- schemathesis/engine/recorder.py +247 -0
- schemathesis/errors.py +43 -0
- schemathesis/filters.py +17 -98
- schemathesis/generation/__init__.py +5 -33
- schemathesis/generation/case.py +317 -0
- schemathesis/generation/coverage.py +282 -175
- schemathesis/generation/hypothesis/__init__.py +36 -0
- schemathesis/generation/hypothesis/builder.py +800 -0
- schemathesis/generation/{_hypothesis.py → hypothesis/examples.py} +2 -11
- schemathesis/generation/hypothesis/given.py +66 -0
- schemathesis/generation/hypothesis/reporting.py +14 -0
- schemathesis/generation/hypothesis/strategies.py +16 -0
- schemathesis/generation/meta.py +115 -0
- schemathesis/generation/metrics.py +93 -0
- schemathesis/generation/modes.py +20 -0
- schemathesis/generation/overrides.py +116 -0
- schemathesis/generation/stateful/__init__.py +37 -0
- schemathesis/generation/stateful/state_machine.py +278 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +284 -0
- schemathesis/hooks.py +80 -101
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +455 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +72 -0
- schemathesis/openapi/loaders.py +313 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +281 -0
- schemathesis/pytest/loaders.py +36 -0
- schemathesis/{extra/pytest_plugin.py → pytest/plugin.py} +128 -108
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +537 -273
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/_cache.py +1 -2
- schemathesis/specs/graphql/scalars.py +42 -6
- schemathesis/specs/graphql/schemas.py +141 -137
- schemathesis/specs/graphql/validation.py +11 -17
- schemathesis/specs/openapi/__init__.py +6 -1
- schemathesis/specs/openapi/_cache.py +1 -2
- schemathesis/specs/openapi/_hypothesis.py +142 -156
- schemathesis/specs/openapi/checks.py +368 -257
- schemathesis/specs/openapi/converter.py +4 -4
- schemathesis/specs/openapi/definitions.py +1 -1
- schemathesis/specs/openapi/examples.py +23 -21
- schemathesis/specs/openapi/expressions/__init__.py +31 -19
- schemathesis/specs/openapi/expressions/extractors.py +1 -4
- schemathesis/specs/openapi/expressions/lexer.py +1 -1
- schemathesis/specs/openapi/expressions/nodes.py +36 -41
- schemathesis/specs/openapi/expressions/parser.py +1 -1
- schemathesis/specs/openapi/formats.py +35 -7
- schemathesis/specs/openapi/media_types.py +53 -12
- schemathesis/specs/openapi/negative/__init__.py +7 -4
- schemathesis/specs/openapi/negative/mutations.py +6 -5
- schemathesis/specs/openapi/parameters.py +7 -10
- schemathesis/specs/openapi/patterns.py +94 -31
- schemathesis/specs/openapi/references.py +12 -53
- schemathesis/specs/openapi/schemas.py +238 -308
- schemathesis/specs/openapi/security.py +1 -1
- schemathesis/specs/openapi/serialization.py +12 -6
- schemathesis/specs/openapi/stateful/__init__.py +268 -133
- schemathesis/specs/openapi/stateful/control.py +87 -0
- schemathesis/specs/openapi/stateful/links.py +209 -0
- schemathesis/transport/__init__.py +142 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +124 -0
- schemathesis/transport/requests.py +244 -0
- schemathesis/{_xml.py → transport/serialization.py} +69 -11
- schemathesis/transport/wsgi.py +171 -0
- schemathesis-4.0.0.dist-info/METADATA +204 -0
- schemathesis-4.0.0.dist-info/RECORD +164 -0
- {schemathesis-3.39.15.dist-info → schemathesis-4.0.0.dist-info}/entry_points.txt +1 -1
- {schemathesis-3.39.15.dist-info → schemathesis-4.0.0.dist-info}/licenses/LICENSE +1 -1
- schemathesis/_compat.py +0 -74
- schemathesis/_dependency_versions.py +0 -19
- schemathesis/_hypothesis.py +0 -712
- schemathesis/_override.py +0 -50
- schemathesis/_patches.py +0 -21
- schemathesis/_rate_limiter.py +0 -7
- schemathesis/cli/callbacks.py +0 -466
- schemathesis/cli/cassettes.py +0 -561
- schemathesis/cli/context.py +0 -75
- schemathesis/cli/debug.py +0 -27
- schemathesis/cli/handlers.py +0 -19
- schemathesis/cli/junitxml.py +0 -124
- schemathesis/cli/output/__init__.py +0 -1
- schemathesis/cli/output/default.py +0 -920
- schemathesis/cli/output/short.py +0 -59
- schemathesis/cli/reporting.py +0 -79
- schemathesis/cli/sanitization.py +0 -26
- schemathesis/code_samples.py +0 -151
- schemathesis/constants.py +0 -54
- schemathesis/contrib/__init__.py +0 -11
- schemathesis/contrib/openapi/__init__.py +0 -11
- schemathesis/contrib/openapi/fill_missing_examples.py +0 -24
- schemathesis/contrib/openapi/formats/__init__.py +0 -9
- schemathesis/contrib/openapi/formats/uuid.py +0 -16
- schemathesis/contrib/unique_data.py +0 -41
- schemathesis/exceptions.py +0 -571
- schemathesis/experimental/__init__.py +0 -109
- schemathesis/extra/_aiohttp.py +0 -28
- schemathesis/extra/_flask.py +0 -13
- schemathesis/extra/_server.py +0 -18
- schemathesis/failures.py +0 -284
- schemathesis/fixups/__init__.py +0 -37
- schemathesis/fixups/fast_api.py +0 -41
- schemathesis/fixups/utf8_bom.py +0 -28
- schemathesis/generation/_methods.py +0 -44
- schemathesis/graphql.py +0 -3
- schemathesis/internal/__init__.py +0 -7
- schemathesis/internal/checks.py +0 -86
- schemathesis/internal/copy.py +0 -32
- schemathesis/internal/datetime.py +0 -5
- schemathesis/internal/deprecation.py +0 -37
- schemathesis/internal/diff.py +0 -15
- schemathesis/internal/extensions.py +0 -27
- schemathesis/internal/jsonschema.py +0 -36
- schemathesis/internal/output.py +0 -68
- schemathesis/internal/transformation.py +0 -26
- schemathesis/internal/validation.py +0 -34
- schemathesis/lazy.py +0 -474
- schemathesis/loaders.py +0 -122
- schemathesis/models.py +0 -1341
- schemathesis/parameters.py +0 -90
- schemathesis/runner/__init__.py +0 -605
- schemathesis/runner/events.py +0 -389
- schemathesis/runner/impl/__init__.py +0 -3
- schemathesis/runner/impl/context.py +0 -88
- schemathesis/runner/impl/core.py +0 -1280
- schemathesis/runner/impl/solo.py +0 -80
- schemathesis/runner/impl/threadpool.py +0 -391
- schemathesis/runner/serialization.py +0 -544
- schemathesis/sanitization.py +0 -252
- schemathesis/serializers.py +0 -328
- schemathesis/service/__init__.py +0 -18
- schemathesis/service/auth.py +0 -11
- schemathesis/service/ci.py +0 -202
- schemathesis/service/client.py +0 -133
- schemathesis/service/constants.py +0 -38
- schemathesis/service/events.py +0 -61
- schemathesis/service/extensions.py +0 -224
- schemathesis/service/hosts.py +0 -111
- schemathesis/service/metadata.py +0 -71
- schemathesis/service/models.py +0 -258
- schemathesis/service/report.py +0 -255
- schemathesis/service/serialization.py +0 -173
- schemathesis/service/usage.py +0 -66
- schemathesis/specs/graphql/loaders.py +0 -364
- schemathesis/specs/openapi/expressions/context.py +0 -16
- schemathesis/specs/openapi/links.py +0 -389
- schemathesis/specs/openapi/loaders.py +0 -707
- schemathesis/specs/openapi/stateful/statistic.py +0 -198
- schemathesis/specs/openapi/stateful/types.py +0 -14
- schemathesis/specs/openapi/validation.py +0 -26
- schemathesis/stateful/__init__.py +0 -147
- schemathesis/stateful/config.py +0 -97
- schemathesis/stateful/context.py +0 -135
- schemathesis/stateful/events.py +0 -274
- schemathesis/stateful/runner.py +0 -309
- schemathesis/stateful/sink.py +0 -68
- schemathesis/stateful/state_machine.py +0 -328
- schemathesis/stateful/statistic.py +0 -22
- schemathesis/stateful/validation.py +0 -100
- schemathesis/targets.py +0 -77
- schemathesis/transports/__init__.py +0 -369
- schemathesis/transports/asgi.py +0 -7
- schemathesis/transports/auth.py +0 -38
- schemathesis/transports/headers.py +0 -36
- schemathesis/transports/responses.py +0 -57
- schemathesis/types.py +0 -44
- schemathesis/utils.py +0 -164
- schemathesis-3.39.15.dist-info/METADATA +0 -293
- schemathesis-3.39.15.dist-info/RECORD +0 -160
- /schemathesis/{extra → cli/ext}/__init__.py +0 -0
- /schemathesis/{_lazy_import.py → core/lazy_import.py} +0 -0
- /schemathesis/{internal → core}/result.py +0 -0
- {schemathesis-3.39.15.dist-info → schemathesis-4.0.0.dist-info}/WHEEL +0 -0
@@ -1,173 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from dataclasses import asdict
|
4
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, cast
|
5
|
-
|
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
|
12
|
-
|
13
|
-
S = TypeVar("S", bound=events.ExecutionEvent)
|
14
|
-
SerializeFunc = Callable[[S], Optional[Dict[str, Any]]]
|
15
|
-
|
16
|
-
|
17
|
-
def serialize_initialized(event: events.Initialized) -> dict[str, Any] | None:
|
18
|
-
return {
|
19
|
-
"operations_count": event.operations_count,
|
20
|
-
"location": event.location or "",
|
21
|
-
"base_url": event.base_url,
|
22
|
-
}
|
23
|
-
|
24
|
-
|
25
|
-
def serialize_before_probing(_: events.BeforeProbing) -> None:
|
26
|
-
return None
|
27
|
-
|
28
|
-
|
29
|
-
def serialize_after_probing(event: events.AfterProbing) -> dict[str, Any] | None:
|
30
|
-
probes = event.probes or []
|
31
|
-
return {"probes": [probe.serialize() for probe in probes]}
|
32
|
-
|
33
|
-
|
34
|
-
def serialize_before_analysis(_: events.BeforeAnalysis) -> None:
|
35
|
-
return None
|
36
|
-
|
37
|
-
|
38
|
-
def serialize_after_analysis(event: events.AfterAnalysis) -> dict[str, Any] | None:
|
39
|
-
return event._serialize()
|
40
|
-
|
41
|
-
|
42
|
-
def serialize_before_execution(event: events.BeforeExecution) -> dict[str, Any] | None:
|
43
|
-
return {
|
44
|
-
"correlation_id": event.correlation_id,
|
45
|
-
"verbose_name": event.verbose_name,
|
46
|
-
"data_generation_method": event.data_generation_method,
|
47
|
-
}
|
48
|
-
|
49
|
-
|
50
|
-
def serialize_after_execution(event: events.AfterExecution) -> dict[str, Any] | None:
|
51
|
-
return {
|
52
|
-
"correlation_id": event.correlation_id,
|
53
|
-
"verbose_name": event.verbose_name,
|
54
|
-
"status": event.status,
|
55
|
-
"elapsed_time": event.elapsed_time,
|
56
|
-
"data_generation_method": event.data_generation_method,
|
57
|
-
"result": {
|
58
|
-
"checks": [_serialize_check(check) for check in event.result.checks],
|
59
|
-
"errors": [asdict(error) for error in event.result.errors],
|
60
|
-
"skip_reason": event.result.skip_reason,
|
61
|
-
},
|
62
|
-
}
|
63
|
-
|
64
|
-
|
65
|
-
def serialize_interrupted(_: events.Interrupted) -> dict[str, Any] | None:
|
66
|
-
return None
|
67
|
-
|
68
|
-
|
69
|
-
def serialize_internal_error(event: events.InternalError) -> dict[str, Any] | None:
|
70
|
-
return {
|
71
|
-
"type": event.type.value,
|
72
|
-
"subtype": event.subtype.value if event.subtype else event.subtype,
|
73
|
-
"title": event.title,
|
74
|
-
"message": event.message,
|
75
|
-
"extras": event.extras,
|
76
|
-
"exception_type": event.exception_type,
|
77
|
-
"exception": event.exception,
|
78
|
-
"exception_with_traceback": event.exception_with_traceback,
|
79
|
-
}
|
80
|
-
|
81
|
-
|
82
|
-
def serialize_finished(event: events.Finished) -> dict[str, Any] | None:
|
83
|
-
return {
|
84
|
-
"generic_errors": [
|
85
|
-
{
|
86
|
-
"exception": error.exception,
|
87
|
-
"exception_with_traceback": error.exception_with_traceback,
|
88
|
-
"title": error.title,
|
89
|
-
}
|
90
|
-
for error in event.generic_errors
|
91
|
-
],
|
92
|
-
"running_time": event.running_time,
|
93
|
-
}
|
94
|
-
|
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
|
-
|
113
|
-
SERIALIZER_MAP = {
|
114
|
-
events.Initialized: serialize_initialized,
|
115
|
-
events.BeforeProbing: serialize_before_probing,
|
116
|
-
events.AfterProbing: serialize_after_probing,
|
117
|
-
events.BeforeAnalysis: serialize_before_analysis,
|
118
|
-
events.AfterAnalysis: serialize_after_analysis,
|
119
|
-
events.BeforeExecution: serialize_before_execution,
|
120
|
-
events.AfterExecution: serialize_after_execution,
|
121
|
-
events.Interrupted: serialize_interrupted,
|
122
|
-
events.InternalError: serialize_internal_error,
|
123
|
-
events.StatefulEvent: serialize_stateful_event,
|
124
|
-
events.AfterStatefulExecution: serialize_after_stateful_execution,
|
125
|
-
events.Finished: serialize_finished,
|
126
|
-
}
|
127
|
-
|
128
|
-
|
129
|
-
def serialize_event(
|
130
|
-
event: events.ExecutionEvent,
|
131
|
-
*,
|
132
|
-
on_initialized: SerializeFunc | None = None,
|
133
|
-
on_before_probing: SerializeFunc | None = None,
|
134
|
-
on_after_probing: SerializeFunc | None = None,
|
135
|
-
on_before_analysis: SerializeFunc | None = None,
|
136
|
-
on_after_analysis: SerializeFunc | None = None,
|
137
|
-
on_before_execution: SerializeFunc | None = None,
|
138
|
-
on_after_execution: SerializeFunc | None = None,
|
139
|
-
on_interrupted: SerializeFunc | None = None,
|
140
|
-
on_internal_error: SerializeFunc | None = None,
|
141
|
-
on_stateful_event: SerializeFunc | None = None,
|
142
|
-
on_after_stateful_execution: SerializeFunc | None = None,
|
143
|
-
on_finished: SerializeFunc | None = None,
|
144
|
-
extra: dict[str, Any] | None = None,
|
145
|
-
) -> dict[str, dict[str, Any] | None]:
|
146
|
-
"""Turn an event into JSON-serializable structure."""
|
147
|
-
# Use the explicitly provided serializer for this event and fallback to default one if it is not provided
|
148
|
-
serializer = {
|
149
|
-
events.Initialized: on_initialized,
|
150
|
-
events.BeforeProbing: on_before_probing,
|
151
|
-
events.AfterProbing: on_after_probing,
|
152
|
-
events.BeforeAnalysis: on_before_analysis,
|
153
|
-
events.AfterAnalysis: on_after_analysis,
|
154
|
-
events.BeforeExecution: on_before_execution,
|
155
|
-
events.AfterExecution: on_after_execution,
|
156
|
-
events.Interrupted: on_interrupted,
|
157
|
-
events.InternalError: on_internal_error,
|
158
|
-
events.StatefulEvent: on_stateful_event,
|
159
|
-
events.AfterStatefulExecution: on_after_stateful_execution,
|
160
|
-
events.Finished: on_finished,
|
161
|
-
}.get(event.__class__)
|
162
|
-
if serializer is None:
|
163
|
-
serializer = cast(SerializeFunc, SERIALIZER_MAP[event.__class__])
|
164
|
-
data = serializer(event)
|
165
|
-
if extra is not None:
|
166
|
-
# If `extra` is present, then merge it with the serialized data. If serialized data is empty, then replace it
|
167
|
-
# with `extra` value
|
168
|
-
if data is None:
|
169
|
-
data = extra
|
170
|
-
else:
|
171
|
-
data = merge_recursively(data, extra)
|
172
|
-
# Externally tagged structure
|
173
|
-
return {event.__class__.__name__: data}
|
schemathesis/service/usage.py
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import sys
|
4
|
-
from typing import Any
|
5
|
-
|
6
|
-
import click
|
7
|
-
from click.types import StringParamType
|
8
|
-
|
9
|
-
from .. import cli, hooks
|
10
|
-
|
11
|
-
|
12
|
-
def collect(args: list[str] | None = None) -> dict[str, Any] | None:
|
13
|
-
"""Collect anonymized CLI usage data."""
|
14
|
-
context: click.Context | None = click.get_current_context(silent=True)
|
15
|
-
if context is not None and not sys.argv[0].endswith("pytest"):
|
16
|
-
args = args or sys.argv[2:]
|
17
|
-
parameters, _, types = parse_cli_args(context, args)
|
18
|
-
parameters_data: dict[str, dict[str, Any]] = {}
|
19
|
-
used_headers: list[str] = []
|
20
|
-
schema = parameters["schema"]
|
21
|
-
app = parameters.get("app")
|
22
|
-
if not schema:
|
23
|
-
schema_kind = None
|
24
|
-
else:
|
25
|
-
schema_kind = cli.callbacks.parse_schema_kind(schema, app).name
|
26
|
-
usage = {
|
27
|
-
"schema_kind": schema_kind,
|
28
|
-
"parameters": parameters_data,
|
29
|
-
"used_headers": used_headers,
|
30
|
-
"hooks": hooks.collect_statistic(),
|
31
|
-
}
|
32
|
-
types_iter = iter(types)
|
33
|
-
for option, value in parameters.items():
|
34
|
-
option_type = next(types_iter)
|
35
|
-
if isinstance(option_type, click.Argument):
|
36
|
-
continue
|
37
|
-
if option_type.multiple:
|
38
|
-
# Forward the iterator to the next option type
|
39
|
-
for _ in range(len(value) - 1):
|
40
|
-
next(types_iter)
|
41
|
-
entry = _collect_option(option, option_type, used_headers, value)
|
42
|
-
if entry:
|
43
|
-
parameters_data[option] = entry
|
44
|
-
return usage
|
45
|
-
return None
|
46
|
-
|
47
|
-
|
48
|
-
def _collect_option(option: str, option_type: click.Parameter, used_headers: list[str], value: Any) -> dict[str, Any]:
|
49
|
-
entry = {}
|
50
|
-
if isinstance(option_type.type, (StringParamType, click.types.File)):
|
51
|
-
if option == "headers" and value:
|
52
|
-
used_headers.extend(header.split(":", 1)[0] for header in value)
|
53
|
-
else:
|
54
|
-
# Free-form values are replaced with their number of occurrences, to avoid sending sensitive info
|
55
|
-
if option_type.multiple:
|
56
|
-
entry["count"] = len(value)
|
57
|
-
else:
|
58
|
-
entry["count"] = 1
|
59
|
-
else:
|
60
|
-
entry["value"] = value
|
61
|
-
return entry
|
62
|
-
|
63
|
-
|
64
|
-
def parse_cli_args(context: click.Context, args: list[str]) -> tuple[dict[str, Any], list, list[click.Parameter]]:
|
65
|
-
parser = cli.run.make_parser(context)
|
66
|
-
return parser.parse_args(args=args)
|
@@ -1,364 +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 DEFAULT_RESPONSE_TIMEOUT, 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.output import OutputConfig
|
20
|
-
from ...internal.validation import require_relative_url
|
21
|
-
from ...loaders import load_schema_from_url
|
22
|
-
from ...throttling import build_limiter
|
23
|
-
from ...transports.headers import setup_default_headers
|
24
|
-
from ...types import PathLike, Specification
|
25
|
-
|
26
|
-
if TYPE_CHECKING:
|
27
|
-
from graphql import DocumentNode
|
28
|
-
from pyrate_limiter import Limiter
|
29
|
-
|
30
|
-
from ...transports.responses import GenericResponse
|
31
|
-
from .schemas import GraphQLSchema
|
32
|
-
|
33
|
-
|
34
|
-
@lru_cache
|
35
|
-
def get_introspection_query() -> str:
|
36
|
-
import graphql
|
37
|
-
|
38
|
-
return graphql.get_introspection_query()
|
39
|
-
|
40
|
-
|
41
|
-
@lru_cache
|
42
|
-
def get_introspection_query_ast() -> DocumentNode:
|
43
|
-
import graphql
|
44
|
-
|
45
|
-
query = get_introspection_query()
|
46
|
-
return graphql.parse(query)
|
47
|
-
|
48
|
-
|
49
|
-
def from_path(
|
50
|
-
path: PathLike,
|
51
|
-
*,
|
52
|
-
app: Any = None,
|
53
|
-
base_url: str | None = None,
|
54
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
55
|
-
generation_config: GenerationConfig | None = None,
|
56
|
-
output_config: OutputConfig | None = None,
|
57
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
58
|
-
rate_limit: str | None = None,
|
59
|
-
encoding: str = "utf8",
|
60
|
-
sanitize_output: bool = True,
|
61
|
-
) -> GraphQLSchema:
|
62
|
-
"""Load GraphQL schema via a file from an OS path.
|
63
|
-
|
64
|
-
:param path: A path to the schema file.
|
65
|
-
:param encoding: The name of the encoding used to decode the file.
|
66
|
-
"""
|
67
|
-
with open(path, encoding=encoding) as fd:
|
68
|
-
return from_file(
|
69
|
-
fd,
|
70
|
-
app=app,
|
71
|
-
base_url=base_url,
|
72
|
-
data_generation_methods=data_generation_methods,
|
73
|
-
code_sample_style=code_sample_style,
|
74
|
-
generation_config=generation_config,
|
75
|
-
output_config=output_config,
|
76
|
-
location=pathlib.Path(path).absolute().as_uri(),
|
77
|
-
rate_limit=rate_limit,
|
78
|
-
sanitize_output=sanitize_output,
|
79
|
-
)
|
80
|
-
|
81
|
-
|
82
|
-
def extract_schema_from_response(response: GenericResponse) -> dict[str, Any]:
|
83
|
-
from requests import Response
|
84
|
-
|
85
|
-
try:
|
86
|
-
if isinstance(response, Response):
|
87
|
-
decoded = response.json()
|
88
|
-
else:
|
89
|
-
decoded = response.json
|
90
|
-
except JSONDecodeError as exc:
|
91
|
-
raise SchemaError(
|
92
|
-
SchemaErrorType.UNEXPECTED_CONTENT_TYPE,
|
93
|
-
"Received unsupported content while expecting a JSON payload for GraphQL",
|
94
|
-
) from exc
|
95
|
-
return decoded
|
96
|
-
|
97
|
-
|
98
|
-
def from_url(
|
99
|
-
url: str,
|
100
|
-
*,
|
101
|
-
app: Any = None,
|
102
|
-
base_url: str | None = None,
|
103
|
-
port: int | None = None,
|
104
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
105
|
-
generation_config: GenerationConfig | None = None,
|
106
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
107
|
-
wait_for_schema: float | None = None,
|
108
|
-
rate_limit: str | None = None,
|
109
|
-
sanitize_output: bool = True,
|
110
|
-
**kwargs: Any,
|
111
|
-
) -> GraphQLSchema:
|
112
|
-
"""Load GraphQL schema from the network.
|
113
|
-
|
114
|
-
:param url: Schema URL.
|
115
|
-
:param Optional[str] base_url: Base URL to send requests to.
|
116
|
-
:param Optional[int] port: An optional port if you don't want to pass the ``base_url`` parameter, but only to change
|
117
|
-
port in ``url``.
|
118
|
-
:param app: A WSGI app instance.
|
119
|
-
:return: GraphQLSchema
|
120
|
-
"""
|
121
|
-
import backoff
|
122
|
-
import requests
|
123
|
-
|
124
|
-
setup_default_headers(kwargs)
|
125
|
-
kwargs.setdefault("json", {"query": get_introspection_query()})
|
126
|
-
if port:
|
127
|
-
from yarl import URL
|
128
|
-
|
129
|
-
url = str(URL(url).with_port(port))
|
130
|
-
if not base_url:
|
131
|
-
base_url = url
|
132
|
-
|
133
|
-
if wait_for_schema is not None:
|
134
|
-
|
135
|
-
@backoff.on_exception( # type: ignore
|
136
|
-
backoff.constant,
|
137
|
-
requests.exceptions.ConnectionError,
|
138
|
-
max_time=wait_for_schema,
|
139
|
-
interval=WAIT_FOR_SCHEMA_INTERVAL,
|
140
|
-
)
|
141
|
-
def _load_schema(_uri: str, **_kwargs: Any) -> requests.Response:
|
142
|
-
return requests.post(_uri, **_kwargs)
|
143
|
-
|
144
|
-
else:
|
145
|
-
_load_schema = requests.post
|
146
|
-
|
147
|
-
kwargs.setdefault("timeout", DEFAULT_RESPONSE_TIMEOUT / 1000)
|
148
|
-
response = load_schema_from_url(lambda: _load_schema(url, **kwargs))
|
149
|
-
raw_schema = extract_schema_from_response(response)
|
150
|
-
return from_dict(
|
151
|
-
raw_schema=raw_schema,
|
152
|
-
location=url,
|
153
|
-
base_url=base_url,
|
154
|
-
app=app,
|
155
|
-
data_generation_methods=data_generation_methods,
|
156
|
-
code_sample_style=code_sample_style,
|
157
|
-
rate_limit=rate_limit,
|
158
|
-
sanitize_output=sanitize_output,
|
159
|
-
)
|
160
|
-
|
161
|
-
|
162
|
-
def from_file(
|
163
|
-
file: IO[str] | str,
|
164
|
-
*,
|
165
|
-
app: Any = None,
|
166
|
-
base_url: str | None = None,
|
167
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
168
|
-
generation_config: GenerationConfig | None = None,
|
169
|
-
output_config: OutputConfig | None = None,
|
170
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
171
|
-
location: str | None = None,
|
172
|
-
rate_limit: str | None = None,
|
173
|
-
sanitize_output: bool = True,
|
174
|
-
) -> GraphQLSchema:
|
175
|
-
"""Load GraphQL schema from a file descriptor or a string.
|
176
|
-
|
177
|
-
:param file: Could be a file descriptor, string or bytes.
|
178
|
-
"""
|
179
|
-
import graphql
|
180
|
-
|
181
|
-
if isinstance(file, str):
|
182
|
-
data = file
|
183
|
-
else:
|
184
|
-
data = file.read()
|
185
|
-
try:
|
186
|
-
document = graphql.build_schema(data)
|
187
|
-
result = graphql.execute(document, get_introspection_query_ast())
|
188
|
-
# TYPES: We don't pass `is_awaitable` above, therefore `result` is of the `ExecutionResult` type
|
189
|
-
result = cast(graphql.ExecutionResult, result)
|
190
|
-
# TYPES:
|
191
|
-
# - `document` is a valid schema, because otherwise `build_schema` will rise an error;
|
192
|
-
# - `INTROSPECTION_QUERY` is a valid query - it is known upfront;
|
193
|
-
# Therefore the execution result is always valid at this point and `result.data` is not `None`
|
194
|
-
raw_schema = cast(Dict[str, Any], result.data)
|
195
|
-
except Exception as exc:
|
196
|
-
try:
|
197
|
-
raw_schema = json.loads(data)
|
198
|
-
if not isinstance(raw_schema, dict) or "__schema" not in raw_schema:
|
199
|
-
_on_invalid_schema(exc)
|
200
|
-
except json.JSONDecodeError:
|
201
|
-
_on_invalid_schema(exc, extras=[entry for entry in str(exc).splitlines() if entry])
|
202
|
-
return from_dict(
|
203
|
-
raw_schema,
|
204
|
-
app=app,
|
205
|
-
base_url=base_url,
|
206
|
-
data_generation_methods=data_generation_methods,
|
207
|
-
generation_config=generation_config,
|
208
|
-
output_config=output_config,
|
209
|
-
code_sample_style=code_sample_style,
|
210
|
-
location=location,
|
211
|
-
rate_limit=rate_limit,
|
212
|
-
sanitize_output=sanitize_output,
|
213
|
-
)
|
214
|
-
|
215
|
-
|
216
|
-
def _on_invalid_schema(exc: Exception, extras: list[str] | None = None) -> NoReturn:
|
217
|
-
raise SchemaError(
|
218
|
-
SchemaErrorType.GRAPHQL_INVALID_SCHEMA,
|
219
|
-
"The provided API schema does not appear to be a valid GraphQL schema",
|
220
|
-
extras=extras or [],
|
221
|
-
) from exc
|
222
|
-
|
223
|
-
|
224
|
-
def from_dict(
|
225
|
-
raw_schema: dict[str, Any],
|
226
|
-
*,
|
227
|
-
app: Any = None,
|
228
|
-
base_url: str | None = None,
|
229
|
-
location: str | None = None,
|
230
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
231
|
-
generation_config: GenerationConfig | None = None,
|
232
|
-
output_config: OutputConfig | None = None,
|
233
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
234
|
-
rate_limit: str | None = None,
|
235
|
-
sanitize_output: bool = True,
|
236
|
-
) -> GraphQLSchema:
|
237
|
-
"""Load GraphQL schema from a Python dictionary.
|
238
|
-
|
239
|
-
:param dict raw_schema: A schema to load.
|
240
|
-
:param Optional[str] location: Optional schema location. Either a full URL or a filesystem path.
|
241
|
-
:param Optional[str] base_url: Base URL to send requests to.
|
242
|
-
:param app: A WSGI app instance.
|
243
|
-
:return: GraphQLSchema
|
244
|
-
"""
|
245
|
-
from ... import transports
|
246
|
-
from .schemas import GraphQLSchema
|
247
|
-
|
248
|
-
_code_sample_style = CodeSampleStyle.from_str(code_sample_style)
|
249
|
-
hook_context = HookContext()
|
250
|
-
if "data" in raw_schema:
|
251
|
-
raw_schema = raw_schema["data"]
|
252
|
-
dispatch("before_load_schema", hook_context, raw_schema)
|
253
|
-
rate_limiter: Limiter | None = None
|
254
|
-
if rate_limit is not None:
|
255
|
-
rate_limiter = build_limiter(rate_limit)
|
256
|
-
instance = GraphQLSchema(
|
257
|
-
raw_schema,
|
258
|
-
specification=Specification.GRAPHQL,
|
259
|
-
location=location,
|
260
|
-
base_url=base_url,
|
261
|
-
app=app,
|
262
|
-
data_generation_methods=DataGenerationMethod.ensure_list(data_generation_methods),
|
263
|
-
generation_config=generation_config or GenerationConfig(),
|
264
|
-
output_config=output_config or OutputConfig(),
|
265
|
-
code_sample_style=_code_sample_style,
|
266
|
-
rate_limiter=rate_limiter,
|
267
|
-
sanitize_output=sanitize_output,
|
268
|
-
transport=transports.get(app),
|
269
|
-
) # type: ignore
|
270
|
-
dispatch("after_load_schema", hook_context, instance)
|
271
|
-
return instance
|
272
|
-
|
273
|
-
|
274
|
-
def from_wsgi(
|
275
|
-
schema_path: str,
|
276
|
-
app: Any,
|
277
|
-
*,
|
278
|
-
base_url: str | None = None,
|
279
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
280
|
-
generation_config: GenerationConfig | None = None,
|
281
|
-
output_config: OutputConfig | None = None,
|
282
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
283
|
-
rate_limit: str | None = None,
|
284
|
-
sanitize_output: bool = True,
|
285
|
-
**kwargs: Any,
|
286
|
-
) -> GraphQLSchema:
|
287
|
-
"""Load GraphQL schema from a WSGI app.
|
288
|
-
|
289
|
-
:param str schema_path: An in-app relative URL to the schema.
|
290
|
-
:param app: A WSGI app instance.
|
291
|
-
:param Optional[str] base_url: Base URL to send requests to.
|
292
|
-
:return: GraphQLSchema
|
293
|
-
"""
|
294
|
-
from werkzeug import Client
|
295
|
-
|
296
|
-
from ...transports.responses import WSGIResponse
|
297
|
-
|
298
|
-
require_relative_url(schema_path)
|
299
|
-
setup_default_headers(kwargs)
|
300
|
-
kwargs.setdefault("json", {"query": get_introspection_query()})
|
301
|
-
client = Client(app, WSGIResponse)
|
302
|
-
response = load_schema_from_url(lambda: client.post(schema_path, **kwargs))
|
303
|
-
raw_schema = extract_schema_from_response(response)
|
304
|
-
return from_dict(
|
305
|
-
raw_schema=raw_schema,
|
306
|
-
location=schema_path,
|
307
|
-
base_url=base_url,
|
308
|
-
app=app,
|
309
|
-
data_generation_methods=data_generation_methods,
|
310
|
-
generation_config=generation_config,
|
311
|
-
output_config=output_config,
|
312
|
-
code_sample_style=code_sample_style,
|
313
|
-
rate_limit=rate_limit,
|
314
|
-
sanitize_output=sanitize_output,
|
315
|
-
)
|
316
|
-
|
317
|
-
|
318
|
-
def from_asgi(
|
319
|
-
schema_path: str,
|
320
|
-
app: Any,
|
321
|
-
*,
|
322
|
-
base_url: str | None = None,
|
323
|
-
data_generation_methods: DataGenerationMethodInput = DEFAULT_DATA_GENERATION_METHODS,
|
324
|
-
generation_config: GenerationConfig | None = None,
|
325
|
-
output_config: OutputConfig | None = None,
|
326
|
-
code_sample_style: str = CodeSampleStyle.default().name,
|
327
|
-
rate_limit: str | None = None,
|
328
|
-
sanitize_output: bool = True,
|
329
|
-
**kwargs: Any,
|
330
|
-
) -> GraphQLSchema:
|
331
|
-
"""Load GraphQL schema from an ASGI app.
|
332
|
-
|
333
|
-
:param str schema_path: An in-app relative URL to the schema.
|
334
|
-
:param app: An ASGI app instance.
|
335
|
-
:param Optional[str] base_url: Base URL to send requests to.
|
336
|
-
"""
|
337
|
-
from starlette_testclient import TestClient as ASGIClient
|
338
|
-
|
339
|
-
require_relative_url(schema_path)
|
340
|
-
setup_default_headers(kwargs)
|
341
|
-
kwargs.setdefault("json", {"query": get_introspection_query()})
|
342
|
-
client = ASGIClient(app)
|
343
|
-
response = load_schema_from_url(lambda: client.post(schema_path, **kwargs))
|
344
|
-
raw_schema = extract_schema_from_response(response)
|
345
|
-
return from_dict(
|
346
|
-
raw_schema=raw_schema,
|
347
|
-
location=schema_path,
|
348
|
-
base_url=base_url,
|
349
|
-
app=app,
|
350
|
-
data_generation_methods=data_generation_methods,
|
351
|
-
generation_config=generation_config,
|
352
|
-
output_config=output_config,
|
353
|
-
code_sample_style=code_sample_style,
|
354
|
-
rate_limit=rate_limit,
|
355
|
-
sanitize_output=sanitize_output,
|
356
|
-
)
|
357
|
-
|
358
|
-
|
359
|
-
def get_loader_for_app(app: Any) -> Callable:
|
360
|
-
from ...transports.asgi import is_asgi_app
|
361
|
-
|
362
|
-
if is_asgi_app(app):
|
363
|
-
return from_asgi
|
364
|
-
return from_wsgi
|
@@ -1,16 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING
|
5
|
-
|
6
|
-
if TYPE_CHECKING:
|
7
|
-
from ....models import Case
|
8
|
-
from ....transports.responses import GenericResponse
|
9
|
-
|
10
|
-
|
11
|
-
@dataclass
|
12
|
-
class ExpressionContext:
|
13
|
-
"""Context in what an expression are evaluated."""
|
14
|
-
|
15
|
-
response: GenericResponse
|
16
|
-
case: Case
|