schemathesis 4.0.0b1__py3-none-any.whl → 4.0.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/cli/commands/run/handlers/cassettes.py +23 -5
- schemathesis/cli/commands/run/handlers/junitxml.py +8 -2
- schemathesis/cli/commands/run/handlers/output.py +15 -4
- schemathesis/cli/constants.py +1 -1
- schemathesis/config/_operations.py +16 -21
- schemathesis/core/__init__.py +2 -0
- schemathesis/core/errors.py +1 -1
- schemathesis/engine/control.py +10 -2
- schemathesis/engine/core.py +6 -0
- schemathesis/engine/errors.py +1 -1
- schemathesis/engine/phases/__init__.py +12 -2
- schemathesis/engine/phases/probes.py +25 -4
- schemathesis/generation/hypothesis/builder.py +35 -6
- schemathesis/generation/meta.py +4 -0
- schemathesis/generation/overrides.py +2 -0
- schemathesis/hooks.py +2 -0
- schemathesis/pytest/lazy.py +12 -2
- schemathesis/schemas.py +10 -4
- schemathesis/specs/graphql/_cache.py +13 -3
- schemathesis/specs/graphql/schemas.py +2 -0
- schemathesis/specs/openapi/examples.py +4 -0
- schemathesis/specs/openapi/expressions/extractors.py +2 -0
- schemathesis/specs/openapi/expressions/lexer.py +2 -0
- schemathesis/specs/openapi/expressions/nodes.py +35 -4
- schemathesis/specs/openapi/negative/__init__.py +2 -0
- schemathesis/specs/openapi/negative/mutations.py +2 -0
- schemathesis/specs/openapi/stateful/links.py +1 -0
- schemathesis/transport/prepare.py +0 -2
- schemathesis/transport/requests.py +24 -4
- schemathesis/transport/wsgi.py +11 -1
- {schemathesis-4.0.0b1.dist-info → schemathesis-4.0.2.dist-info}/METADATA +6 -12
- {schemathesis-4.0.0b1.dist-info → schemathesis-4.0.2.dist-info}/RECORD +35 -35
- {schemathesis-4.0.0b1.dist-info → schemathesis-4.0.2.dist-info}/WHEEL +0 -0
- {schemathesis-4.0.0b1.dist-info → schemathesis-4.0.2.dist-info}/entry_points.txt +0 -0
- {schemathesis-4.0.0b1.dist-info → schemathesis-4.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,7 @@ import datetime
|
|
4
4
|
import json
|
5
5
|
import sys
|
6
6
|
import threading
|
7
|
-
from dataclasses import dataclass
|
7
|
+
from dataclasses import dataclass
|
8
8
|
from http.cookies import SimpleCookie
|
9
9
|
from pathlib import Path
|
10
10
|
from queue import Queue
|
@@ -35,10 +35,23 @@ class CassetteWriter(EventHandler):
|
|
35
35
|
format: ReportFormat
|
36
36
|
path: Path
|
37
37
|
config: ProjectConfig
|
38
|
-
queue: Queue
|
39
|
-
worker: threading.Thread
|
38
|
+
queue: Queue
|
39
|
+
worker: threading.Thread
|
40
|
+
|
41
|
+
__slots__ = ("format", "path", "config", "queue", "worker")
|
42
|
+
|
43
|
+
def __init__(
|
44
|
+
self,
|
45
|
+
format: ReportFormat,
|
46
|
+
path: Path,
|
47
|
+
config: ProjectConfig,
|
48
|
+
queue: Queue | None = None,
|
49
|
+
) -> None:
|
50
|
+
self.format = format
|
51
|
+
self.path = path
|
52
|
+
self.config = config
|
53
|
+
self.queue = queue or Queue()
|
40
54
|
|
41
|
-
def __post_init__(self) -> None:
|
42
55
|
kwargs = {
|
43
56
|
"path": self.path,
|
44
57
|
"config": self.config,
|
@@ -49,7 +62,12 @@ class CassetteWriter(EventHandler):
|
|
49
62
|
writer = har_writer
|
50
63
|
else:
|
51
64
|
writer = vcr_writer
|
52
|
-
|
65
|
+
|
66
|
+
self.worker = threading.Thread(
|
67
|
+
name="SchemathesisCassetteWriter",
|
68
|
+
target=writer,
|
69
|
+
kwargs=kwargs,
|
70
|
+
)
|
53
71
|
self.worker.start()
|
54
72
|
|
55
73
|
def start(self, ctx: ExecutionContext) -> None:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import platform
|
4
|
-
from dataclasses import dataclass
|
4
|
+
from dataclasses import dataclass
|
5
5
|
from pathlib import Path
|
6
6
|
from typing import Iterable
|
7
7
|
|
@@ -16,7 +16,13 @@ from schemathesis.engine import Status, events
|
|
16
16
|
@dataclass
|
17
17
|
class JunitXMLHandler(EventHandler):
|
18
18
|
path: Path
|
19
|
-
test_cases: dict
|
19
|
+
test_cases: dict
|
20
|
+
|
21
|
+
__slots__ = ("path", "test_cases")
|
22
|
+
|
23
|
+
def __init__(self, path: Path, test_cases: dict | None = None) -> None:
|
24
|
+
self.path = path
|
25
|
+
self.test_cases = test_cases or {}
|
20
26
|
|
21
27
|
def handle_event(self, ctx: ExecutionContext, event: events.EngineEvent) -> None:
|
22
28
|
if isinstance(event, events.ScenarioFinished):
|
@@ -324,10 +324,21 @@ class ProbingProgressManager:
|
|
324
324
|
|
325
325
|
@dataclass
|
326
326
|
class WarningData:
|
327
|
-
missing_auth: dict[int, set[str]]
|
328
|
-
missing_test_data: set[str]
|
329
|
-
|
330
|
-
|
327
|
+
missing_auth: dict[int, set[str]]
|
328
|
+
missing_test_data: set[str]
|
329
|
+
validation_mismatch: set[str]
|
330
|
+
|
331
|
+
__slots__ = ("missing_auth", "missing_test_data", "validation_mismatch")
|
332
|
+
|
333
|
+
def __init__(
|
334
|
+
self,
|
335
|
+
missing_auth: dict[int, set[str]] | None = None,
|
336
|
+
missing_test_data: set[str] | None = None,
|
337
|
+
validation_mismatch: set[str] | None = None,
|
338
|
+
) -> None:
|
339
|
+
self.missing_auth = missing_auth or {}
|
340
|
+
self.missing_test_data = missing_test_data or set()
|
341
|
+
self.validation_mismatch = validation_mismatch or set()
|
331
342
|
|
332
343
|
@property
|
333
344
|
def is_empty(self) -> bool:
|
schemathesis/cli/constants.py
CHANGED
@@ -5,4 +5,4 @@ ISSUE_TRACKER_URL = (
|
|
5
5
|
"https://github.com/schemathesis/schemathesis/issues/new?"
|
6
6
|
"labels=Status%3A%20Needs%20Triage%2C+Type%3A+Bug&template=bug_report.md&title=%5BBUG%5D"
|
7
7
|
)
|
8
|
-
EXTENSIONS_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/
|
8
|
+
EXTENSIONS_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/stable/guides/extending/"
|
@@ -159,34 +159,23 @@ class OperationsConfig(DiffBase):
|
|
159
159
|
if exclude_deprecated:
|
160
160
|
exclude_set.include(is_deprecated)
|
161
161
|
|
162
|
-
|
163
|
-
if not include_set.is_empty():
|
164
|
-
self.operations.insert(0, OperationConfig(filter_set=include_set, enabled=True))
|
165
|
-
if not exclude_set.is_empty():
|
166
|
-
self.operations.insert(0, OperationConfig(filter_set=exclude_set, enabled=False))
|
162
|
+
operations = list(self.operations)
|
167
163
|
|
168
164
|
final = FilterSet()
|
169
165
|
|
170
|
-
# Get a stable reference to operations
|
171
|
-
operations = list(self.operations)
|
172
|
-
|
173
|
-
# Define a closure that implements our priority logic
|
174
166
|
def priority_filter(ctx: HasAPIOperation) -> bool:
|
175
167
|
"""Filter operations according to CLI and config priority."""
|
176
|
-
# 1. CLI includes override everything if present
|
177
|
-
if not include_set.is_empty():
|
178
|
-
return include_set.match(ctx)
|
179
|
-
|
180
|
-
# 2. CLI excludes take precedence over config
|
181
|
-
if not exclude_set.is_empty() and exclude_set.match(ctx):
|
182
|
-
return False
|
183
|
-
|
184
|
-
# 3. Check config operations in priority order (first match wins)
|
185
168
|
for op_config in operations:
|
186
|
-
if op_config._filter_set.match(ctx):
|
187
|
-
return
|
169
|
+
if op_config._filter_set.match(ctx) and not op_config.enabled:
|
170
|
+
return False
|
171
|
+
|
172
|
+
if not include_set.is_empty():
|
173
|
+
if exclude_set.is_empty():
|
174
|
+
return include_set.match(ctx)
|
175
|
+
return include_set.match(ctx) and not exclude_set.match(ctx)
|
176
|
+
elif not exclude_set.is_empty():
|
177
|
+
return not exclude_set.match(ctx)
|
188
178
|
|
189
|
-
# 4. Default to include if no rule matches
|
190
179
|
return True
|
191
180
|
|
192
181
|
# Add our priority function as the filter
|
@@ -278,24 +267,30 @@ class OperationConfig(DiffBase):
|
|
278
267
|
@classmethod
|
279
268
|
def from_dict(cls, data: dict[str, Any]) -> OperationConfig:
|
280
269
|
filter_set = FilterSet()
|
270
|
+
seen = set()
|
281
271
|
for key_suffix, arg_suffix in (("", ""), ("-regex", "_regex")):
|
282
272
|
for attr, arg_name in FILTER_ATTRIBUTES:
|
283
273
|
key = f"include-{attr}{key_suffix}"
|
284
274
|
if key in data:
|
275
|
+
seen.add(key)
|
285
276
|
with reraise_filter_error(attr):
|
286
277
|
filter_set.include(**{f"{arg_name}{arg_suffix}": data[key]})
|
287
278
|
key = f"exclude-{attr}{key_suffix}"
|
288
279
|
if key in data:
|
280
|
+
seen.add(key)
|
289
281
|
with reraise_filter_error(attr):
|
290
282
|
filter_set.exclude(**{f"{arg_name}{arg_suffix}": data[key]})
|
291
283
|
for key, method in (("include-by", filter_set.include), ("exclude-by", filter_set.exclude)):
|
292
284
|
if key in data:
|
285
|
+
seen.add(key)
|
293
286
|
expression = data[key]
|
294
287
|
try:
|
295
288
|
func = expression_to_filter_function(expression)
|
296
289
|
method(func)
|
297
290
|
except ValueError:
|
298
291
|
raise ConfigError(f"Invalid filter expression: '{expression}'") from None
|
292
|
+
if not set(data) - seen:
|
293
|
+
raise ConfigError("Operation filters defined, but no settings are being overridden")
|
299
294
|
|
300
295
|
return cls(
|
301
296
|
filter_set=filter_set,
|
schemathesis/core/__init__.py
CHANGED
schemathesis/core/errors.py
CHANGED
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
|
|
20
20
|
|
21
21
|
|
22
22
|
SCHEMA_ERROR_SUGGESTION = "Ensure that the definition complies with the OpenAPI specification"
|
23
|
-
SERIALIZERS_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/
|
23
|
+
SERIALIZERS_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/stable/guides/custom-serializers/"
|
24
24
|
SERIALIZERS_SUGGESTION_MESSAGE = f"Check your schema or add custom serializers: {SERIALIZERS_DOCUMENTATION_URL}"
|
25
25
|
SERIALIZATION_NOT_POSSIBLE_MESSAGE = f"No supported serializers for media types: {{}}\n{SERIALIZERS_SUGGESTION_MESSAGE}"
|
26
26
|
SERIALIZATION_FOR_TYPE_IS_NOT_POSSIBLE_MESSAGE = (
|
schemathesis/engine/control.py
CHANGED
@@ -12,8 +12,16 @@ class ExecutionControl:
|
|
12
12
|
|
13
13
|
stop_event: threading.Event
|
14
14
|
max_failures: int | None
|
15
|
-
_failures_counter: int
|
16
|
-
has_reached_the_failure_limit: bool
|
15
|
+
_failures_counter: int
|
16
|
+
has_reached_the_failure_limit: bool
|
17
|
+
|
18
|
+
__slots__ = ("stop_event", "max_failures", "_failures_counter", "has_reached_the_failure_limit")
|
19
|
+
|
20
|
+
def __init__(self, stop_event: threading.Event, max_failures: int | None) -> None:
|
21
|
+
self.stop_event = stop_event
|
22
|
+
self.max_failures = max_failures
|
23
|
+
self._failures_counter = 0
|
24
|
+
self.has_reached_the_failure_limit = False
|
17
25
|
|
18
26
|
@property
|
19
27
|
def is_stopped(self) -> bool:
|
schemathesis/engine/core.py
CHANGED
@@ -18,6 +18,8 @@ from .phases import Phase, PhaseName, PhaseSkipReason
|
|
18
18
|
class Engine:
|
19
19
|
schema: BaseSchema
|
20
20
|
|
21
|
+
__slots__ = ("schema",)
|
22
|
+
|
21
23
|
def execute(self) -> EventStream:
|
22
24
|
"""Execute all test phases."""
|
23
25
|
# Unregister auth if explicitly provided
|
@@ -103,6 +105,8 @@ class ExecutionPlan:
|
|
103
105
|
|
104
106
|
phases: Sequence[Phase]
|
105
107
|
|
108
|
+
__slots__ = ("phases",)
|
109
|
+
|
106
110
|
def execute(self, engine: EngineContext) -> EventGenerator:
|
107
111
|
"""Execute all phases in sequence."""
|
108
112
|
yield events.EngineStarted()
|
@@ -150,6 +154,8 @@ class EventStream:
|
|
150
154
|
generator: EventGenerator
|
151
155
|
stop_event: threading.Event
|
152
156
|
|
157
|
+
__slots__ = ("generator", "stop_event")
|
158
|
+
|
153
159
|
def __next__(self) -> events.EngineEvent:
|
154
160
|
return next(self.generator)
|
155
161
|
|
schemathesis/engine/errors.py
CHANGED
@@ -261,7 +261,7 @@ def get_runtime_error_suggestion(error_type: RuntimeErrorKind, bold: Callable[[s
|
|
261
261
|
RuntimeErrorKind.SCHEMA_INVALID_REGULAR_EXPRESSION: "Ensure your regex is compatible with Python's syntax.\n"
|
262
262
|
"For guidance, visit: https://docs.python.org/3/library/re.html",
|
263
263
|
RuntimeErrorKind.HYPOTHESIS_UNSUPPORTED_GRAPHQL_SCALAR: "Define a custom strategy for it.\n"
|
264
|
-
"For guidance, visit: https://schemathesis.readthedocs.io/en/
|
264
|
+
"For guidance, visit: https://schemathesis.readthedocs.io/en/stable/guides/graphql-custom-scalars/",
|
265
265
|
RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_DATA_TOO_LARGE: _format_health_check_suggestion("data_too_large"),
|
266
266
|
RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_FILTER_TOO_MUCH: _format_health_check_suggestion("filter_too_much"),
|
267
267
|
RuntimeErrorKind.HYPOTHESIS_HEALTH_CHECK_TOO_SLOW: _format_health_check_suggestion("too_slow"),
|
@@ -60,8 +60,18 @@ class Phase:
|
|
60
60
|
|
61
61
|
name: PhaseName
|
62
62
|
is_supported: bool
|
63
|
-
is_enabled: bool
|
64
|
-
skip_reason: PhaseSkipReason | None
|
63
|
+
is_enabled: bool
|
64
|
+
skip_reason: PhaseSkipReason | None
|
65
|
+
|
66
|
+
__slots__ = ("name", "is_supported", "is_enabled", "skip_reason")
|
67
|
+
|
68
|
+
def __init__(
|
69
|
+
self, name: PhaseName, is_supported: bool, is_enabled: bool = True, skip_reason: PhaseSkipReason | None = None
|
70
|
+
) -> None:
|
71
|
+
self.name = name
|
72
|
+
self.is_supported = is_supported
|
73
|
+
self.is_enabled = is_enabled
|
74
|
+
self.skip_reason = skip_reason
|
65
75
|
|
66
76
|
def should_execute(self, ctx: EngineContext) -> bool:
|
67
77
|
"""Determine if phase should run based on context & configuration."""
|
@@ -68,6 +68,8 @@ class Probe:
|
|
68
68
|
|
69
69
|
name: str
|
70
70
|
|
71
|
+
__slots__ = ("name",)
|
72
|
+
|
71
73
|
def prepare_request(
|
72
74
|
self, session: requests.Session, request: requests.Request, schema: BaseSchema
|
73
75
|
) -> requests.PreparedRequest:
|
@@ -92,9 +94,25 @@ class ProbeOutcome(str, enum.Enum):
|
|
92
94
|
class ProbeRun:
|
93
95
|
probe: Probe
|
94
96
|
outcome: ProbeOutcome
|
95
|
-
request: requests.PreparedRequest | None
|
96
|
-
response: requests.Response | None
|
97
|
-
error: Exception | None
|
97
|
+
request: requests.PreparedRequest | None
|
98
|
+
response: requests.Response | None
|
99
|
+
error: Exception | None
|
100
|
+
|
101
|
+
__slots__ = ("probe", "outcome", "request", "response", "error")
|
102
|
+
|
103
|
+
def __init__(
|
104
|
+
self,
|
105
|
+
probe: Probe,
|
106
|
+
outcome: ProbeOutcome,
|
107
|
+
request: requests.PreparedRequest | None = None,
|
108
|
+
response: requests.Response | None = None,
|
109
|
+
error: Exception | None = None,
|
110
|
+
) -> None:
|
111
|
+
self.probe = probe
|
112
|
+
self.outcome = outcome
|
113
|
+
self.request = request
|
114
|
+
self.response = response
|
115
|
+
self.error = error
|
98
116
|
|
99
117
|
@property
|
100
118
|
def is_failure(self) -> bool:
|
@@ -105,7 +123,10 @@ class ProbeRun:
|
|
105
123
|
class NullByteInHeader(Probe):
|
106
124
|
"""Support NULL bytes in headers."""
|
107
125
|
|
108
|
-
|
126
|
+
__slots__ = ("name",)
|
127
|
+
|
128
|
+
def __init__(self) -> None:
|
129
|
+
self.name = "Supports NULL byte in headers"
|
109
130
|
|
110
131
|
def prepare_request(
|
111
132
|
self, session: requests.Session, request: requests.Request, schema: BaseSchema
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
from dataclasses import dataclass
|
4
|
+
from dataclasses import dataclass
|
5
5
|
from enum import Enum
|
6
6
|
from functools import wraps
|
7
7
|
from itertools import combinations
|
@@ -53,11 +53,39 @@ class HypothesisTestMode(str, Enum):
|
|
53
53
|
class HypothesisTestConfig:
|
54
54
|
project: ProjectConfig
|
55
55
|
modes: list[HypothesisTestMode]
|
56
|
-
settings: hypothesis.settings | None
|
57
|
-
seed: int | None
|
58
|
-
as_strategy_kwargs: dict[str, Any]
|
59
|
-
given_args: tuple[GivenInput, ...]
|
60
|
-
given_kwargs: dict[str, GivenInput]
|
56
|
+
settings: hypothesis.settings | None
|
57
|
+
seed: int | None
|
58
|
+
as_strategy_kwargs: dict[str, Any]
|
59
|
+
given_args: tuple[GivenInput, ...]
|
60
|
+
given_kwargs: dict[str, GivenInput]
|
61
|
+
|
62
|
+
__slots__ = (
|
63
|
+
"project",
|
64
|
+
"modes",
|
65
|
+
"settings",
|
66
|
+
"seed",
|
67
|
+
"as_strategy_kwargs",
|
68
|
+
"given_args",
|
69
|
+
"given_kwargs",
|
70
|
+
)
|
71
|
+
|
72
|
+
def __init__(
|
73
|
+
self,
|
74
|
+
project: ProjectConfig,
|
75
|
+
modes: list[HypothesisTestMode],
|
76
|
+
settings: hypothesis.settings | None = None,
|
77
|
+
seed: int | None = None,
|
78
|
+
as_strategy_kwargs: dict[str, Any] | None = None,
|
79
|
+
given_args: tuple[GivenInput, ...] = (),
|
80
|
+
given_kwargs: dict[str, GivenInput] | None = None,
|
81
|
+
) -> None:
|
82
|
+
self.project = project
|
83
|
+
self.modes = modes
|
84
|
+
self.settings = settings
|
85
|
+
self.seed = seed
|
86
|
+
self.as_strategy_kwargs = as_strategy_kwargs or {}
|
87
|
+
self.given_args = given_args
|
88
|
+
self.given_kwargs = given_kwargs or {}
|
61
89
|
|
62
90
|
|
63
91
|
def create_test(
|
@@ -393,6 +421,7 @@ class Template:
|
|
393
421
|
class TemplateValue:
|
394
422
|
kwargs: dict[str, Any]
|
395
423
|
components: dict[ComponentKind, ComponentInfo]
|
424
|
+
|
396
425
|
__slots__ = ("kwargs", "components")
|
397
426
|
|
398
427
|
|
schemathesis/generation/meta.py
CHANGED
@@ -37,11 +37,15 @@ class ComponentInfo:
|
|
37
37
|
class GeneratePhaseData:
|
38
38
|
"""Metadata specific to generate phase."""
|
39
39
|
|
40
|
+
__slots__ = ()
|
41
|
+
|
40
42
|
|
41
43
|
@dataclass
|
42
44
|
class ExplicitPhaseData:
|
43
45
|
"""Metadata specific to explicit phase."""
|
44
46
|
|
47
|
+
__slots__ = ()
|
48
|
+
|
45
49
|
|
46
50
|
@dataclass
|
47
51
|
class CoveragePhaseData:
|
schemathesis/hooks.py
CHANGED
schemathesis/pytest/lazy.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from contextlib import nullcontext
|
4
|
-
from dataclasses import dataclass
|
4
|
+
from dataclasses import dataclass
|
5
5
|
from inspect import signature
|
6
6
|
from typing import TYPE_CHECKING, Any, Callable, Generator, Type
|
7
7
|
|
@@ -71,7 +71,17 @@ def get_all_tests(
|
|
71
71
|
@dataclass
|
72
72
|
class LazySchema:
|
73
73
|
fixture_name: str
|
74
|
-
filter_set: FilterSet
|
74
|
+
filter_set: FilterSet
|
75
|
+
|
76
|
+
__slots__ = ("fixture_name", "filter_set")
|
77
|
+
|
78
|
+
def __init__(
|
79
|
+
self,
|
80
|
+
fixture_name: str,
|
81
|
+
filter_set: FilterSet | None = None,
|
82
|
+
) -> None:
|
83
|
+
self.fixture_name = fixture_name
|
84
|
+
self.filter_set = filter_set or FilterSet()
|
75
85
|
|
76
86
|
def include(
|
77
87
|
self,
|
schemathesis/schemas.py
CHANGED
@@ -50,9 +50,6 @@ if TYPE_CHECKING:
|
|
50
50
|
from schemathesis.generation.stateful.state_machine import APIStateMachine
|
51
51
|
|
52
52
|
|
53
|
-
C = TypeVar("C", bound=Case)
|
54
|
-
|
55
|
-
|
56
53
|
@lru_cache
|
57
54
|
def get_full_path(base_path: str, path: str) -> str:
|
58
55
|
return unquote(urljoin(base_path, quote(path.lstrip("/"))))
|
@@ -483,6 +480,8 @@ class APIOperationMap(Mapping):
|
|
483
480
|
_schema: BaseSchema
|
484
481
|
_data: Mapping
|
485
482
|
|
483
|
+
__slots__ = ("_schema", "_data")
|
484
|
+
|
486
485
|
def __getitem__(self, item: str) -> APIOperation:
|
487
486
|
return self._data[item]
|
488
487
|
|
@@ -526,6 +525,8 @@ class Parameter:
|
|
526
525
|
# The parameter definition in the language acceptable by the API
|
527
526
|
definition: Any
|
528
527
|
|
528
|
+
__slots__ = ("definition",)
|
529
|
+
|
529
530
|
@property
|
530
531
|
def location(self) -> str:
|
531
532
|
"""Where this parameter is located.
|
@@ -556,7 +557,12 @@ P = TypeVar("P", bound=Parameter)
|
|
556
557
|
class ParameterSet(Generic[P]):
|
557
558
|
"""A set of parameters for the same location."""
|
558
559
|
|
559
|
-
items: list[P]
|
560
|
+
items: list[P]
|
561
|
+
|
562
|
+
__slots__ = ("items",)
|
563
|
+
|
564
|
+
def __init__(self, items: list[P] | None = None) -> None:
|
565
|
+
self.items = items or []
|
560
566
|
|
561
567
|
def _repr_pretty_(self, *args: Any, **kwargs: Any) -> None: ...
|
562
568
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from typing import TYPE_CHECKING
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
@@ -9,8 +9,18 @@ if TYPE_CHECKING:
|
|
9
9
|
|
10
10
|
@dataclass
|
11
11
|
class OperationCache:
|
12
|
-
_maps: dict[str, APIOperationMap]
|
13
|
-
_operations: dict[str, APIOperation]
|
12
|
+
_maps: dict[str, APIOperationMap]
|
13
|
+
_operations: dict[str, APIOperation]
|
14
|
+
|
15
|
+
__slots__ = ("_maps", "_operations")
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
_maps: dict[str, APIOperationMap] | None = None,
|
20
|
+
_operations: dict[str, APIOperation] | None = None,
|
21
|
+
) -> None:
|
22
|
+
self._maps = _maps or {}
|
23
|
+
self._operations = _operations or {}
|
14
24
|
|
15
25
|
def get_map(self, key: str) -> APIOperationMap | None:
|
16
26
|
return self._maps.get(key)
|
@@ -71,6 +71,8 @@ class GraphQLOperationDefinition(OperationDefinition):
|
|
71
71
|
type_: graphql.GraphQLType
|
72
72
|
root_type: RootType
|
73
73
|
|
74
|
+
__slots__ = ("raw", "resolved", "scope", "field_name", "type_", "root_type")
|
75
|
+
|
74
76
|
def _repr_pretty_(self, *args: Any, **kwargs: Any) -> None: ...
|
75
77
|
|
76
78
|
@property
|
@@ -35,6 +35,8 @@ class ParameterExample:
|
|
35
35
|
name: str
|
36
36
|
value: Any
|
37
37
|
|
38
|
+
__slots__ = ("container", "name", "value")
|
39
|
+
|
38
40
|
|
39
41
|
@dataclass
|
40
42
|
class BodyExample:
|
@@ -43,6 +45,8 @@ class BodyExample:
|
|
43
45
|
value: Any
|
44
46
|
media_type: str
|
45
47
|
|
48
|
+
__slots__ = ("value", "media_type")
|
49
|
+
|
46
50
|
|
47
51
|
Example = Union[ParameterExample, BodyExample]
|
48
52
|
|
@@ -39,6 +39,8 @@ class String(Node):
|
|
39
39
|
|
40
40
|
value: str
|
41
41
|
|
42
|
+
__slots__ = ("value",)
|
43
|
+
|
42
44
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
43
45
|
"""String tokens are passed as they are.
|
44
46
|
|
@@ -53,6 +55,8 @@ class String(Node):
|
|
53
55
|
class URL(Node):
|
54
56
|
"""A node for `$url` expression."""
|
55
57
|
|
58
|
+
__slots__ = ()
|
59
|
+
|
56
60
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
57
61
|
import requests
|
58
62
|
|
@@ -66,6 +70,8 @@ class URL(Node):
|
|
66
70
|
class Method(Node):
|
67
71
|
"""A node for `$method` expression."""
|
68
72
|
|
73
|
+
__slots__ = ()
|
74
|
+
|
69
75
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
70
76
|
return output.case.operation.method.upper()
|
71
77
|
|
@@ -74,6 +80,8 @@ class Method(Node):
|
|
74
80
|
class StatusCode(Node):
|
75
81
|
"""A node for `$statusCode` expression."""
|
76
82
|
|
83
|
+
__slots__ = ()
|
84
|
+
|
77
85
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
78
86
|
return str(output.response.status_code)
|
79
87
|
|
@@ -84,7 +92,14 @@ class NonBodyRequest(Node):
|
|
84
92
|
|
85
93
|
location: str
|
86
94
|
parameter: str
|
87
|
-
extractor: Extractor | None
|
95
|
+
extractor: Extractor | None
|
96
|
+
|
97
|
+
__slots__ = ("location", "parameter", "extractor")
|
98
|
+
|
99
|
+
def __init__(self, location: str, parameter: str, extractor: Extractor | None = None) -> None:
|
100
|
+
self.location = location
|
101
|
+
self.parameter = parameter
|
102
|
+
self.extractor = extractor
|
88
103
|
|
89
104
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
90
105
|
container = {
|
@@ -106,7 +121,12 @@ class NonBodyRequest(Node):
|
|
106
121
|
class BodyRequest(Node):
|
107
122
|
"""A node for `$request` expressions where location is `body`."""
|
108
123
|
|
109
|
-
pointer: str | None
|
124
|
+
pointer: str | None
|
125
|
+
|
126
|
+
__slots__ = ("pointer",)
|
127
|
+
|
128
|
+
def __init__(self, pointer: str | None = None) -> None:
|
129
|
+
self.pointer = pointer
|
110
130
|
|
111
131
|
def evaluate(self, output: StepOutput) -> Any | Unresolvable:
|
112
132
|
document = output.case.body
|
@@ -120,7 +140,13 @@ class HeaderResponse(Node):
|
|
120
140
|
"""A node for `$response.header` expressions."""
|
121
141
|
|
122
142
|
parameter: str
|
123
|
-
extractor: Extractor | None
|
143
|
+
extractor: Extractor | None
|
144
|
+
|
145
|
+
__slots__ = ("parameter", "extractor")
|
146
|
+
|
147
|
+
def __init__(self, parameter: str, extractor: Extractor | None = None) -> None:
|
148
|
+
self.parameter = parameter
|
149
|
+
self.extractor = extractor
|
124
150
|
|
125
151
|
def evaluate(self, output: StepOutput) -> str | Unresolvable:
|
126
152
|
value = output.response.headers.get(self.parameter.lower())
|
@@ -135,7 +161,12 @@ class HeaderResponse(Node):
|
|
135
161
|
class BodyResponse(Node):
|
136
162
|
"""A node for `$response.body` expressions."""
|
137
163
|
|
138
|
-
pointer: str | None
|
164
|
+
pointer: str | None
|
165
|
+
|
166
|
+
__slots__ = ("pointer",)
|
167
|
+
|
168
|
+
def __init__(self, pointer: str | None = None) -> None:
|
169
|
+
self.pointer = pointer
|
139
170
|
|
140
171
|
def evaluate(self, output: StepOutput) -> Any:
|
141
172
|
document = output.response.json()
|
@@ -74,6 +74,8 @@ class MutationContext:
|
|
74
74
|
# Payload media type, if available
|
75
75
|
media_type: str | None
|
76
76
|
|
77
|
+
__slots__ = ("keywords", "non_keywords", "location", "media_type")
|
78
|
+
|
77
79
|
@property
|
78
80
|
def is_header_location(self) -> bool:
|
79
81
|
return is_header_location(self.location)
|
@@ -34,8 +34,6 @@ def prepare_headers(case: Case, headers: dict[str, str] | None = None) -> CaseIn
|
|
34
34
|
default_headers.setdefault(SCHEMATHESIS_TEST_CASE_HEADER, case.id)
|
35
35
|
if headers:
|
36
36
|
default_headers.update(headers)
|
37
|
-
for header in get_exclude_headers(case):
|
38
|
-
default_headers.pop(header, None)
|
39
37
|
return default_headers
|
40
38
|
|
41
39
|
|
@@ -4,7 +4,7 @@ import binascii
|
|
4
4
|
import inspect
|
5
5
|
import os
|
6
6
|
from io import BytesIO
|
7
|
-
from typing import TYPE_CHECKING, Any
|
7
|
+
from typing import TYPE_CHECKING, Any, MutableMapping
|
8
8
|
from urllib.parse import urlparse
|
9
9
|
|
10
10
|
from schemathesis.core import NotSet
|
@@ -13,7 +13,7 @@ from schemathesis.core.transforms import deepclone, merge_at
|
|
13
13
|
from schemathesis.core.transport import DEFAULT_RESPONSE_TIMEOUT, Response
|
14
14
|
from schemathesis.generation.overrides import Override
|
15
15
|
from schemathesis.transport import BaseTransport, SerializationContext
|
16
|
-
from schemathesis.transport.prepare import prepare_body, prepare_headers, prepare_url
|
16
|
+
from schemathesis.transport.prepare import get_exclude_headers, prepare_body, prepare_headers, prepare_url
|
17
17
|
from schemathesis.transport.serialization import Binary, serialize_binary, serialize_json, serialize_xml, serialize_yaml
|
18
18
|
|
19
19
|
if TYPE_CHECKING:
|
@@ -85,18 +85,36 @@ class RequestsTransport(BaseTransport["requests.Session"]):
|
|
85
85
|
def send(self, case: Case, *, session: requests.Session | None = None, **kwargs: Any) -> Response:
|
86
86
|
import requests
|
87
87
|
|
88
|
+
if session is not None and session.headers:
|
89
|
+
# These headers are explicitly provided via config or CLI args.
|
90
|
+
# They have lower priority than ones provided via `kwargs`
|
91
|
+
headers = kwargs.setdefault("headers", {}) or {}
|
92
|
+
for name, value in session.headers.items():
|
93
|
+
headers.setdefault(name, value)
|
94
|
+
kwargs["headers"] = headers
|
95
|
+
|
88
96
|
data = self.serialize_case(case, **kwargs)
|
89
97
|
kwargs.pop("base_url", None)
|
90
98
|
data.update({key: value for key, value in kwargs.items() if key not in data})
|
91
99
|
data.setdefault("timeout", DEFAULT_RESPONSE_TIMEOUT)
|
92
100
|
|
101
|
+
excluded_headers = get_exclude_headers(case)
|
102
|
+
for name in excluded_headers:
|
103
|
+
data["headers"].pop(name, None)
|
104
|
+
current_session_headers: MutableMapping[str, Any] = {}
|
105
|
+
current_session_auth = None
|
106
|
+
|
93
107
|
if session is None:
|
94
108
|
validate_vanilla_requests_kwargs(data)
|
95
109
|
session = requests.Session()
|
96
|
-
session.headers = {}
|
97
110
|
close_session = True
|
98
111
|
else:
|
112
|
+
current_session_headers = session.headers
|
113
|
+
if isinstance(session.auth, tuple) and "Authorization" in excluded_headers:
|
114
|
+
current_session_auth = session.auth
|
115
|
+
session.auth = None
|
99
116
|
close_session = False
|
117
|
+
session.headers = {}
|
100
118
|
|
101
119
|
verify = data.get("verify", True)
|
102
120
|
|
@@ -115,8 +133,10 @@ class RequestsTransport(BaseTransport["requests.Session"]):
|
|
115
133
|
path_parameters={},
|
116
134
|
),
|
117
135
|
)
|
118
|
-
|
119
136
|
finally:
|
137
|
+
session.headers = current_session_headers
|
138
|
+
if current_session_auth is not None:
|
139
|
+
session.auth = current_session_auth
|
120
140
|
if close_session:
|
121
141
|
session.close()
|
122
142
|
|
schemathesis/transport/wsgi.py
CHANGED
@@ -12,7 +12,13 @@ from schemathesis.generation.case import Case
|
|
12
12
|
from schemathesis.generation.overrides import Override
|
13
13
|
from schemathesis.python import wsgi
|
14
14
|
from schemathesis.transport import BaseTransport, SerializationContext
|
15
|
-
from schemathesis.transport.prepare import
|
15
|
+
from schemathesis.transport.prepare import (
|
16
|
+
get_exclude_headers,
|
17
|
+
normalize_base_url,
|
18
|
+
prepare_body,
|
19
|
+
prepare_headers,
|
20
|
+
prepare_path,
|
21
|
+
)
|
16
22
|
from schemathesis.transport.requests import REQUESTS_TRANSPORT
|
17
23
|
from schemathesis.transport.serialization import serialize_binary, serialize_json, serialize_xml, serialize_yaml
|
18
24
|
|
@@ -73,6 +79,10 @@ class WSGITransport(BaseTransport["werkzeug.Client"]):
|
|
73
79
|
data = self.serialize_case(case, headers=headers, params=params)
|
74
80
|
data.update({key: value for key, value in kwargs.items() if key not in data})
|
75
81
|
|
82
|
+
excluded_headers = get_exclude_headers(case)
|
83
|
+
for name in excluded_headers:
|
84
|
+
data["headers"].pop(name, None)
|
85
|
+
|
76
86
|
client = session or wsgi.get_client(application)
|
77
87
|
cookies = {**(case.cookies or {}), **(cookies or {})}
|
78
88
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: schemathesis
|
3
|
-
Version: 4.0.
|
3
|
+
Version: 4.0.2
|
4
4
|
Summary: Property-based testing framework for Open API and GraphQL based apps
|
5
5
|
Project-URL: Documentation, https://schemathesis.readthedocs.io/en/stable/
|
6
|
-
Project-URL: Changelog, https://
|
6
|
+
Project-URL: Changelog, https://github.com/schemathesis/schemathesis/blob/master/CHANGELOG.md
|
7
7
|
Project-URL: Bug Tracker, https://github.com/schemathesis/schemathesis
|
8
8
|
Project-URL: Funding, https://github.com/sponsors/Stranger6667
|
9
9
|
Project-URL: Source Code, https://github.com/schemathesis/schemathesis
|
@@ -170,7 +170,7 @@ def test_api(case):
|
|
170
170
|
|
171
171
|
**CI/CD:**
|
172
172
|
```yaml
|
173
|
-
- uses: schemathesis/action@
|
173
|
+
- uses: schemathesis/action@v2
|
174
174
|
with:
|
175
175
|
schema: "https://your-api.com/openapi.json"
|
176
176
|
```
|
@@ -184,27 +184,21 @@ Used by teams at **[Spotify](https://github.com/backstage/backstage)**, **[WordP
|
|
184
184
|
|
185
185
|
## Documentation
|
186
186
|
|
187
|
-
📚 **[Complete documentation](https://schemathesis.readthedocs.io/en/
|
188
|
-
|
189
|
-
> **Note:** See also [docs](https://schemathesis.readthedocs.io/en/stable/) for the stable V3 release
|
187
|
+
📚 **[Complete documentation](https://schemathesis.readthedocs.io/en/stable/)** with guides, examples, and API reference.
|
190
188
|
|
191
189
|
## Get Help
|
192
190
|
|
193
191
|
- 💬 [Discord community](https://discord.gg/R9ASRAmHnA)
|
194
|
-
- 🐛 [GitHub issues](https://github.com/schemathesis/schemathesis/issues)
|
192
|
+
- 🐛 [GitHub issues](https://github.com/schemathesis/schemathesis/issues)
|
195
193
|
|
196
194
|
## Contributing
|
197
195
|
|
198
|
-
We welcome contributions! See our [contributing guidelines](CONTRIBUTING.
|
196
|
+
We welcome contributions! See our [contributing guidelines](CONTRIBUTING.md) and join discussions in [issues](https://github.com/schemathesis/schemathesis/issues) or [Discord](https://discord.gg/R9ASRAmHnA).
|
199
197
|
|
200
198
|
## Acknowledgements
|
201
199
|
|
202
200
|
Schemathesis is built on top of <a href="https://hypothesis.works/" target="_blank">Hypothesis</a>, a powerful property-based testing library for Python.
|
203
201
|
|
204
|
-
---
|
205
|
-
|
206
|
-
> **Note:** This is the V4 development branch. For the stable release, see [V3](https://github.com/schemathesis/schemathesis/tree/v3).
|
207
|
-
|
208
202
|
## License
|
209
203
|
|
210
204
|
This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
@@ -3,12 +3,12 @@ schemathesis/auths.py,sha256=JdEwPRS9WKmPcxzGXYYz9pjlIUMQYCfif7ZJU0Kde-I,16400
|
|
3
3
|
schemathesis/checks.py,sha256=GTdejjXDooAOuq66nvCK3i-AMPBuU-_-aNeSeL9JIlc,6561
|
4
4
|
schemathesis/errors.py,sha256=T8nobEi5tQX_SkwaYb8JFoIlF9F_vOQVprZ8EVPAhjA,931
|
5
5
|
schemathesis/filters.py,sha256=OEub50Ob5sf0Tn3iTeuIaxSMtepF7KVoiEM9wtt5GGA,13433
|
6
|
-
schemathesis/hooks.py,sha256=
|
6
|
+
schemathesis/hooks.py,sha256=L1o9IQz0_mY59gs3WGHGSCnGHjF6WWEZZuwJaHcVG1o,13922
|
7
7
|
schemathesis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
schemathesis/schemas.py,sha256=
|
8
|
+
schemathesis/schemas.py,sha256=glaIjN7JO1mdOCs5_HUFjLWQ-89z8NFBUIuc5p8cLAU,28386
|
9
9
|
schemathesis/cli/__init__.py,sha256=U9gjzWWpiFhaqevPjZbwyTNjABdpvXETI4HgwdGKnvs,877
|
10
10
|
schemathesis/cli/__main__.py,sha256=MWaenjaUTZIfNPFzKmnkTiawUri7DVldtg3mirLwzU8,92
|
11
|
-
schemathesis/cli/constants.py,sha256=
|
11
|
+
schemathesis/cli/constants.py,sha256=CVcQNHEiX-joAQmyuEVKWPOSxDHsOw_EXXZsEclzLuY,341
|
12
12
|
schemathesis/cli/core.py,sha256=ue7YUdVo3YvuzGL4s6i62NL6YqNDeVPBSnQ1znrvG2w,480
|
13
13
|
schemathesis/cli/commands/__init__.py,sha256=rubTCCRGuMIbNYOl8yQEioiuHtTq__tWjkUtFWYGhqQ,3433
|
14
14
|
schemathesis/cli/commands/data.py,sha256=_ALywjIeCZjuaoDQFy-Kj8RZkEGqXd-Y95O47h8Jszs,171
|
@@ -21,9 +21,9 @@ schemathesis/cli/commands/run/loaders.py,sha256=6j0ez7wduAUYbUT28ELKxMf-dYEWr_67
|
|
21
21
|
schemathesis/cli/commands/run/validation.py,sha256=FzCzYdW1-hn3OgyzPO1p6wHEX5PG7HdewbPRvclV_vc,9002
|
22
22
|
schemathesis/cli/commands/run/handlers/__init__.py,sha256=TPZ3KdGi8m0fjlN0GjA31MAXXn1qI7uU4FtiDwroXZI,1915
|
23
23
|
schemathesis/cli/commands/run/handlers/base.py,sha256=yDsTtCiztLksfk7cRzg8JlaAVOfS-zwK3tsJMOXAFyc,530
|
24
|
-
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=
|
25
|
-
schemathesis/cli/commands/run/handlers/junitxml.py,sha256=
|
26
|
-
schemathesis/cli/commands/run/handlers/output.py,sha256=
|
24
|
+
schemathesis/cli/commands/run/handlers/cassettes.py,sha256=rRD4byjp4HXCkJS-zx3jSIFOJsPq77ejPpYeyCtsEZs,19461
|
25
|
+
schemathesis/cli/commands/run/handlers/junitxml.py,sha256=Q5GxGjsPq-oKVgq5P19nv0rsi-ZWT5ghHWh-WXgpQ5k,2550
|
26
|
+
schemathesis/cli/commands/run/handlers/output.py,sha256=4sffi2lj3QcDt6JKJCsIT9wCesj1ZdHfTaP6-8dTwh8,62884
|
27
27
|
schemathesis/cli/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
28
|
schemathesis/cli/ext/fs.py,sha256=3lvoAsEDDdih75ITJJNxemd3nwxX55gGWrI7uDxm0cM,447
|
29
29
|
schemathesis/cli/ext/groups.py,sha256=kQ37t6qeArcKaY2y5VxyK3_KwAkBKCVm58IYV8gewds,2720
|
@@ -36,7 +36,7 @@ schemathesis/config/_env.py,sha256=8XfIyrnGNQuCDnfG0lwmKRFbasRUjgeQGBAMupsmtOU,6
|
|
36
36
|
schemathesis/config/_error.py,sha256=TxuuqQ1olwJc7P7ssfxXb1dB_Xn5uVsoazjvYvRxrxA,5437
|
37
37
|
schemathesis/config/_generation.py,sha256=_THqCfC20i8RRRsO2hAwoJ52FV-CS1xOA6me3Wp3FHw,5087
|
38
38
|
schemathesis/config/_health_check.py,sha256=zC9inla5ibMBlEy5WyM4_TME7ju_KH3Bwfo21RI3Gks,561
|
39
|
-
schemathesis/config/_operations.py,sha256=
|
39
|
+
schemathesis/config/_operations.py,sha256=EWUFxICG7kzr-yqBeZyZmpwCEMgDkgD7lUXrO_oCLwo,11860
|
40
40
|
schemathesis/config/_output.py,sha256=3G9SOi-4oNcQPHeNRG3HggFCwvcKOW1kF28a9m0H-pU,4434
|
41
41
|
schemathesis/config/_parameters.py,sha256=i76Hwaf834fBAMmtKfKTl1SFCicJ-Y-5tZt5QNGW2fA,618
|
42
42
|
schemathesis/config/_phases.py,sha256=NFUhn-xzEQdNtgNVW1t51lMquXbjRNGR_QuiCRLCi28,6454
|
@@ -46,12 +46,12 @@ schemathesis/config/_report.py,sha256=aYLnPO74B7Wfn_qTwlEp5zY9L74U1XFuYS10yjwKKW
|
|
46
46
|
schemathesis/config/_validator.py,sha256=IcE8geFZ0ZwR18rkIRs25i7pTl7Z84XbjYGUB-mqReU,258
|
47
47
|
schemathesis/config/_warnings.py,sha256=sI0VZcTj3dOnphhBwYwU_KTagxr89HGWTtQ99HcY84k,772
|
48
48
|
schemathesis/config/schema.json,sha256=wC1qe9M_fXotfmlBOmW_FCTRw9K5YC814-PipMGKllE,18907
|
49
|
-
schemathesis/core/__init__.py,sha256=
|
49
|
+
schemathesis/core/__init__.py,sha256=j862XBH5dXhxsrDg9mE7n4cSSfol0EHdY0ru1d27tCc,1917
|
50
50
|
schemathesis/core/compat.py,sha256=9BWCrFoqN2sJIaiht_anxe8kLjYMR7t0iiOkXqLRUZ8,1058
|
51
51
|
schemathesis/core/control.py,sha256=IzwIc8HIAEMtZWW0Q0iXI7T1niBpjvcLlbuwOSmy5O8,130
|
52
52
|
schemathesis/core/curl.py,sha256=yuaCe_zHLGwUjEeloQi6W3tOA3cGdnHDNI17-5jia0o,1723
|
53
53
|
schemathesis/core/deserialization.py,sha256=ygIj4fNaOd0mJ2IvTsn6bsabBt_2AbSLCz-z9UqfpdQ,2406
|
54
|
-
schemathesis/core/errors.py,sha256=
|
54
|
+
schemathesis/core/errors.py,sha256=KuFLy5ZOGn8KlD4ai5HK_WFlB3fJ2rSKwV1yD4fn4BU,16295
|
55
55
|
schemathesis/core/failures.py,sha256=MYyRnom-XeUEuBmq2ffdz34xhxmpSHWaQunfHtliVsY,8932
|
56
56
|
schemathesis/core/fs.py,sha256=ItQT0_cVwjDdJX9IiI7EnU75NI2H3_DCEyyUjzg_BgI,472
|
57
57
|
schemathesis/core/hooks.py,sha256=qhbkkRSf8URJ4LKv2wmKRINKpquUOgxQzWBHKWRWo3Q,475
|
@@ -70,13 +70,13 @@ schemathesis/core/output/__init__.py,sha256=SiHqONFskXl73AtP5dV29L14nZoKo7B-IeG5
|
|
70
70
|
schemathesis/core/output/sanitization.py,sha256=Ev3tae8dVwsYd7yVb2_1VBFYs92WFsQ4Eu1fGaymItE,2013
|
71
71
|
schemathesis/engine/__init__.py,sha256=QaFE-FinaTAaarteADo2RRMJ-Sz6hZB9TzD5KjMinIA,706
|
72
72
|
schemathesis/engine/context.py,sha256=x-I9KX6rO6hdCvvN8FEdzIZBqIcNaxdNYHgQjcXbZhM,3931
|
73
|
-
schemathesis/engine/control.py,sha256=
|
74
|
-
schemathesis/engine/core.py,sha256=
|
75
|
-
schemathesis/engine/errors.py,sha256=
|
73
|
+
schemathesis/engine/control.py,sha256=FXzP8dxL47j1Giqpy2-Bsr_MdMw9YiATSK_UfpFwDtk,1348
|
74
|
+
schemathesis/engine/core.py,sha256=5jfAqFH0XSD7NVgoSXuUPW-dooItscneAzUNq1RBh1E,5712
|
75
|
+
schemathesis/engine/errors.py,sha256=cWKuwj0Kzr2BHdVCHACnniUJ8sFVJ0Nqckc3iggZS1o,18800
|
76
76
|
schemathesis/engine/events.py,sha256=VV6epicFIJnX4c87fVNSd0ibDccX3gryDv52OUGa3FI,6370
|
77
77
|
schemathesis/engine/recorder.py,sha256=K3HfMARrT5mPWXPnYebjjcq5CcsBRhMrtZwEL9_Lvtg,8432
|
78
|
-
schemathesis/engine/phases/__init__.py,sha256=
|
79
|
-
schemathesis/engine/phases/probes.py,sha256
|
78
|
+
schemathesis/engine/phases/__init__.py,sha256=jUIfb_9QoUo4zmJEVU0z70PgXPYjt8CIqp4qP_HlYHg,3146
|
79
|
+
schemathesis/engine/phases/probes.py,sha256=SEtWKPdkLfRTKV0_tbiNHTK3sJsUUPZ0jZQ9Nv4qUi8,5678
|
80
80
|
schemathesis/engine/phases/stateful/__init__.py,sha256=Lz1rgNqCfUSIz173XqCGsiMuUI5bh4L-RIFexU1-c_Q,2461
|
81
81
|
schemathesis/engine/phases/stateful/_executor.py,sha256=CV4jUuXpV4uSXVJqDI4btnLR8dpzOQVqbv2qVCgE4_s,15182
|
82
82
|
schemathesis/engine/phases/stateful/context.py,sha256=A7X1SLDOWFpCvFN9IiIeNVZM0emjqatmJL_k9UsO7vM,2946
|
@@ -86,12 +86,12 @@ schemathesis/engine/phases/unit/_pool.py,sha256=iU0hdHDmohPnEv7_S1emcabuzbTf-Czn
|
|
86
86
|
schemathesis/generation/__init__.py,sha256=tvNO2FLiY8z3fZ_kL_QJhSgzXfnT4UqwSXMHCwfLI0g,645
|
87
87
|
schemathesis/generation/case.py,sha256=WbOJagE7Gjz3ZvBxzRl8vJHgm_LjW0wf2oRuPzoj6LI,11547
|
88
88
|
schemathesis/generation/coverage.py,sha256=bKP0idU5-eiK4VwhH4kjxDPtCZzMg81mbN1tEDuT6EA,47913
|
89
|
-
schemathesis/generation/meta.py,sha256=
|
89
|
+
schemathesis/generation/meta.py,sha256=adkoMuCfzSjHJ9ZDocQn0GnVldSCkLL3eVR5A_jafwM,2552
|
90
90
|
schemathesis/generation/metrics.py,sha256=cZU5HdeAMcLFEDnTbNE56NuNq4P0N4ew-g1NEz5-kt4,2836
|
91
91
|
schemathesis/generation/modes.py,sha256=Q1fhjWr3zxabU5qdtLvKfpMFZJAwlW9pnxgenjeXTyU,481
|
92
|
-
schemathesis/generation/overrides.py,sha256=
|
92
|
+
schemathesis/generation/overrides.py,sha256=OBWqDQPreiliaf2M-oyXppVKHoJkCRzxtwSJx1b6AFw,3759
|
93
93
|
schemathesis/generation/hypothesis/__init__.py,sha256=SVwM-rx07jPZzms0idWYACgUtWAxh49HRuTnaQ__zf0,1549
|
94
|
-
schemathesis/generation/hypothesis/builder.py,sha256=
|
94
|
+
schemathesis/generation/hypothesis/builder.py,sha256=xc5U3LfJ1lQHKFFW_9W8n3KFutwA2mv954A4jLhB9vg,33105
|
95
95
|
schemathesis/generation/hypothesis/examples.py,sha256=6eGaKUEC3elmKsaqfKj1sLvM8EHc-PWT4NRBq4NI0Rs,1409
|
96
96
|
schemathesis/generation/hypothesis/given.py,sha256=sTZR1of6XaHAPWtHx2_WLlZ50M8D5Rjux0GmWkWjDq4,2337
|
97
97
|
schemathesis/generation/hypothesis/reporting.py,sha256=uDVow6Ya8YFkqQuOqRsjbzsbyP4KKfr3jA7ZaY4FuKY,279
|
@@ -108,7 +108,7 @@ schemathesis/openapi/generation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
108
108
|
schemathesis/openapi/generation/filters.py,sha256=pY9cUZdL_kQK80Z2aylTOqqa12zmaYUlYC5BfYgeQMk,2395
|
109
109
|
schemathesis/pytest/__init__.py,sha256=7W0q-Thcw03IAQfXE_Mo8JPZpUdHJzfu85fjK1ZdfQM,88
|
110
110
|
schemathesis/pytest/control_flow.py,sha256=F8rAPsPeNv_sJiJgbZYtTpwKWjauZmqFUaKroY2GmQI,217
|
111
|
-
schemathesis/pytest/lazy.py,sha256=
|
111
|
+
schemathesis/pytest/lazy.py,sha256=u58q0orI0zisivLJKJkSo53RaQMPLSMiE0vJ1TQ9_uA,11073
|
112
112
|
schemathesis/pytest/loaders.py,sha256=Sbv8e5F77_x4amLP50iwubfm6kpOhx7LhLFGsVXW5Ys,925
|
113
113
|
schemathesis/pytest/plugin.py,sha256=m4zGLw6A537o4mBb9FvuM4jmAoxfpg0DPLWq5eCLXGc,13818
|
114
114
|
schemathesis/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -116,10 +116,10 @@ schemathesis/python/asgi.py,sha256=5PyvuTBaivvyPUEi3pwJni91K1kX5Zc0u9c6c1D8a1Q,2
|
|
116
116
|
schemathesis/python/wsgi.py,sha256=uShAgo_NChbfYaV1117e6UHp0MTg7jaR0Sy_to3Jmf8,219
|
117
117
|
schemathesis/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
118
118
|
schemathesis/specs/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
|
-
schemathesis/specs/graphql/_cache.py,sha256=
|
119
|
+
schemathesis/specs/graphql/_cache.py,sha256=mlOtzEvdE9gZ1posP7OqHUYlaZyiygL5U_Xda7wphFc,988
|
120
120
|
schemathesis/specs/graphql/nodes.py,sha256=bE3G1kNmqJ8OV4igBvIK-UORrkQA6Nofduf87O3TD9I,541
|
121
121
|
schemathesis/specs/graphql/scalars.py,sha256=6lew8mnwhrtg23leiEbG43mLGPLlRln8mClCY94XpDA,2680
|
122
|
-
schemathesis/specs/graphql/schemas.py,sha256=
|
122
|
+
schemathesis/specs/graphql/schemas.py,sha256=ezkqgMwx37tMWlhy_I0ahDF1Q44emDSJkyjduq1QGjE,14624
|
123
123
|
schemathesis/specs/graphql/validation.py,sha256=-W1Noc1MQmTb4RX-gNXMeU2qkgso4mzVfHxtdLkCPKM,1422
|
124
124
|
schemathesis/specs/openapi/__init__.py,sha256=C5HOsfuDJGq_3mv8CRBvRvb0Diy1p0BFdqyEXMS-loE,238
|
125
125
|
schemathesis/specs/openapi/_cache.py,sha256=HpglmETmZU0RCHxp3DO_sg5_B_nzi54Zuw9vGzzYCxY,4295
|
@@ -128,7 +128,7 @@ schemathesis/specs/openapi/checks.py,sha256=mKJ-ZkbjhbXS4eWDZiv8zslXKFDqkE3Mp4N8
|
|
128
128
|
schemathesis/specs/openapi/constants.py,sha256=JqM_FHOenqS_MuUE9sxVQ8Hnw0DNM8cnKDwCwPLhID4,783
|
129
129
|
schemathesis/specs/openapi/converter.py,sha256=lil8IewM5j8tvt4lpA9g_KITvIwx1M96i45DNSHNjoc,3505
|
130
130
|
schemathesis/specs/openapi/definitions.py,sha256=8htclglV3fW6JPBqs59lgM4LnA25Mm9IptXBPb_qUT0,93949
|
131
|
-
schemathesis/specs/openapi/examples.py,sha256=
|
131
|
+
schemathesis/specs/openapi/examples.py,sha256=11GMuwyTws8MizQ3CkfT3dQD4TFP22CGYBMUyESYsDM,21218
|
132
132
|
schemathesis/specs/openapi/formats.py,sha256=8AIS7Uey-Z1wm1WYRqnsVqHwG9d316PdqfKLqwUs7us,3516
|
133
133
|
schemathesis/specs/openapi/media_types.py,sha256=F5M6TKl0s6Z5X8mZpPsWDEdPBvxclKRcUOc41eEwKbo,2472
|
134
134
|
schemathesis/specs/openapi/parameters.py,sha256=BevME4DWLQ-OFvc_7fREMjj99VAbVNxVb5i8OEX6Pfs,14453
|
@@ -140,25 +140,25 @@ schemathesis/specs/openapi/serialization.py,sha256=VdDLmeHqxlWM4cxQQcCkvrU6Xuriv
|
|
140
140
|
schemathesis/specs/openapi/utils.py,sha256=ER4vJkdFVDIE7aKyxyYatuuHVRNutytezgE52pqZNE8,900
|
141
141
|
schemathesis/specs/openapi/expressions/__init__.py,sha256=hfuRtXD75tQFhzSo6QgDZ3zByyWeZRKevB8edszAVj4,2272
|
142
142
|
schemathesis/specs/openapi/expressions/errors.py,sha256=YLVhps-sYcslgVaahfcUYxUSHlIfWL-rQMeT5PZSMZ8,219
|
143
|
-
schemathesis/specs/openapi/expressions/extractors.py,sha256=
|
144
|
-
schemathesis/specs/openapi/expressions/lexer.py,sha256=
|
145
|
-
schemathesis/specs/openapi/expressions/nodes.py,sha256=
|
143
|
+
schemathesis/specs/openapi/expressions/extractors.py,sha256=IvOrgq_1IWNnirOSV_wLi0UcWOTiL-mLvBLFzLwRpVA,498
|
144
|
+
schemathesis/specs/openapi/expressions/lexer.py,sha256=ZbYPbVX-2c2Dan-6fi4NrDlFT6N55Wrz-4921TKHlZs,4030
|
145
|
+
schemathesis/specs/openapi/expressions/nodes.py,sha256=qaFpAM3seIzmlYLr9So2kRCSNrteZTa7djcRiOD_ji4,4811
|
146
146
|
schemathesis/specs/openapi/expressions/parser.py,sha256=e-ZxshrGE_5CVbgcZLYgdGSjdifgyzgKkLQp0dI0cJY,4503
|
147
|
-
schemathesis/specs/openapi/negative/__init__.py,sha256=
|
148
|
-
schemathesis/specs/openapi/negative/mutations.py,sha256=
|
147
|
+
schemathesis/specs/openapi/negative/__init__.py,sha256=1sajF22SrE4pUK7-C6PiseZ9PiR5trN33cfUqEMGIbo,3915
|
148
|
+
schemathesis/specs/openapi/negative/mutations.py,sha256=ZLiNb4n2K1Oeq3eev8tgNqvlyP7cBIPUTLe7Gc6nvDM,19318
|
149
149
|
schemathesis/specs/openapi/negative/types.py,sha256=a7buCcVxNBG6ILBM3A7oNTAX0lyDseEtZndBuej8MbI,174
|
150
150
|
schemathesis/specs/openapi/negative/utils.py,sha256=ozcOIuASufLqZSgnKUACjX-EOZrrkuNdXX0SDnLoGYA,168
|
151
151
|
schemathesis/specs/openapi/stateful/__init__.py,sha256=FSitLbJxjBYU814cqjI_QCkdyoIc-9xfT_1HQcYwsXw,15064
|
152
152
|
schemathesis/specs/openapi/stateful/control.py,sha256=QaXLSbwQWtai5lxvvVtQV3BLJ8n5ePqSKB00XFxp-MA,3695
|
153
|
-
schemathesis/specs/openapi/stateful/links.py,sha256=
|
153
|
+
schemathesis/specs/openapi/stateful/links.py,sha256=h5q40jUbcIk5DS_Tih1cvFJxS_QxxG0_9ZQnTs1A_zo,8806
|
154
154
|
schemathesis/transport/__init__.py,sha256=6yg_RfV_9L0cpA6qpbH-SL9_3ggtHQji9CZrpIkbA6s,5321
|
155
155
|
schemathesis/transport/asgi.py,sha256=qTClt6oT_xUEWnRHokACN_uqCNNUZrRPT6YG0PjbElY,926
|
156
|
-
schemathesis/transport/prepare.py,sha256=
|
157
|
-
schemathesis/transport/requests.py,sha256=
|
156
|
+
schemathesis/transport/prepare.py,sha256=iiB8KTAqnnuqjWzblIPiGVdkGIF7Yr1SAEz-KZzBNXw,4581
|
157
|
+
schemathesis/transport/requests.py,sha256=7N0KYcCEVba7fpeL8OphoY0W8B-qNOARrgYC51cg3AE,10227
|
158
158
|
schemathesis/transport/serialization.py,sha256=igUXKZ_VJ9gV7P0TUc5PDQBJXl_s0kK9T3ljGWWvo6E,10339
|
159
|
-
schemathesis/transport/wsgi.py,sha256=
|
160
|
-
schemathesis-4.0.
|
161
|
-
schemathesis-4.0.
|
162
|
-
schemathesis-4.0.
|
163
|
-
schemathesis-4.0.
|
164
|
-
schemathesis-4.0.
|
159
|
+
schemathesis/transport/wsgi.py,sha256=KoAfvu6RJtzyj24VGB8e-Iaa9smpgXJ3VsM8EgAz2tc,6152
|
160
|
+
schemathesis-4.0.2.dist-info/METADATA,sha256=wqlw5M4fnuJgE7zRWt8qKu6TiP1wsPJtMiRLCMfluu8,8471
|
161
|
+
schemathesis-4.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
162
|
+
schemathesis-4.0.2.dist-info/entry_points.txt,sha256=hiK3un-xfgPdwj9uj16YVDtTNpO128bmk0U82SMv8ZQ,152
|
163
|
+
schemathesis-4.0.2.dist-info/licenses/LICENSE,sha256=2Ve4J8v5jMQAWrT7r1nf3bI8Vflk3rZVQefiF2zpxwg,1121
|
164
|
+
schemathesis-4.0.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|