schemathesis 3.13.0__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.
- schemathesis/__init__.py +53 -25
- schemathesis/auths.py +507 -0
- schemathesis/checks.py +190 -25
- schemathesis/cli/__init__.py +27 -1016
- schemathesis/cli/__main__.py +4 -0
- schemathesis/cli/commands/__init__.py +133 -0
- schemathesis/cli/commands/data.py +10 -0
- schemathesis/cli/commands/run/__init__.py +602 -0
- schemathesis/cli/commands/run/context.py +228 -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 +45 -0
- schemathesis/cli/commands/run/handlers/cassettes.py +464 -0
- schemathesis/cli/commands/run/handlers/junitxml.py +60 -0
- schemathesis/cli/commands/run/handlers/output.py +1750 -0
- schemathesis/cli/commands/run/loaders.py +118 -0
- schemathesis/cli/commands/run/validation.py +256 -0
- schemathesis/cli/constants.py +5 -0
- schemathesis/cli/core.py +19 -0
- schemathesis/cli/ext/fs.py +16 -0
- schemathesis/cli/ext/groups.py +203 -0
- schemathesis/cli/ext/options.py +81 -0
- schemathesis/config/__init__.py +202 -0
- schemathesis/config/_auth.py +51 -0
- schemathesis/config/_checks.py +268 -0
- schemathesis/config/_diff_base.py +101 -0
- schemathesis/config/_env.py +21 -0
- schemathesis/config/_error.py +163 -0
- schemathesis/config/_generation.py +157 -0
- schemathesis/config/_health_check.py +24 -0
- schemathesis/config/_operations.py +335 -0
- schemathesis/config/_output.py +171 -0
- schemathesis/config/_parameters.py +19 -0
- schemathesis/config/_phases.py +253 -0
- schemathesis/config/_projects.py +543 -0
- schemathesis/config/_rate_limit.py +17 -0
- schemathesis/config/_report.py +120 -0
- schemathesis/config/_validator.py +9 -0
- schemathesis/config/_warnings.py +89 -0
- schemathesis/config/schema.json +975 -0
- schemathesis/core/__init__.py +72 -0
- schemathesis/core/adapter.py +34 -0
- schemathesis/core/compat.py +32 -0
- schemathesis/core/control.py +2 -0
- schemathesis/core/curl.py +100 -0
- schemathesis/core/deserialization.py +210 -0
- schemathesis/core/errors.py +588 -0
- schemathesis/core/failures.py +316 -0
- schemathesis/core/fs.py +19 -0
- schemathesis/core/hooks.py +20 -0
- schemathesis/core/jsonschema/__init__.py +13 -0
- schemathesis/core/jsonschema/bundler.py +183 -0
- schemathesis/core/jsonschema/keywords.py +40 -0
- schemathesis/core/jsonschema/references.py +222 -0
- schemathesis/core/jsonschema/types.py +41 -0
- schemathesis/core/lazy_import.py +15 -0
- schemathesis/core/loaders.py +107 -0
- schemathesis/core/marks.py +66 -0
- schemathesis/core/media_types.py +79 -0
- schemathesis/core/output/__init__.py +46 -0
- schemathesis/core/output/sanitization.py +54 -0
- schemathesis/core/parameters.py +45 -0
- schemathesis/core/rate_limit.py +60 -0
- schemathesis/core/registries.py +34 -0
- schemathesis/core/result.py +27 -0
- schemathesis/core/schema_analysis.py +17 -0
- schemathesis/core/shell.py +203 -0
- schemathesis/core/transforms.py +144 -0
- schemathesis/core/transport.py +223 -0
- schemathesis/core/validation.py +73 -0
- schemathesis/core/version.py +7 -0
- schemathesis/engine/__init__.py +28 -0
- schemathesis/engine/context.py +152 -0
- schemathesis/engine/control.py +44 -0
- schemathesis/engine/core.py +201 -0
- schemathesis/engine/errors.py +446 -0
- schemathesis/engine/events.py +284 -0
- schemathesis/engine/observations.py +42 -0
- schemathesis/engine/phases/__init__.py +108 -0
- schemathesis/engine/phases/analysis.py +28 -0
- schemathesis/engine/phases/probes.py +172 -0
- schemathesis/engine/phases/stateful/__init__.py +68 -0
- schemathesis/engine/phases/stateful/_executor.py +364 -0
- schemathesis/engine/phases/stateful/context.py +85 -0
- schemathesis/engine/phases/unit/__init__.py +220 -0
- schemathesis/engine/phases/unit/_executor.py +459 -0
- schemathesis/engine/phases/unit/_pool.py +82 -0
- schemathesis/engine/recorder.py +254 -0
- schemathesis/errors.py +47 -0
- schemathesis/filters.py +395 -0
- schemathesis/generation/__init__.py +25 -0
- schemathesis/generation/case.py +478 -0
- schemathesis/generation/coverage.py +1528 -0
- schemathesis/generation/hypothesis/__init__.py +121 -0
- schemathesis/generation/hypothesis/builder.py +992 -0
- schemathesis/generation/hypothesis/examples.py +56 -0
- schemathesis/generation/hypothesis/given.py +66 -0
- schemathesis/generation/hypothesis/reporting.py +285 -0
- schemathesis/generation/meta.py +227 -0
- schemathesis/generation/metrics.py +93 -0
- schemathesis/generation/modes.py +20 -0
- schemathesis/generation/overrides.py +127 -0
- schemathesis/generation/stateful/__init__.py +37 -0
- schemathesis/generation/stateful/state_machine.py +294 -0
- schemathesis/graphql/__init__.py +15 -0
- schemathesis/graphql/checks.py +109 -0
- schemathesis/graphql/loaders.py +285 -0
- schemathesis/hooks.py +270 -91
- schemathesis/openapi/__init__.py +13 -0
- schemathesis/openapi/checks.py +467 -0
- schemathesis/openapi/generation/__init__.py +0 -0
- schemathesis/openapi/generation/filters.py +72 -0
- schemathesis/openapi/loaders.py +315 -0
- schemathesis/pytest/__init__.py +5 -0
- schemathesis/pytest/control_flow.py +7 -0
- schemathesis/pytest/lazy.py +341 -0
- schemathesis/pytest/loaders.py +36 -0
- schemathesis/pytest/plugin.py +357 -0
- schemathesis/python/__init__.py +0 -0
- schemathesis/python/asgi.py +12 -0
- schemathesis/python/wsgi.py +12 -0
- schemathesis/schemas.py +683 -247
- schemathesis/specs/graphql/__init__.py +0 -1
- schemathesis/specs/graphql/nodes.py +27 -0
- schemathesis/specs/graphql/scalars.py +86 -0
- schemathesis/specs/graphql/schemas.py +395 -123
- schemathesis/specs/graphql/validation.py +33 -0
- schemathesis/specs/openapi/__init__.py +9 -1
- schemathesis/specs/openapi/_hypothesis.py +578 -317
- schemathesis/specs/openapi/adapter/__init__.py +10 -0
- schemathesis/specs/openapi/adapter/parameters.py +729 -0
- schemathesis/specs/openapi/adapter/protocol.py +59 -0
- schemathesis/specs/openapi/adapter/references.py +19 -0
- schemathesis/specs/openapi/adapter/responses.py +368 -0
- schemathesis/specs/openapi/adapter/security.py +144 -0
- schemathesis/specs/openapi/adapter/v2.py +30 -0
- schemathesis/specs/openapi/adapter/v3_0.py +30 -0
- schemathesis/specs/openapi/adapter/v3_1.py +30 -0
- schemathesis/specs/openapi/analysis.py +96 -0
- schemathesis/specs/openapi/checks.py +753 -74
- schemathesis/specs/openapi/converter.py +176 -37
- schemathesis/specs/openapi/definitions.py +599 -4
- schemathesis/specs/openapi/examples.py +581 -165
- schemathesis/specs/openapi/expressions/__init__.py +52 -5
- schemathesis/specs/openapi/expressions/extractors.py +25 -0
- schemathesis/specs/openapi/expressions/lexer.py +34 -31
- schemathesis/specs/openapi/expressions/nodes.py +97 -46
- schemathesis/specs/openapi/expressions/parser.py +35 -13
- schemathesis/specs/openapi/formats.py +122 -0
- schemathesis/specs/openapi/media_types.py +75 -0
- schemathesis/specs/openapi/negative/__init__.py +117 -68
- schemathesis/specs/openapi/negative/mutations.py +294 -104
- schemathesis/specs/openapi/negative/utils.py +3 -6
- schemathesis/specs/openapi/patterns.py +458 -0
- schemathesis/specs/openapi/references.py +60 -81
- schemathesis/specs/openapi/schemas.py +648 -650
- schemathesis/specs/openapi/serialization.py +53 -30
- schemathesis/specs/openapi/stateful/__init__.py +404 -69
- schemathesis/specs/openapi/stateful/control.py +87 -0
- schemathesis/specs/openapi/stateful/dependencies/__init__.py +232 -0
- schemathesis/specs/openapi/stateful/dependencies/inputs.py +428 -0
- schemathesis/specs/openapi/stateful/dependencies/models.py +341 -0
- schemathesis/specs/openapi/stateful/dependencies/naming.py +491 -0
- schemathesis/specs/openapi/stateful/dependencies/outputs.py +34 -0
- schemathesis/specs/openapi/stateful/dependencies/resources.py +339 -0
- schemathesis/specs/openapi/stateful/dependencies/schemas.py +447 -0
- schemathesis/specs/openapi/stateful/inference.py +254 -0
- schemathesis/specs/openapi/stateful/links.py +219 -78
- schemathesis/specs/openapi/types/__init__.py +3 -0
- schemathesis/specs/openapi/types/common.py +23 -0
- schemathesis/specs/openapi/types/v2.py +129 -0
- schemathesis/specs/openapi/types/v3.py +134 -0
- schemathesis/specs/openapi/utils.py +7 -6
- schemathesis/specs/openapi/warnings.py +75 -0
- schemathesis/transport/__init__.py +224 -0
- schemathesis/transport/asgi.py +26 -0
- schemathesis/transport/prepare.py +126 -0
- schemathesis/transport/requests.py +278 -0
- schemathesis/transport/serialization.py +329 -0
- schemathesis/transport/wsgi.py +175 -0
- schemathesis-4.4.2.dist-info/METADATA +213 -0
- schemathesis-4.4.2.dist-info/RECORD +192 -0
- {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info}/WHEEL +1 -1
- schemathesis-4.4.2.dist-info/entry_points.txt +6 -0
- {schemathesis-3.13.0.dist-info → schemathesis-4.4.2.dist-info/licenses}/LICENSE +1 -1
- schemathesis/_compat.py +0 -41
- schemathesis/_hypothesis.py +0 -115
- schemathesis/cli/callbacks.py +0 -188
- schemathesis/cli/cassettes.py +0 -253
- schemathesis/cli/context.py +0 -36
- schemathesis/cli/debug.py +0 -21
- schemathesis/cli/handlers.py +0 -11
- schemathesis/cli/junitxml.py +0 -41
- schemathesis/cli/options.py +0 -51
- schemathesis/cli/output/__init__.py +0 -1
- schemathesis/cli/output/default.py +0 -508
- schemathesis/cli/output/short.py +0 -40
- schemathesis/constants.py +0 -79
- schemathesis/exceptions.py +0 -207
- schemathesis/extra/_aiohttp.py +0 -27
- schemathesis/extra/_flask.py +0 -10
- schemathesis/extra/_server.py +0 -16
- schemathesis/extra/pytest_plugin.py +0 -216
- schemathesis/failures.py +0 -131
- schemathesis/fixups/__init__.py +0 -29
- schemathesis/fixups/fast_api.py +0 -30
- schemathesis/lazy.py +0 -227
- schemathesis/models.py +0 -1041
- schemathesis/parameters.py +0 -88
- schemathesis/runner/__init__.py +0 -460
- schemathesis/runner/events.py +0 -240
- schemathesis/runner/impl/__init__.py +0 -3
- schemathesis/runner/impl/core.py +0 -755
- schemathesis/runner/impl/solo.py +0 -85
- schemathesis/runner/impl/threadpool.py +0 -367
- schemathesis/runner/serialization.py +0 -189
- schemathesis/serializers.py +0 -233
- schemathesis/service/__init__.py +0 -3
- schemathesis/service/client.py +0 -46
- schemathesis/service/constants.py +0 -12
- schemathesis/service/events.py +0 -39
- schemathesis/service/handler.py +0 -39
- schemathesis/service/models.py +0 -7
- schemathesis/service/serialization.py +0 -153
- schemathesis/service/worker.py +0 -40
- schemathesis/specs/graphql/loaders.py +0 -215
- schemathesis/specs/openapi/constants.py +0 -7
- schemathesis/specs/openapi/expressions/context.py +0 -12
- schemathesis/specs/openapi/expressions/pointers.py +0 -29
- schemathesis/specs/openapi/filters.py +0 -44
- schemathesis/specs/openapi/links.py +0 -302
- schemathesis/specs/openapi/loaders.py +0 -453
- schemathesis/specs/openapi/parameters.py +0 -413
- schemathesis/specs/openapi/security.py +0 -129
- schemathesis/specs/openapi/validation.py +0 -24
- schemathesis/stateful.py +0 -349
- schemathesis/targets.py +0 -32
- schemathesis/types.py +0 -38
- schemathesis/utils.py +0 -436
- schemathesis-3.13.0.dist-info/METADATA +0 -202
- schemathesis-3.13.0.dist-info/RECORD +0 -91
- schemathesis-3.13.0.dist-info/entry_points.txt +0 -6
- /schemathesis/{extra → cli/ext}/__init__.py +0 -0
schemathesis/utils.py
DELETED
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import cgi
|
|
2
|
-
import functools
|
|
3
|
-
import pathlib
|
|
4
|
-
import re
|
|
5
|
-
import sys
|
|
6
|
-
import traceback
|
|
7
|
-
import warnings
|
|
8
|
-
from contextlib import contextmanager
|
|
9
|
-
from inspect import getfullargspec
|
|
10
|
-
from json import JSONDecodeError
|
|
11
|
-
from typing import (
|
|
12
|
-
Any,
|
|
13
|
-
Callable,
|
|
14
|
-
Dict,
|
|
15
|
-
Generator,
|
|
16
|
-
Generic,
|
|
17
|
-
List,
|
|
18
|
-
NoReturn,
|
|
19
|
-
Optional,
|
|
20
|
-
Set,
|
|
21
|
-
Tuple,
|
|
22
|
-
Type,
|
|
23
|
-
TypeVar,
|
|
24
|
-
Union,
|
|
25
|
-
overload,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
import pytest
|
|
29
|
-
import requests
|
|
30
|
-
import yaml
|
|
31
|
-
import yarl
|
|
32
|
-
from hypothesis.core import is_invalid_test
|
|
33
|
-
from hypothesis.reporting import with_reporter
|
|
34
|
-
from hypothesis.strategies import SearchStrategy
|
|
35
|
-
from hypothesis.utils.conventions import InferType
|
|
36
|
-
from requests.auth import HTTPDigestAuth
|
|
37
|
-
from requests.exceptions import InvalidHeader # type: ignore
|
|
38
|
-
from requests.utils import check_header_validity # type: ignore
|
|
39
|
-
from werkzeug.wrappers import Response as BaseResponse
|
|
40
|
-
from werkzeug.wrappers.json import JSONMixin
|
|
41
|
-
|
|
42
|
-
from .constants import USER_AGENT, DataGenerationMethod
|
|
43
|
-
from .exceptions import UsageError
|
|
44
|
-
from .types import DataGenerationMethodInput, Filter, GenericTest, NotSet, RawAuth
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
from yaml import CSafeLoader as SafeLoader
|
|
48
|
-
except ImportError:
|
|
49
|
-
# pylint: disable=unused-import
|
|
50
|
-
from yaml import SafeLoader # type: ignore
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
NOT_SET = NotSet()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def file_exists(path: str) -> bool:
|
|
57
|
-
try:
|
|
58
|
-
return pathlib.Path(path).is_file()
|
|
59
|
-
except OSError:
|
|
60
|
-
# For example, path could be too long
|
|
61
|
-
return False
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def is_latin_1_encodable(value: str) -> bool:
|
|
65
|
-
"""Header values are encoded to latin-1 before sending."""
|
|
66
|
-
try:
|
|
67
|
-
value.encode("latin-1")
|
|
68
|
-
return True
|
|
69
|
-
except UnicodeEncodeError:
|
|
70
|
-
return False
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
# Adapted from http.client._is_illegal_header_value
|
|
74
|
-
INVALID_HEADER_RE = re.compile(r"\n(?![ \t])|\r(?![ \t\n])") # pragma: no mutate
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def has_invalid_characters(name: str, value: str) -> bool:
|
|
78
|
-
try:
|
|
79
|
-
check_header_validity((name, value))
|
|
80
|
-
return bool(INVALID_HEADER_RE.search(value))
|
|
81
|
-
except InvalidHeader:
|
|
82
|
-
return True
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def is_schemathesis_test(func: Callable) -> bool:
|
|
86
|
-
"""Check whether test is parametrized with schemathesis."""
|
|
87
|
-
try:
|
|
88
|
-
from .schemas import BaseSchema # pylint: disable=import-outside-toplevel
|
|
89
|
-
|
|
90
|
-
item = getattr(func, PARAMETRIZE_MARKER, None)
|
|
91
|
-
# Comparison is needed to avoid false-positives when mocks are collected by pytest
|
|
92
|
-
return isinstance(item, BaseSchema)
|
|
93
|
-
except Exception:
|
|
94
|
-
return False
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def fail_on_no_matches(node_id: str) -> NoReturn: # type: ignore
|
|
98
|
-
pytest.fail(f"Test function {node_id} does not match any API operations and therefore has no effect")
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def force_tuple(item: Filter) -> Union[List, Set, Tuple]:
|
|
102
|
-
if not isinstance(item, (list, set, tuple)):
|
|
103
|
-
return (item,)
|
|
104
|
-
return item
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def dict_true_values(**kwargs: Any) -> Dict[str, Any]:
|
|
108
|
-
"""Create a dict with given kwargs while skipping items where bool(value) evaluates to False."""
|
|
109
|
-
return {key: value for key, value in kwargs.items() if bool(value)}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def dict_not_none_values(**kwargs: Any) -> Dict[str, Any]:
|
|
113
|
-
return {key: value for key, value in kwargs.items() if value is not None}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
IGNORED_PATTERNS = (
|
|
117
|
-
"Falsifying example: ",
|
|
118
|
-
"You can add @seed",
|
|
119
|
-
"Failed to reproduce exception. Expected:",
|
|
120
|
-
"Flaky example!",
|
|
121
|
-
"Traceback (most recent call last):",
|
|
122
|
-
"You can reproduce this example by temporarily",
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@contextmanager
|
|
127
|
-
def capture_hypothesis_output() -> Generator[List[str], None, None]:
|
|
128
|
-
"""Capture all output of Hypothesis into a list of strings.
|
|
129
|
-
|
|
130
|
-
It allows us to have more granular control over Schemathesis output.
|
|
131
|
-
|
|
132
|
-
Usage::
|
|
133
|
-
|
|
134
|
-
@given(i=st.integers())
|
|
135
|
-
def test(i):
|
|
136
|
-
assert 0
|
|
137
|
-
|
|
138
|
-
with capture_hypothesis_output() as output:
|
|
139
|
-
test() # hypothesis test
|
|
140
|
-
# output == ["Falsifying example: test(i=0)"]
|
|
141
|
-
"""
|
|
142
|
-
output = []
|
|
143
|
-
|
|
144
|
-
def get_output(value: str) -> None:
|
|
145
|
-
# Drop messages that could be confusing in the Schemathesis context
|
|
146
|
-
if value.startswith(IGNORED_PATTERNS):
|
|
147
|
-
return
|
|
148
|
-
output.append(value)
|
|
149
|
-
|
|
150
|
-
# the following context manager is untyped
|
|
151
|
-
with with_reporter(get_output): # type: ignore
|
|
152
|
-
yield output
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def format_exception(error: Exception, include_traceback: bool = False) -> str:
|
|
156
|
-
"""Format exception as text."""
|
|
157
|
-
error_type = type(error)
|
|
158
|
-
if include_traceback:
|
|
159
|
-
lines = traceback.format_exception(error_type, error, error.__traceback__)
|
|
160
|
-
else:
|
|
161
|
-
lines = traceback.format_exception_only(error_type, error)
|
|
162
|
-
return "".join(lines)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def parse_content_type(content_type: str) -> Tuple[str, str]:
|
|
166
|
-
"""Parse Content Type and return main type and subtype."""
|
|
167
|
-
try:
|
|
168
|
-
content_type, _ = cgi.parse_header(content_type)
|
|
169
|
-
main_type, sub_type = content_type.split("/", 1)
|
|
170
|
-
except ValueError as exc:
|
|
171
|
-
raise ValueError(f"Malformed media type: `{content_type}`") from exc
|
|
172
|
-
return main_type.lower(), sub_type.lower()
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def is_json_media_type(value: str) -> bool:
|
|
176
|
-
"""Detect whether the content type is JSON-compatible.
|
|
177
|
-
|
|
178
|
-
For example - ``application/problem+json`` matches.
|
|
179
|
-
"""
|
|
180
|
-
main, sub = parse_content_type(value)
|
|
181
|
-
return main == "application" and (sub == "json" or sub.endswith("+json"))
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def is_plain_text_media_type(value: str) -> bool:
|
|
185
|
-
"""Detect variations of the ``text/plain`` media type."""
|
|
186
|
-
return parse_content_type(value) == ("text", "plain")
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def are_content_types_equal(source: str, target: str) -> bool:
|
|
190
|
-
"""Check if two content types are the same excluding options."""
|
|
191
|
-
return parse_content_type(source) == parse_content_type(target)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def make_loader(*tags_to_remove: str) -> Type[yaml.SafeLoader]:
|
|
195
|
-
"""Create a YAML loader, that doesn't parse specific tokens into Python objects."""
|
|
196
|
-
cls: Type[yaml.SafeLoader] = type("YAMLLoader", (SafeLoader,), {})
|
|
197
|
-
cls.yaml_implicit_resolvers = {
|
|
198
|
-
key: [(tag, regexp) for tag, regexp in mapping if tag not in tags_to_remove]
|
|
199
|
-
for key, mapping in cls.yaml_implicit_resolvers.copy().items()
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
# Fix pyyaml scientific notation parse bug
|
|
203
|
-
# See PR: https://github.com/yaml/pyyaml/pull/174 for upstream fix
|
|
204
|
-
cls.add_implicit_resolver( # type: ignore
|
|
205
|
-
"tag:yaml.org,2002:float",
|
|
206
|
-
re.compile(
|
|
207
|
-
r"""^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+]?[0-9]+)?
|
|
208
|
-
|[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
|
|
209
|
-
|\.[0-9_]+(?:[eE][-+]?[0-9]+)?
|
|
210
|
-
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
|
|
211
|
-
|[-+]?\.(?:inf|Inf|INF)
|
|
212
|
-
|\.(?:nan|NaN|NAN))$""",
|
|
213
|
-
re.X,
|
|
214
|
-
),
|
|
215
|
-
list("-+0123456789."),
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
return cls
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
StringDatesYAMLLoader = make_loader("tag:yaml.org,2002:timestamp")
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
class WSGIResponse(BaseResponse, JSONMixin): # pylint: disable=too-many-ancestors
|
|
225
|
-
# We store "requests" request to build a reproduction code
|
|
226
|
-
request: requests.PreparedRequest
|
|
227
|
-
|
|
228
|
-
def on_json_loading_failed(self, e: JSONDecodeError) -> NoReturn:
|
|
229
|
-
# We don't need a werkzeug-specific exception when JSON parsing error happens
|
|
230
|
-
raise e
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def get_requests_auth(auth: Optional[RawAuth], auth_type: Optional[str]) -> Optional[Union[HTTPDigestAuth, RawAuth]]:
|
|
234
|
-
if auth and auth_type == "digest":
|
|
235
|
-
return HTTPDigestAuth(*auth)
|
|
236
|
-
return auth
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
GenericResponse = Union[requests.Response, WSGIResponse] # pragma: no mutate
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
def get_response_payload(response: GenericResponse) -> str:
|
|
243
|
-
if isinstance(response, requests.Response):
|
|
244
|
-
return response.text
|
|
245
|
-
return response.get_data(as_text=True)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def import_app(path: str) -> Any:
|
|
249
|
-
"""Import an application from a string."""
|
|
250
|
-
path, name = (re.split(r":(?![\\/])", path, 1) + [""])[:2]
|
|
251
|
-
__import__(path)
|
|
252
|
-
# accessing the module from sys.modules returns a proper module, while `__import__`
|
|
253
|
-
# may return a parent module (system dependent)
|
|
254
|
-
module = sys.modules[path]
|
|
255
|
-
return getattr(module, name)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
Schema = Union[Dict[str, Any], List, str, float, int]
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
@overload
|
|
262
|
-
def traverse_schema(schema: Dict[str, Any], callback: Callable, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
263
|
-
pass
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
@overload
|
|
267
|
-
def traverse_schema(schema: List, callback: Callable, *args: Any, **kwargs: Any) -> List:
|
|
268
|
-
pass
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
@overload
|
|
272
|
-
def traverse_schema(schema: str, callback: Callable, *args: Any, **kwargs: Any) -> str:
|
|
273
|
-
pass
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
@overload
|
|
277
|
-
def traverse_schema(schema: float, callback: Callable, *args: Any, **kwargs: Any) -> float:
|
|
278
|
-
pass
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
def traverse_schema(schema: Schema, callback: Callable[..., Dict[str, Any]], *args: Any, **kwargs: Any) -> Schema:
|
|
282
|
-
"""Apply callback recursively to the given schema."""
|
|
283
|
-
if isinstance(schema, dict):
|
|
284
|
-
schema = callback(schema, *args, **kwargs)
|
|
285
|
-
for key, sub_item in schema.items():
|
|
286
|
-
schema[key] = traverse_schema(sub_item, callback, *args, **kwargs)
|
|
287
|
-
elif isinstance(schema, list):
|
|
288
|
-
schema = [traverse_schema(sub_item, callback, *args, **kwargs) for sub_item in schema]
|
|
289
|
-
return schema
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
def _warn_deprecation(*, thing: str, removed_in: str, replacement: str) -> None:
|
|
293
|
-
warnings.warn(
|
|
294
|
-
f"Property `{thing}` is deprecated and will be removed in Schemathesis {removed_in}. "
|
|
295
|
-
f"Use `{replacement}` instead.",
|
|
296
|
-
DeprecationWarning,
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def deprecated_property(*, removed_in: str, replacement: str) -> Callable:
|
|
301
|
-
def wrapper(prop: Callable) -> Callable:
|
|
302
|
-
@property # type: ignore
|
|
303
|
-
def inner(self: Any) -> Any:
|
|
304
|
-
_warn_deprecation(thing=prop.__name__, removed_in=removed_in, replacement=replacement)
|
|
305
|
-
return prop(self)
|
|
306
|
-
|
|
307
|
-
return inner
|
|
308
|
-
|
|
309
|
-
return wrapper
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def deprecated(*, removed_in: str, replacement: str) -> Callable:
|
|
313
|
-
def wrapper(func: Callable) -> Callable:
|
|
314
|
-
def inner(*args: Any, **kwargs: Any) -> Any:
|
|
315
|
-
_warn_deprecation(thing=func.__name__, removed_in=removed_in, replacement=replacement)
|
|
316
|
-
return func(*args, **kwargs)
|
|
317
|
-
|
|
318
|
-
return inner
|
|
319
|
-
|
|
320
|
-
return wrapper
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def setup_headers(kwargs: Dict[str, Any]) -> None:
|
|
324
|
-
headers = kwargs.setdefault("headers", {})
|
|
325
|
-
if "user-agent" not in {header.lower() for header in headers}:
|
|
326
|
-
kwargs["headers"]["User-Agent"] = USER_AGENT
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def require_relative_url(url: str) -> None:
|
|
330
|
-
"""Raise an error if the URL is not relative."""
|
|
331
|
-
if yarl.URL(url).is_absolute():
|
|
332
|
-
raise ValueError("Schema path should be relative for WSGI/ASGI loaders")
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
T = TypeVar("T")
|
|
336
|
-
E = TypeVar("E", bound=Exception)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
class Ok(Generic[T]):
|
|
340
|
-
__slots__ = ("_value",)
|
|
341
|
-
|
|
342
|
-
def __init__(self, value: T):
|
|
343
|
-
self._value = value
|
|
344
|
-
|
|
345
|
-
def ok(self) -> T:
|
|
346
|
-
return self._value
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
class Err(Generic[E]):
|
|
350
|
-
__slots__ = ("_error",)
|
|
351
|
-
|
|
352
|
-
def __init__(self, error: E):
|
|
353
|
-
self._error = error
|
|
354
|
-
|
|
355
|
-
def err(self) -> E:
|
|
356
|
-
return self._error
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
Result = Union[Ok[T], Err[E]]
|
|
360
|
-
GivenInput = Union[SearchStrategy, InferType]
|
|
361
|
-
PARAMETRIZE_MARKER = "_schemathesis_test"
|
|
362
|
-
GIVEN_ARGS_MARKER = "_schemathesis_given_args"
|
|
363
|
-
GIVEN_KWARGS_MARKER = "_schemathesis_given_kwargs"
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
def get_given_args(func: GenericTest) -> Tuple:
|
|
367
|
-
return getattr(func, GIVEN_ARGS_MARKER, ())
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
def get_given_kwargs(func: GenericTest) -> Dict[str, Any]:
|
|
371
|
-
return getattr(func, GIVEN_KWARGS_MARKER, {})
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def is_given_applied(func: GenericTest) -> bool:
|
|
375
|
-
return hasattr(func, GIVEN_ARGS_MARKER) or hasattr(func, GIVEN_KWARGS_MARKER)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
def given_proxy(*args: GivenInput, **kwargs: GivenInput) -> Callable[[GenericTest], GenericTest]:
|
|
379
|
-
"""Proxy Hypothesis strategies to ``hypothesis.given``."""
|
|
380
|
-
|
|
381
|
-
def wrapper(func: GenericTest) -> GenericTest:
|
|
382
|
-
if hasattr(func, GIVEN_ARGS_MARKER):
|
|
383
|
-
|
|
384
|
-
def wrapped_test(*_: Any, **__: Any) -> NoReturn:
|
|
385
|
-
raise UsageError(
|
|
386
|
-
f"You have applied `given` to the `{func.__name__}` test more than once, which "
|
|
387
|
-
"overrides the previous decorator. You need to pass all arguments to the same `given` call."
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
return wrapped_test
|
|
391
|
-
|
|
392
|
-
setattr(func, GIVEN_ARGS_MARKER, args)
|
|
393
|
-
setattr(func, GIVEN_KWARGS_MARKER, kwargs)
|
|
394
|
-
return func
|
|
395
|
-
|
|
396
|
-
return wrapper
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
def merge_given_args(func: GenericTest, args: Tuple, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
400
|
-
"""Merge positional arguments to ``@schema.given`` into a dictionary with keyword arguments.
|
|
401
|
-
|
|
402
|
-
Kwargs are modified inplace.
|
|
403
|
-
"""
|
|
404
|
-
if args:
|
|
405
|
-
argspec = getfullargspec(func)
|
|
406
|
-
for name, strategy in zip(reversed([arg for arg in argspec.args if arg != "case"]), reversed(args)):
|
|
407
|
-
kwargs[name] = strategy
|
|
408
|
-
return kwargs
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
def validate_given_args(func: GenericTest, args: Tuple, kwargs: Dict[str, Any]) -> Optional[Callable]:
|
|
412
|
-
argspec = getfullargspec(func)
|
|
413
|
-
return is_invalid_test(func, argspec, args, kwargs) # type: ignore
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def compose(*functions: Callable) -> Callable:
|
|
417
|
-
"""Compose multiple functions into a single one."""
|
|
418
|
-
|
|
419
|
-
def noop(x: Any) -> Any:
|
|
420
|
-
return x
|
|
421
|
-
|
|
422
|
-
return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, noop)
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
def maybe_set_assertion_message(exc: AssertionError, check_name: str) -> str:
|
|
426
|
-
message = str(exc)
|
|
427
|
-
if not message:
|
|
428
|
-
message = f"Check '{check_name}' failed"
|
|
429
|
-
exc.args = (message,)
|
|
430
|
-
return message
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
def prepare_data_generation_methods(data_generation_methods: DataGenerationMethodInput) -> List[DataGenerationMethod]:
|
|
434
|
-
if isinstance(data_generation_methods, DataGenerationMethod):
|
|
435
|
-
return [data_generation_methods]
|
|
436
|
-
return list(data_generation_methods)
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: schemathesis
|
|
3
|
-
Version: 3.13.0
|
|
4
|
-
Summary: Property-based testing framework for Open API and GraphQL based apps
|
|
5
|
-
Home-page: https://github.com/schemathesis/schemathesis
|
|
6
|
-
License: MIT
|
|
7
|
-
Keywords: pytest,hypothesis,openapi,swagger,graphql,testing
|
|
8
|
-
Author: Dmitry Dygalo
|
|
9
|
-
Author-email: dadygalo@gmail.com
|
|
10
|
-
Maintainer: Dmitry Dygalo
|
|
11
|
-
Maintainer-email: dadygalo@gmail.com
|
|
12
|
-
Requires-Python: >=3.7,<4.0
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Environment :: Console
|
|
15
|
-
Classifier: Framework :: Hypothesis
|
|
16
|
-
Classifier: Framework :: Pytest
|
|
17
|
-
Classifier: Intended Audience :: Developers
|
|
18
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
-
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Programming Language :: Python :: 3
|
|
21
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
26
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
27
|
-
Classifier: Topic :: Software Development :: Testing
|
|
28
|
-
Requires-Dist: attrs (>=19.2)
|
|
29
|
-
Requires-Dist: click (>=8.0,<9.0)
|
|
30
|
-
Requires-Dist: colorama (>=0.4,<0.5)
|
|
31
|
-
Requires-Dist: curlify (>=2.2.1,<3.0.0)
|
|
32
|
-
Requires-Dist: hypothesis (>=6.13.3,<7.0)
|
|
33
|
-
Requires-Dist: hypothesis_graphql (>=0.5.0)
|
|
34
|
-
Requires-Dist: hypothesis_jsonschema (>=0.22.0)
|
|
35
|
-
Requires-Dist: importlib_metadata (>=1.1,!=3.8); python_version < "3.8"
|
|
36
|
-
Requires-Dist: jsonschema (>=4.3.2,<5.0.0)
|
|
37
|
-
Requires-Dist: junit-xml (>=1.9,<2.0)
|
|
38
|
-
Requires-Dist: pytest (>4.6.4)
|
|
39
|
-
Requires-Dist: pytest-subtests (>=0.2.1,<1.0)
|
|
40
|
-
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
41
|
-
Requires-Dist: requests (>=2.22,<3.0)
|
|
42
|
-
Requires-Dist: starlette (>=0.13,<1)
|
|
43
|
-
Requires-Dist: typing-extensions (>=3.7,<5)
|
|
44
|
-
Requires-Dist: werkzeug (>=0.16.0)
|
|
45
|
-
Requires-Dist: yarl (>=1.5,<2.0)
|
|
46
|
-
Project-URL: Change Log, https://github.com/schemathesis/schemathesis/blob/master/docs/changelog.rst
|
|
47
|
-
Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
|
|
48
|
-
Project-URL: Funding, https://github.com/sponsors/Stranger6667
|
|
49
|
-
Project-URL: Repository, https://github.com/schemathesis/schemathesis
|
|
50
|
-
Project-URL: Source Code, https://github.com/schemathesis/schemathesis
|
|
51
|
-
Description-Content-Type: text/x-rst
|
|
52
|
-
|
|
53
|
-
Schemathesis
|
|
54
|
-
============
|
|
55
|
-
|
|
56
|
-
|Build| |Coverage| |Version| |Python versions| |Docs| |Chat| |License|
|
|
57
|
-
|
|
58
|
-
Schemathesis is a modern API testing tool for web applications built with Open API and GraphQL specifications.
|
|
59
|
-
|
|
60
|
-
It reads the application schema and generates test cases, which will ensure that your application is compliant with its schema (read more about how it works in `our research paper <https://arxiv.org/abs/2112.10328>`_).
|
|
61
|
-
|
|
62
|
-
The application under test could be written in any language; the only thing you need is a valid API schema in a supported format.
|
|
63
|
-
|
|
64
|
-
Simple to use and yet powerful to uncover hard-to-find errors thanks to the property-based testing approach backed by state-of-the-art `Hypothesis <http://hypothesis.works/>`_ library.
|
|
65
|
-
|
|
66
|
-
Features
|
|
67
|
-
--------
|
|
68
|
-
|
|
69
|
-
- Content-Type, schema, and status code conformance checks for Open API;
|
|
70
|
-
- Testing of explicit examples from the input schema;
|
|
71
|
-
- Stateful testing via Open API links;
|
|
72
|
-
- Concurrent test execution;
|
|
73
|
-
- Targeted testing;
|
|
74
|
-
- Storing and replaying network requests;
|
|
75
|
-
- Built-in ASGI / WSGI application support;
|
|
76
|
-
- Code samples for easy failure reproduction;
|
|
77
|
-
- Ready-to-go Docker image;
|
|
78
|
-
- Configurable with user-defined checks, string formats, hooks, and targets.
|
|
79
|
-
|
|
80
|
-
📣 Schemathesis as a Service 📣
|
|
81
|
-
-------------------------------
|
|
82
|
-
|
|
83
|
-
Schemathesis will be available as `SaaS <https://schemathesis.io/?utm_source=github>`_ soon!
|
|
84
|
-
|
|
85
|
-
It is freemium with much better visuals for debugging, more checks, and static analysis :)
|
|
86
|
-
|
|
87
|
-
`Signup <http://eepurl.com/hN-0H1>`_ to get notified when it is ready!
|
|
88
|
-
|
|
89
|
-
Installation
|
|
90
|
-
------------
|
|
91
|
-
|
|
92
|
-
To install Schemathesis via ``pip`` run the following command:
|
|
93
|
-
|
|
94
|
-
.. code:: bash
|
|
95
|
-
|
|
96
|
-
pip install schemathesis
|
|
97
|
-
|
|
98
|
-
You can also use our Docker image without installing Schemathesis as a Python package.
|
|
99
|
-
|
|
100
|
-
📣 **Please fill out our** `quick survey <https://forms.gle/dv4s5SXAYWzvuwFWA>`_ so that we can learn how satisfied you are with Schemathesis, and what improvements we should make. Thank you!
|
|
101
|
-
|
|
102
|
-
Usage
|
|
103
|
-
-----
|
|
104
|
-
|
|
105
|
-
You can use Schemathesis in the command line directly:
|
|
106
|
-
|
|
107
|
-
.. code:: bash
|
|
108
|
-
|
|
109
|
-
schemathesis run --stateful=links --checks all http://0.0.0.0:8081/schema.yaml
|
|
110
|
-
|
|
111
|
-
Or via Docker:
|
|
112
|
-
|
|
113
|
-
.. code:: bash
|
|
114
|
-
|
|
115
|
-
docker run schemathesis/schemathesis:stable run \
|
|
116
|
-
--stateful=links --checks all http://0.0.0.0:8081/schema.yaml
|
|
117
|
-
|
|
118
|
-
.. image:: https://raw.githubusercontent.com/schemathesis/schemathesis/master/img/schemathesis.gif
|
|
119
|
-
|
|
120
|
-
Or in your Python tests:
|
|
121
|
-
|
|
122
|
-
.. code:: python
|
|
123
|
-
|
|
124
|
-
import schemathesis
|
|
125
|
-
|
|
126
|
-
schema = schemathesis.from_uri("http://0.0.0.0:8081/schema.yaml")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@schema.parametrize()
|
|
130
|
-
def test_api(case):
|
|
131
|
-
case.call_and_validate()
|
|
132
|
-
|
|
133
|
-
CLI is simple to use and requires no coding; the in-code approach gives more flexibility.
|
|
134
|
-
|
|
135
|
-
Both examples above will run hundreds of requests against the API under test and report all found failures and inconsistencies along with instructions to reproduce them.
|
|
136
|
-
|
|
137
|
-
💡 See a complete working example project in the ``/example`` directory. 💡
|
|
138
|
-
|
|
139
|
-
Contributing
|
|
140
|
-
------------
|
|
141
|
-
|
|
142
|
-
Any contribution to development, testing, or any other area is highly appreciated and useful to the project.
|
|
143
|
-
For guidance on how to contribute to Schemathesis, see the `contributing guidelines <https://github.com/schemathesis/schemathesis/blob/master/CONTRIBUTING.rst>`_.
|
|
144
|
-
|
|
145
|
-
Support this project
|
|
146
|
-
--------------------
|
|
147
|
-
|
|
148
|
-
Hi, my name is Dmitry! I started this project during my work at `Kiwi.com <https://kiwi.com/>`_. I am grateful to them for all the support they
|
|
149
|
-
provided to this project during its early days and for the opportunity to evolve Schemathesis independently.
|
|
150
|
-
|
|
151
|
-
In order to grow the community of contributors and users, and allow me to devote more time to this project, please `donate today <https://github.com/sponsors/Stranger6667>`_.
|
|
152
|
-
|
|
153
|
-
Also, I occasionally write posts about Schemathesis in `my blog <https://dygalo.dev/>`_.
|
|
154
|
-
|
|
155
|
-
Links
|
|
156
|
-
-----
|
|
157
|
-
|
|
158
|
-
- **Documentation**: https://schemathesis.readthedocs.io/en/stable/
|
|
159
|
-
- **Releases**: https://pypi.org/project/schemathesis/
|
|
160
|
-
- **Code**: https://github.com/schemathesis/schemathesis
|
|
161
|
-
- **Issue tracker**: https://github.com/schemathesis/schemathesis/issues
|
|
162
|
-
- **Chat**: https://gitter.im/schemathesis/schemathesis
|
|
163
|
-
|
|
164
|
-
Additional content:
|
|
165
|
-
|
|
166
|
-
- Research paper: `Deriving Semantics-Aware Fuzzers from Web API Schemas <https://arxiv.org/abs/2112.10328>`_ by **@Zac-HD** and **@Stranger6667**
|
|
167
|
-
- `An article <https://dygalo.dev/blog/schemathesis-property-based-testing-for-api-schemas/>`_ about Schemathesis by **@Stranger6667**
|
|
168
|
-
- `Effective API schemas testing <https://youtu.be/VVLZ25JgjD4>`_ from DevConf.cz by **@Stranger6667**
|
|
169
|
-
- `A video <https://www.youtube.com/watch?v=9FHRwrv-xuQ>`_ from EuroPython 2020 by **@hultner**
|
|
170
|
-
- `Schemathesis tutorial <https://appdev.consulting.redhat.com/tracks/contract-first/automated-testing-with-schemathesis.html>`_ with an accompanying `video <https://www.youtube.com/watch?v=4r7OC-lBKMg>`_ by Red Hat
|
|
171
|
-
- `Using Hypothesis and Schemathesis to Test FastAPI <https://testdriven.io/blog/fastapi-hypothesis/>`_ by **@amalshaji**
|
|
172
|
-
|
|
173
|
-
Non-English content:
|
|
174
|
-
|
|
175
|
-
- `A tutorial <https://habr.com/ru/company/oleg-bunin/blog/576496/>`_ (RUS) about Schemathesis by **@Stranger6667**
|
|
176
|
-
|
|
177
|
-
License
|
|
178
|
-
-------
|
|
179
|
-
|
|
180
|
-
The code in this project is licensed under `MIT license`_.
|
|
181
|
-
By contributing to Schemathesis, you agree that your contributions will be licensed under its MIT license.
|
|
182
|
-
|
|
183
|
-
.. |Build| image:: https://github.com/schemathesis/schemathesis/workflows/build/badge.svg
|
|
184
|
-
:target: https://github.com/schemathesis/schemathesis/actions
|
|
185
|
-
.. |Coverage| image:: https://codecov.io/gh/schemathesis/schemathesis/branch/master/graph/badge.svg
|
|
186
|
-
:target: https://codecov.io/gh/schemathesis/schemathesis/branch/master
|
|
187
|
-
:alt: codecov.io status for master branch
|
|
188
|
-
.. |Version| image:: https://img.shields.io/pypi/v/schemathesis.svg
|
|
189
|
-
:target: https://pypi.org/project/schemathesis/
|
|
190
|
-
.. |Python versions| image:: https://img.shields.io/pypi/pyversions/schemathesis.svg
|
|
191
|
-
:target: https://pypi.org/project/schemathesis/
|
|
192
|
-
.. |License| image:: https://img.shields.io/pypi/l/schemathesis.svg
|
|
193
|
-
:target: https://opensource.org/licenses/MIT
|
|
194
|
-
.. |Chat| image:: https://img.shields.io/gitter/room/schemathesis/schemathesis.svg
|
|
195
|
-
:target: https://gitter.im/schemathesis/schemathesis
|
|
196
|
-
:alt: Gitter
|
|
197
|
-
.. |Docs| image:: https://readthedocs.org/projects/schemathesis/badge/?version=stable
|
|
198
|
-
:target: https://schemathesis.readthedocs.io/en/stable/?badge=stable
|
|
199
|
-
:alt: Documentation Status
|
|
200
|
-
|
|
201
|
-
.. _MIT license: https://opensource.org/licenses/MIT
|
|
202
|
-
|