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
schemathesis/_override.py
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING
|
5
|
-
|
6
|
-
from .exceptions import UsageError
|
7
|
-
|
8
|
-
if TYPE_CHECKING:
|
9
|
-
from .models import APIOperation
|
10
|
-
from .parameters import ParameterSet
|
11
|
-
from .types import GenericTest
|
12
|
-
|
13
|
-
|
14
|
-
@dataclass
|
15
|
-
class CaseOverride:
|
16
|
-
"""Overrides for various parts of a test case."""
|
17
|
-
|
18
|
-
query: dict[str, str]
|
19
|
-
headers: dict[str, str]
|
20
|
-
cookies: dict[str, str]
|
21
|
-
path_parameters: dict[str, str]
|
22
|
-
|
23
|
-
def for_operation(self, operation: APIOperation) -> dict[str, dict[str, str]]:
|
24
|
-
return {
|
25
|
-
"query": (_for_parameters(self.query, operation.query)),
|
26
|
-
"headers": (_for_parameters(self.headers, operation.headers)),
|
27
|
-
"cookies": (_for_parameters(self.cookies, operation.cookies)),
|
28
|
-
"path_parameters": (_for_parameters(self.path_parameters, operation.path_parameters)),
|
29
|
-
}
|
30
|
-
|
31
|
-
|
32
|
-
def _for_parameters(overridden: dict[str, str], defined: ParameterSet) -> dict[str, str]:
|
33
|
-
output = {}
|
34
|
-
for param in defined:
|
35
|
-
if param.name in overridden:
|
36
|
-
output[param.name] = overridden[param.name]
|
37
|
-
return output
|
38
|
-
|
39
|
-
|
40
|
-
def get_override_from_mark(test: GenericTest) -> CaseOverride | None:
|
41
|
-
return getattr(test, "_schemathesis_override", None)
|
42
|
-
|
43
|
-
|
44
|
-
def set_override_mark(test: GenericTest, override: CaseOverride) -> None:
|
45
|
-
test._schemathesis_override = override # type: ignore[attr-defined]
|
46
|
-
|
47
|
-
|
48
|
-
def check_no_override_mark(test: GenericTest) -> None:
|
49
|
-
if hasattr(test, "_schemathesis_override"):
|
50
|
-
raise UsageError(f"`{test.__name__}` has already been decorated with `override`.")
|
schemathesis/_patches.py
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
"""A set of performance-related patches."""
|
2
|
-
|
3
|
-
from typing import Any
|
4
|
-
|
5
|
-
|
6
|
-
def install() -> None:
|
7
|
-
from hypothesis.internal.reflection import is_first_param_referenced_in_function
|
8
|
-
from hypothesis.strategies._internal import core
|
9
|
-
from hypothesis_jsonschema import _from_schema, _resolve
|
10
|
-
|
11
|
-
from .internal.copy import fast_deepcopy
|
12
|
-
|
13
|
-
# This one is used a lot, and under the hood it re-parses the AST of the same function
|
14
|
-
def _is_first_param_referenced_in_function(f: Any) -> bool:
|
15
|
-
if f.__name__ == "from_object_schema" and f.__module__ == "hypothesis_jsonschema._from_schema":
|
16
|
-
return True
|
17
|
-
return is_first_param_referenced_in_function(f)
|
18
|
-
|
19
|
-
core.is_first_param_referenced_in_function = _is_first_param_referenced_in_function # type: ignore
|
20
|
-
_resolve.deepcopy = fast_deepcopy # type: ignore
|
21
|
-
_from_schema.deepcopy = fast_deepcopy # type: ignore
|
schemathesis/_rate_limiter.py
DELETED
schemathesis/cli/callbacks.py
DELETED
@@ -1,466 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import codecs
|
4
|
-
import enum
|
5
|
-
import operator
|
6
|
-
import os
|
7
|
-
import re
|
8
|
-
import traceback
|
9
|
-
from contextlib import contextmanager
|
10
|
-
from functools import partial, reduce
|
11
|
-
from typing import TYPE_CHECKING, Callable, Generator
|
12
|
-
from urllib.parse import urlparse
|
13
|
-
|
14
|
-
import click
|
15
|
-
|
16
|
-
from .. import exceptions, experimental, throttling
|
17
|
-
from ..code_samples import CodeSampleStyle
|
18
|
-
from ..constants import TRUE_VALUES
|
19
|
-
from ..exceptions import extract_nth_traceback
|
20
|
-
from ..generation import DataGenerationMethod
|
21
|
-
from ..internal.transformation import convert_boolean_string as _convert_boolean_string
|
22
|
-
from ..internal.validation import file_exists, is_filename, is_illegal_surrogate
|
23
|
-
from ..loaders import load_app
|
24
|
-
from ..service.hosts import get_temporary_hosts_file
|
25
|
-
from ..stateful import Stateful
|
26
|
-
from ..transports.headers import has_invalid_characters, is_latin_1_encodable
|
27
|
-
from .cassettes import CassetteFormat
|
28
|
-
from .constants import DEFAULT_WORKERS
|
29
|
-
|
30
|
-
if TYPE_CHECKING:
|
31
|
-
import hypothesis
|
32
|
-
from click.types import LazyFile # type: ignore[attr-defined]
|
33
|
-
|
34
|
-
from ..types import PathLike
|
35
|
-
|
36
|
-
INVALID_DERANDOMIZE_MESSAGE = (
|
37
|
-
"`--hypothesis-derandomize` implies no database, so passing `--hypothesis-database` too is invalid."
|
38
|
-
)
|
39
|
-
MISSING_CASSETTE_PATH_ARGUMENT_MESSAGE = (
|
40
|
-
"Missing argument, `--cassette-path` should be specified as well if you use `--cassette-preserve-exact-body-bytes`."
|
41
|
-
)
|
42
|
-
INVALID_SCHEMA_MESSAGE = "Invalid SCHEMA, must be a valid URL, file path or an API name from Schemathesis.io."
|
43
|
-
FILE_DOES_NOT_EXIST_MESSAGE = "The specified file does not exist. Please provide a valid path to an existing file."
|
44
|
-
INVALID_BASE_URL_MESSAGE = (
|
45
|
-
"The provided base URL is invalid. This URL serves as a prefix for all API endpoints you want to test. "
|
46
|
-
"Make sure it is a properly formatted URL."
|
47
|
-
)
|
48
|
-
MISSING_BASE_URL_MESSAGE = "The `--base-url` option is required when specifying a schema via a file."
|
49
|
-
WSGI_DOCUMENTATION_URL = "https://schemathesis.readthedocs.io/en/stable/python.html#asgi-wsgi-support"
|
50
|
-
APPLICATION_MISSING_MODULE_MESSAGE = f"""Unable to import application from {{module}}.
|
51
|
-
|
52
|
-
The `--app` option should follow this format:
|
53
|
-
|
54
|
-
module_path:variable_name
|
55
|
-
|
56
|
-
- `module_path`: A path to an importable Python module.
|
57
|
-
- `variable_name`: The name of the application variable within that module.
|
58
|
-
|
59
|
-
Example: `st run --app=your_module:app ...`
|
60
|
-
|
61
|
-
For details on working with WSGI applications, visit {WSGI_DOCUMENTATION_URL}"""
|
62
|
-
APPLICATION_IMPORT_ERROR_MESSAGE = f"""An error occurred while loading the application from {{module}}.
|
63
|
-
|
64
|
-
Traceback:
|
65
|
-
|
66
|
-
{{traceback}}
|
67
|
-
|
68
|
-
For details on working with WSGI applications, visit {WSGI_DOCUMENTATION_URL}"""
|
69
|
-
MISSING_REQUEST_CERT_MESSAGE = "The `--request-cert` option must be specified if `--request-cert-key` is used."
|
70
|
-
|
71
|
-
|
72
|
-
@enum.unique
|
73
|
-
class SchemaInputKind(enum.Enum):
|
74
|
-
"""Kinds of SCHEMA input."""
|
75
|
-
|
76
|
-
# Regular URL like https://example.schemathesis.io/openapi.json
|
77
|
-
URL = 1
|
78
|
-
# Local path
|
79
|
-
PATH = 2
|
80
|
-
# Relative path within a Python app
|
81
|
-
APP_PATH = 3
|
82
|
-
# A name for API created in Schemathesis.io
|
83
|
-
NAME = 4
|
84
|
-
|
85
|
-
|
86
|
-
def parse_schema_kind(schema: str, app: str | None) -> SchemaInputKind:
|
87
|
-
"""Detect what kind the input schema is."""
|
88
|
-
try:
|
89
|
-
netloc = urlparse(schema).netloc
|
90
|
-
except ValueError as exc:
|
91
|
-
raise click.UsageError(INVALID_SCHEMA_MESSAGE) from exc
|
92
|
-
if "\x00" in schema or not schema:
|
93
|
-
raise click.UsageError(INVALID_SCHEMA_MESSAGE)
|
94
|
-
if netloc:
|
95
|
-
return SchemaInputKind.URL
|
96
|
-
if file_exists(schema) or is_filename(schema):
|
97
|
-
return SchemaInputKind.PATH
|
98
|
-
if app is not None:
|
99
|
-
return SchemaInputKind.APP_PATH
|
100
|
-
# Assume NAME if it is not a URL or PATH or APP_PATH
|
101
|
-
return SchemaInputKind.NAME
|
102
|
-
|
103
|
-
|
104
|
-
def validate_schema(
|
105
|
-
schema: str,
|
106
|
-
kind: SchemaInputKind,
|
107
|
-
*,
|
108
|
-
base_url: str | None,
|
109
|
-
dry_run: bool,
|
110
|
-
app: str | None,
|
111
|
-
api_name: str | None,
|
112
|
-
) -> None:
|
113
|
-
if kind == SchemaInputKind.URL:
|
114
|
-
validate_url(schema)
|
115
|
-
if kind == SchemaInputKind.PATH:
|
116
|
-
if app is None:
|
117
|
-
if not file_exists(schema):
|
118
|
-
raise click.UsageError(FILE_DOES_NOT_EXIST_MESSAGE)
|
119
|
-
# Base URL is required if it is not a dry run
|
120
|
-
if base_url is None and not dry_run:
|
121
|
-
raise click.UsageError(MISSING_BASE_URL_MESSAGE)
|
122
|
-
if kind == SchemaInputKind.NAME:
|
123
|
-
if api_name is not None:
|
124
|
-
raise click.UsageError(f"Got unexpected extra argument ({api_name})")
|
125
|
-
|
126
|
-
|
127
|
-
def validate_url(value: str) -> None:
|
128
|
-
from requests import PreparedRequest, RequestException
|
129
|
-
|
130
|
-
try:
|
131
|
-
PreparedRequest().prepare_url(value, {}) # type: ignore
|
132
|
-
except RequestException as exc:
|
133
|
-
raise click.UsageError(INVALID_SCHEMA_MESSAGE) from exc
|
134
|
-
|
135
|
-
|
136
|
-
def validate_base_url(ctx: click.core.Context, param: click.core.Parameter, raw_value: str) -> str:
|
137
|
-
try:
|
138
|
-
netloc = urlparse(raw_value).netloc
|
139
|
-
except ValueError as exc:
|
140
|
-
raise click.UsageError(INVALID_BASE_URL_MESSAGE) from exc
|
141
|
-
if raw_value and not netloc:
|
142
|
-
raise click.UsageError(INVALID_BASE_URL_MESSAGE)
|
143
|
-
return raw_value
|
144
|
-
|
145
|
-
|
146
|
-
def validate_generation_codec(ctx: click.core.Context, param: click.core.Parameter, raw_value: str) -> str:
|
147
|
-
try:
|
148
|
-
codecs.getencoder(raw_value)
|
149
|
-
except LookupError as exc:
|
150
|
-
raise click.UsageError(f"Codec `{raw_value}` is unknown") from exc
|
151
|
-
return raw_value
|
152
|
-
|
153
|
-
|
154
|
-
def validate_rate_limit(ctx: click.core.Context, param: click.core.Parameter, raw_value: str | None) -> str | None:
|
155
|
-
if raw_value is None:
|
156
|
-
return raw_value
|
157
|
-
try:
|
158
|
-
throttling.parse_units(raw_value)
|
159
|
-
return raw_value
|
160
|
-
except exceptions.UsageError as exc:
|
161
|
-
raise click.UsageError(exc.args[0]) from exc
|
162
|
-
|
163
|
-
|
164
|
-
def validate_app(ctx: click.core.Context, param: click.core.Parameter, raw_value: str | None) -> str | None:
|
165
|
-
if raw_value is None:
|
166
|
-
return raw_value
|
167
|
-
try:
|
168
|
-
load_app(raw_value)
|
169
|
-
# String is returned instead of an app because it might be passed to a subprocess
|
170
|
-
# Since most app instances are not-transferable to another process, they are passed as strings and
|
171
|
-
# imported in a subprocess
|
172
|
-
return raw_value
|
173
|
-
except Exception as exc:
|
174
|
-
formatted_module_name = click.style(f"'{raw_value}'", bold=True)
|
175
|
-
if isinstance(exc, ModuleNotFoundError):
|
176
|
-
message = APPLICATION_MISSING_MODULE_MESSAGE.format(module=formatted_module_name)
|
177
|
-
click.echo(message)
|
178
|
-
else:
|
179
|
-
trace = extract_nth_traceback(exc.__traceback__, 2)
|
180
|
-
lines = traceback.format_exception(type(exc), exc, trace)
|
181
|
-
traceback_message = "".join(lines).strip()
|
182
|
-
message = APPLICATION_IMPORT_ERROR_MESSAGE.format(
|
183
|
-
module=formatted_module_name, traceback=click.style(traceback_message, fg="red")
|
184
|
-
)
|
185
|
-
click.echo(message)
|
186
|
-
raise click.exceptions.Exit(1) from None
|
187
|
-
|
188
|
-
|
189
|
-
def validate_hypothesis_database(
|
190
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: str | None
|
191
|
-
) -> str | None:
|
192
|
-
if raw_value is None:
|
193
|
-
return raw_value
|
194
|
-
if ctx.params.get("hypothesis_derandomize"):
|
195
|
-
raise click.UsageError(INVALID_DERANDOMIZE_MESSAGE)
|
196
|
-
return raw_value
|
197
|
-
|
198
|
-
|
199
|
-
def validate_auth(
|
200
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: str | None
|
201
|
-
) -> tuple[str, str] | None:
|
202
|
-
if raw_value is not None:
|
203
|
-
with reraise_format_error(raw_value):
|
204
|
-
user, password = tuple(raw_value.split(":"))
|
205
|
-
if not user:
|
206
|
-
raise click.BadParameter("Username should not be empty.")
|
207
|
-
if not is_latin_1_encodable(user):
|
208
|
-
raise click.BadParameter("Username should be latin-1 encodable.")
|
209
|
-
if not is_latin_1_encodable(password):
|
210
|
-
raise click.BadParameter("Password should be latin-1 encodable.")
|
211
|
-
return user, password
|
212
|
-
return None
|
213
|
-
|
214
|
-
|
215
|
-
def _validate_and_build_multiple_options(
|
216
|
-
values: tuple[str, ...], name: str, callback: Callable[[str, str], None]
|
217
|
-
) -> dict[str, str]:
|
218
|
-
output = {}
|
219
|
-
for raw in values:
|
220
|
-
try:
|
221
|
-
key, value = raw.split("=", maxsplit=1)
|
222
|
-
except ValueError as exc:
|
223
|
-
raise click.BadParameter(f"Expected NAME=VALUE format, received {raw}.") from exc
|
224
|
-
key = key.strip()
|
225
|
-
if not key:
|
226
|
-
raise click.BadParameter(f"{name} parameter name should not be empty.")
|
227
|
-
if key in output:
|
228
|
-
raise click.BadParameter(f"{name} parameter {key} is specified multiple times.")
|
229
|
-
value = value.strip()
|
230
|
-
callback(key, value)
|
231
|
-
output[key] = value
|
232
|
-
return output
|
233
|
-
|
234
|
-
|
235
|
-
def _validate_set_query(_: str, value: str) -> None:
|
236
|
-
if is_illegal_surrogate(value):
|
237
|
-
raise click.BadParameter("Query parameter value should not contain surrogates.")
|
238
|
-
|
239
|
-
|
240
|
-
def validate_set_query(
|
241
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]
|
242
|
-
) -> dict[str, str]:
|
243
|
-
return _validate_and_build_multiple_options(raw_value, "Query", _validate_set_query)
|
244
|
-
|
245
|
-
|
246
|
-
def validate_set_header(
|
247
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]
|
248
|
-
) -> dict[str, str]:
|
249
|
-
return _validate_and_build_multiple_options(raw_value, "Header", partial(_validate_header, where="Header"))
|
250
|
-
|
251
|
-
|
252
|
-
def validate_set_cookie(
|
253
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]
|
254
|
-
) -> dict[str, str]:
|
255
|
-
return _validate_and_build_multiple_options(raw_value, "Cookie", partial(_validate_header, where="Cookie"))
|
256
|
-
|
257
|
-
|
258
|
-
def _validate_set_path(_: str, value: str) -> None:
|
259
|
-
if is_illegal_surrogate(value):
|
260
|
-
raise click.BadParameter("Path parameter value should not contain surrogates.")
|
261
|
-
|
262
|
-
|
263
|
-
def validate_set_path(
|
264
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]
|
265
|
-
) -> dict[str, str]:
|
266
|
-
return _validate_and_build_multiple_options(raw_value, "Path", _validate_set_path)
|
267
|
-
|
268
|
-
|
269
|
-
def _validate_header(key: str, value: str, where: str) -> None:
|
270
|
-
if not key:
|
271
|
-
raise click.BadParameter(f"{where} name should not be empty.")
|
272
|
-
if not is_latin_1_encodable(key):
|
273
|
-
raise click.BadParameter(f"{where} name should be latin-1 encodable.")
|
274
|
-
if not is_latin_1_encodable(value):
|
275
|
-
raise click.BadParameter(f"{where} value should be latin-1 encodable.")
|
276
|
-
if has_invalid_characters(key, value):
|
277
|
-
raise click.BadParameter(f"Invalid return character or leading space in {where.lower()}.")
|
278
|
-
|
279
|
-
|
280
|
-
def validate_headers(
|
281
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]
|
282
|
-
) -> dict[str, str]:
|
283
|
-
headers = {}
|
284
|
-
for header in raw_value:
|
285
|
-
with reraise_format_error(header):
|
286
|
-
key, value = header.split(":", maxsplit=1)
|
287
|
-
value = value.lstrip()
|
288
|
-
key = key.strip()
|
289
|
-
_validate_header(key, value, where="Header")
|
290
|
-
headers[key] = value
|
291
|
-
return headers
|
292
|
-
|
293
|
-
|
294
|
-
def validate_regex(ctx: click.core.Context, param: click.core.Parameter, raw_value: tuple[str, ...]) -> tuple[str, ...]:
|
295
|
-
for value in raw_value:
|
296
|
-
try:
|
297
|
-
re.compile(value)
|
298
|
-
except (re.error, OverflowError, RuntimeError) as exc:
|
299
|
-
raise click.BadParameter(f"Invalid regex: {exc.args[0]}.") # noqa: B904
|
300
|
-
return raw_value
|
301
|
-
|
302
|
-
|
303
|
-
def validate_request_cert_key(
|
304
|
-
ctx: click.core.Context, param: click.core.Parameter, raw_value: str | None
|
305
|
-
) -> str | None:
|
306
|
-
if raw_value is not None and "request_cert" not in ctx.params:
|
307
|
-
raise click.UsageError(MISSING_REQUEST_CERT_MESSAGE)
|
308
|
-
return raw_value
|
309
|
-
|
310
|
-
|
311
|
-
def validate_preserve_exact_body_bytes(ctx: click.core.Context, param: click.core.Parameter, raw_value: bool) -> bool:
|
312
|
-
if raw_value and ctx.params["cassette_path"] is None:
|
313
|
-
raise click.UsageError(MISSING_CASSETTE_PATH_ARGUMENT_MESSAGE)
|
314
|
-
return raw_value
|
315
|
-
|
316
|
-
|
317
|
-
def convert_verbosity(
|
318
|
-
ctx: click.core.Context, param: click.core.Parameter, value: str | None
|
319
|
-
) -> hypothesis.Verbosity | None:
|
320
|
-
import hypothesis
|
321
|
-
|
322
|
-
if value is None:
|
323
|
-
return value
|
324
|
-
return hypothesis.Verbosity[value]
|
325
|
-
|
326
|
-
|
327
|
-
def convert_stateful(ctx: click.core.Context, param: click.core.Parameter, value: str) -> Stateful | None:
|
328
|
-
if value == "none":
|
329
|
-
return None
|
330
|
-
return Stateful[value]
|
331
|
-
|
332
|
-
|
333
|
-
def convert_experimental(
|
334
|
-
ctx: click.core.Context, param: click.core.Parameter, value: tuple[str, ...]
|
335
|
-
) -> list[experimental.Experiment]:
|
336
|
-
return [
|
337
|
-
feature
|
338
|
-
for feature in experimental.GLOBAL_EXPERIMENTS.available
|
339
|
-
if feature.name in value or feature.is_env_var_set
|
340
|
-
]
|
341
|
-
|
342
|
-
|
343
|
-
def convert_checks(ctx: click.core.Context, param: click.core.Parameter, value: tuple[list[str]]) -> list[str]:
|
344
|
-
return reduce(operator.iadd, value, [])
|
345
|
-
|
346
|
-
|
347
|
-
def convert_http_methods(
|
348
|
-
ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
|
349
|
-
) -> set[str] | None:
|
350
|
-
if value is None:
|
351
|
-
return value
|
352
|
-
return {item.lower() for item in value}
|
353
|
-
|
354
|
-
|
355
|
-
def convert_status_codes(
|
356
|
-
ctx: click.core.Context, param: click.core.Parameter, value: list[str] | None
|
357
|
-
) -> list[str] | None:
|
358
|
-
if not value:
|
359
|
-
return value
|
360
|
-
|
361
|
-
invalid = []
|
362
|
-
|
363
|
-
for code in value:
|
364
|
-
if len(code) != 3:
|
365
|
-
invalid.append(code)
|
366
|
-
continue
|
367
|
-
|
368
|
-
if code[0] not in {"1", "2", "3", "4", "5"}:
|
369
|
-
invalid.append(code)
|
370
|
-
continue
|
371
|
-
|
372
|
-
upper_code = code.upper()
|
373
|
-
|
374
|
-
if "X" in upper_code:
|
375
|
-
if (
|
376
|
-
upper_code[1:] == "XX"
|
377
|
-
or (upper_code[1] == "X" and upper_code[2].isdigit())
|
378
|
-
or (upper_code[1].isdigit() and upper_code[2] == "X")
|
379
|
-
):
|
380
|
-
continue
|
381
|
-
else:
|
382
|
-
invalid.append(code)
|
383
|
-
continue
|
384
|
-
|
385
|
-
if not code.isnumeric():
|
386
|
-
invalid.append(code)
|
387
|
-
|
388
|
-
if invalid:
|
389
|
-
raise click.UsageError(
|
390
|
-
f"Invalid status code(s): {', '.join(invalid)}. "
|
391
|
-
"Use valid 3-digit codes between 100 and 599, "
|
392
|
-
"or wildcards (e.g., 2XX, 2X0, 20X), where X is a wildcard digit."
|
393
|
-
)
|
394
|
-
return value
|
395
|
-
|
396
|
-
|
397
|
-
def convert_code_sample_style(ctx: click.core.Context, param: click.core.Parameter, value: str) -> CodeSampleStyle:
|
398
|
-
return CodeSampleStyle.from_str(value)
|
399
|
-
|
400
|
-
|
401
|
-
def convert_cassette_format(ctx: click.core.Context, param: click.core.Parameter, value: str) -> CassetteFormat:
|
402
|
-
return CassetteFormat.from_str(value)
|
403
|
-
|
404
|
-
|
405
|
-
def convert_data_generation_method(
|
406
|
-
ctx: click.core.Context, param: click.core.Parameter, value: str
|
407
|
-
) -> list[DataGenerationMethod]:
|
408
|
-
if value == "all":
|
409
|
-
return DataGenerationMethod.all()
|
410
|
-
return [DataGenerationMethod[value]]
|
411
|
-
|
412
|
-
|
413
|
-
def _is_usable_dir(path: PathLike) -> bool:
|
414
|
-
if os.path.isfile(path):
|
415
|
-
path = os.path.dirname(path)
|
416
|
-
while not os.path.exists(path):
|
417
|
-
path = os.path.dirname(path)
|
418
|
-
return os.path.isdir(path) and os.access(path, os.R_OK | os.W_OK | os.X_OK)
|
419
|
-
|
420
|
-
|
421
|
-
def convert_hosts_file(ctx: click.core.Context, param: click.core.Parameter, value: PathLike) -> PathLike:
|
422
|
-
if not _is_usable_dir(value):
|
423
|
-
path = get_temporary_hosts_file()
|
424
|
-
click.secho(
|
425
|
-
"WARNING: The provided hosts.toml file location is unusable - using a temporary file for this session. "
|
426
|
-
f"path={str(value)!r}",
|
427
|
-
fg="yellow",
|
428
|
-
)
|
429
|
-
return path
|
430
|
-
return value
|
431
|
-
|
432
|
-
|
433
|
-
def convert_boolean_string(ctx: click.core.Context, param: click.core.Parameter, value: str) -> str | bool:
|
434
|
-
return _convert_boolean_string(value)
|
435
|
-
|
436
|
-
|
437
|
-
def convert_report(ctx: click.core.Context, param: click.core.Option, value: LazyFile) -> LazyFile:
|
438
|
-
if param.resolve_envvar_value(ctx) is not None and value.lower() in TRUE_VALUES:
|
439
|
-
value = param.flag_value
|
440
|
-
return value
|
441
|
-
|
442
|
-
|
443
|
-
@contextmanager
|
444
|
-
def reraise_format_error(raw_value: str) -> Generator[None, None, None]:
|
445
|
-
try:
|
446
|
-
yield
|
447
|
-
except ValueError as exc:
|
448
|
-
raise click.BadParameter(f"Expected KEY:VALUE format, received {raw_value}.") from exc
|
449
|
-
|
450
|
-
|
451
|
-
def get_workers_count() -> int:
|
452
|
-
"""Detect the number of available CPUs for the current process, if possible.
|
453
|
-
|
454
|
-
Use ``DEFAULT_WORKERS`` if not possible to detect.
|
455
|
-
"""
|
456
|
-
if hasattr(os, "sched_getaffinity"):
|
457
|
-
# In contrast with `os.cpu_count` this call respects limits on CPU resources on some Unix systems
|
458
|
-
return len(os.sched_getaffinity(0))
|
459
|
-
# Number of CPUs in the system, or 1 if undetermined
|
460
|
-
return os.cpu_count() or DEFAULT_WORKERS
|
461
|
-
|
462
|
-
|
463
|
-
def convert_workers(ctx: click.core.Context, param: click.core.Parameter, value: str) -> int:
|
464
|
-
if value == "auto":
|
465
|
-
return get_workers_count()
|
466
|
-
return int(value)
|