schemathesis 4.0.0a10__py3-none-any.whl → 4.0.0a12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. schemathesis/__init__.py +29 -30
  2. schemathesis/auths.py +65 -24
  3. schemathesis/checks.py +73 -39
  4. schemathesis/cli/commands/__init__.py +51 -3
  5. schemathesis/cli/commands/data.py +10 -0
  6. schemathesis/cli/commands/run/__init__.py +163 -274
  7. schemathesis/cli/commands/run/context.py +8 -4
  8. schemathesis/cli/commands/run/events.py +11 -1
  9. schemathesis/cli/commands/run/executor.py +70 -78
  10. schemathesis/cli/commands/run/filters.py +15 -165
  11. schemathesis/cli/commands/run/handlers/cassettes.py +105 -104
  12. schemathesis/cli/commands/run/handlers/junitxml.py +5 -4
  13. schemathesis/cli/commands/run/handlers/output.py +195 -121
  14. schemathesis/cli/commands/run/loaders.py +35 -50
  15. schemathesis/cli/commands/run/validation.py +52 -162
  16. schemathesis/cli/core.py +5 -3
  17. schemathesis/cli/ext/fs.py +7 -5
  18. schemathesis/cli/ext/options.py +0 -21
  19. schemathesis/config/__init__.py +189 -0
  20. schemathesis/config/_auth.py +51 -0
  21. schemathesis/config/_checks.py +268 -0
  22. schemathesis/config/_diff_base.py +99 -0
  23. schemathesis/config/_env.py +21 -0
  24. schemathesis/config/_error.py +156 -0
  25. schemathesis/config/_generation.py +149 -0
  26. schemathesis/config/_health_check.py +24 -0
  27. schemathesis/config/_operations.py +327 -0
  28. schemathesis/config/_output.py +171 -0
  29. schemathesis/config/_parameters.py +19 -0
  30. schemathesis/config/_phases.py +187 -0
  31. schemathesis/config/_projects.py +523 -0
  32. schemathesis/config/_rate_limit.py +17 -0
  33. schemathesis/config/_report.py +120 -0
  34. schemathesis/config/_validator.py +9 -0
  35. schemathesis/config/_warnings.py +25 -0
  36. schemathesis/config/schema.json +885 -0
  37. schemathesis/core/__init__.py +2 -0
  38. schemathesis/core/compat.py +16 -9
  39. schemathesis/core/errors.py +24 -4
  40. schemathesis/core/failures.py +6 -7
  41. schemathesis/core/hooks.py +20 -0
  42. schemathesis/core/output/__init__.py +14 -37
  43. schemathesis/core/output/sanitization.py +3 -146
  44. schemathesis/core/transport.py +36 -1
  45. schemathesis/core/validation.py +16 -0
  46. schemathesis/engine/__init__.py +2 -4
  47. schemathesis/engine/context.py +42 -43
  48. schemathesis/engine/core.py +7 -5
  49. schemathesis/engine/errors.py +60 -1
  50. schemathesis/engine/events.py +10 -2
  51. schemathesis/engine/phases/__init__.py +10 -0
  52. schemathesis/engine/phases/probes.py +11 -8
  53. schemathesis/engine/phases/stateful/__init__.py +2 -1
  54. schemathesis/engine/phases/stateful/_executor.py +104 -46
  55. schemathesis/engine/phases/stateful/context.py +2 -2
  56. schemathesis/engine/phases/unit/__init__.py +23 -15
  57. schemathesis/engine/phases/unit/_executor.py +110 -21
  58. schemathesis/engine/phases/unit/_pool.py +1 -1
  59. schemathesis/errors.py +2 -0
  60. schemathesis/filters.py +2 -3
  61. schemathesis/generation/__init__.py +5 -33
  62. schemathesis/generation/case.py +6 -3
  63. schemathesis/generation/coverage.py +154 -124
  64. schemathesis/generation/hypothesis/builder.py +70 -20
  65. schemathesis/generation/meta.py +3 -3
  66. schemathesis/generation/metrics.py +93 -0
  67. schemathesis/generation/modes.py +0 -8
  68. schemathesis/generation/overrides.py +37 -1
  69. schemathesis/generation/stateful/__init__.py +4 -0
  70. schemathesis/generation/stateful/state_machine.py +9 -1
  71. schemathesis/graphql/loaders.py +159 -16
  72. schemathesis/hooks.py +62 -35
  73. schemathesis/openapi/checks.py +12 -8
  74. schemathesis/openapi/generation/filters.py +10 -8
  75. schemathesis/openapi/loaders.py +142 -17
  76. schemathesis/pytest/lazy.py +2 -5
  77. schemathesis/pytest/loaders.py +24 -0
  78. schemathesis/pytest/plugin.py +33 -2
  79. schemathesis/schemas.py +21 -66
  80. schemathesis/specs/graphql/scalars.py +37 -3
  81. schemathesis/specs/graphql/schemas.py +23 -18
  82. schemathesis/specs/openapi/_hypothesis.py +26 -28
  83. schemathesis/specs/openapi/checks.py +37 -36
  84. schemathesis/specs/openapi/examples.py +4 -3
  85. schemathesis/specs/openapi/formats.py +32 -5
  86. schemathesis/specs/openapi/media_types.py +44 -1
  87. schemathesis/specs/openapi/negative/__init__.py +2 -2
  88. schemathesis/specs/openapi/patterns.py +46 -16
  89. schemathesis/specs/openapi/references.py +2 -3
  90. schemathesis/specs/openapi/schemas.py +19 -22
  91. schemathesis/specs/openapi/stateful/__init__.py +12 -6
  92. schemathesis/transport/__init__.py +54 -16
  93. schemathesis/transport/prepare.py +38 -13
  94. schemathesis/transport/requests.py +12 -9
  95. schemathesis/transport/wsgi.py +11 -12
  96. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a12.dist-info}/METADATA +50 -97
  97. schemathesis-4.0.0a12.dist-info/RECORD +164 -0
  98. schemathesis/cli/commands/run/checks.py +0 -79
  99. schemathesis/cli/commands/run/hypothesis.py +0 -78
  100. schemathesis/cli/commands/run/reports.py +0 -72
  101. schemathesis/cli/hooks.py +0 -36
  102. schemathesis/contrib/__init__.py +0 -9
  103. schemathesis/contrib/openapi/__init__.py +0 -9
  104. schemathesis/contrib/openapi/fill_missing_examples.py +0 -20
  105. schemathesis/engine/config.py +0 -59
  106. schemathesis/experimental/__init__.py +0 -72
  107. schemathesis/generation/targets.py +0 -69
  108. schemathesis-4.0.0a10.dist-info/RECORD +0 -153
  109. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a12.dist-info}/WHEEL +0 -0
  110. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a12.dist-info}/entry_points.txt +0 -0
  111. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a12.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from schemathesis.config._error import ConfigError
6
+ from schemathesis.core import rate_limit
7
+ from schemathesis.core.errors import InvalidRateLimit
8
+
9
+ if TYPE_CHECKING:
10
+ from pyrate_limiter import Limiter
11
+
12
+
13
+ def build_limiter(value: str) -> Limiter:
14
+ try:
15
+ return rate_limit.build_limiter(value)
16
+ except InvalidRateLimit as exc:
17
+ raise ConfigError(str(exc)) from None
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from schemathesis.config._diff_base import DiffBase
10
+ from schemathesis.config._env import resolve
11
+
12
+ DEFAULT_REPORT_DIRECTORY = Path("./schemathesis-report")
13
+
14
+
15
+ class ReportFormat(str, Enum):
16
+ """Available report formats."""
17
+
18
+ JUNIT = "junit"
19
+ VCR = "vcr"
20
+ HAR = "har"
21
+
22
+ @property
23
+ def extension(self) -> str:
24
+ """File extension for this format."""
25
+ return {
26
+ self.JUNIT: "xml",
27
+ self.VCR: "yaml",
28
+ self.HAR: "json",
29
+ }[self]
30
+
31
+
32
+ @dataclass(repr=False)
33
+ class ReportConfig(DiffBase):
34
+ enabled: bool
35
+ path: Path | None
36
+
37
+ __slots__ = ("enabled", "path")
38
+
39
+ def __init__(self, *, enabled: bool = False, path: Path | None = None) -> None:
40
+ self.enabled = enabled
41
+ self.path = path
42
+
43
+ @classmethod
44
+ def from_dict(cls, data: dict[str, Any]) -> ReportConfig:
45
+ path = resolve(data.get("path"))
46
+ if path is not None:
47
+ return cls(enabled=True, path=Path(path))
48
+ enabled = data.get("enabled", False)
49
+ return cls(enabled=enabled, path=path)
50
+
51
+
52
+ @dataclass(repr=False)
53
+ class ReportsConfig(DiffBase):
54
+ directory: Path
55
+ preserve_bytes: bool
56
+ junit: ReportConfig
57
+ vcr: ReportConfig
58
+ har: ReportConfig
59
+ _timestamp: str
60
+
61
+ __slots__ = ("directory", "preserve_bytes", "junit", "vcr", "har", "_timestamp")
62
+
63
+ def __init__(
64
+ self,
65
+ *,
66
+ directory: str | None = None,
67
+ preserve_bytes: bool = False,
68
+ junit: ReportConfig | None = None,
69
+ vcr: ReportConfig | None = None,
70
+ har: ReportConfig | None = None,
71
+ ) -> None:
72
+ self.directory = Path(resolve(directory) or DEFAULT_REPORT_DIRECTORY)
73
+ self.preserve_bytes = preserve_bytes
74
+ self.junit = junit or ReportConfig()
75
+ self.vcr = vcr or ReportConfig()
76
+ self.har = har or ReportConfig()
77
+ self._timestamp = datetime.datetime.now().strftime("%Y%m%dT%H%M%SZ")
78
+
79
+ @classmethod
80
+ def from_dict(cls, data: dict[str, Any]) -> ReportsConfig:
81
+ return cls(
82
+ directory=data.get("directory"),
83
+ preserve_bytes=data.get("preserve-bytes", False),
84
+ junit=ReportConfig.from_dict(data.get("junit", {})),
85
+ vcr=ReportConfig.from_dict(data.get("vcr", {})),
86
+ har=ReportConfig.from_dict(data.get("har", {})),
87
+ )
88
+
89
+ def update(
90
+ self,
91
+ *,
92
+ formats: list[ReportFormat] | None = None,
93
+ junit_path: str | None = None,
94
+ vcr_path: str | None = None,
95
+ har_path: str | None = None,
96
+ directory: Path = DEFAULT_REPORT_DIRECTORY,
97
+ preserve_bytes: bool = False,
98
+ ) -> None:
99
+ formats = formats or []
100
+ if junit_path is not None or ReportFormat.JUNIT in formats:
101
+ self.junit.enabled = True
102
+ self.junit.path = Path(junit_path) if junit_path is not None else junit_path
103
+ if vcr_path is not None or ReportFormat.VCR in formats:
104
+ self.vcr.enabled = True
105
+ self.vcr.path = Path(vcr_path) if vcr_path is not None else vcr_path
106
+ if har_path is not None or ReportFormat.HAR in formats:
107
+ self.har.enabled = True
108
+ self.har.path = Path(har_path) if har_path is not None else har_path
109
+ if directory != DEFAULT_REPORT_DIRECTORY:
110
+ self.directory = directory
111
+ if preserve_bytes is True:
112
+ self.preserve_bytes = preserve_bytes
113
+
114
+ def get_path(self, format: ReportFormat) -> Path:
115
+ """Get the final path for a specific format."""
116
+ report: ReportConfig = getattr(self, format.value)
117
+ if report.path is not None:
118
+ return report.path
119
+
120
+ return self.directory / f"{format.value}-{self._timestamp}.{format.extension}"
@@ -0,0 +1,9 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ import jsonschema.validators
5
+
6
+ with (Path(__file__).absolute().parent / "schema.json").open() as fd:
7
+ CONFIG_SCHEMA = json.loads(fd.read())
8
+
9
+ CONFIG_VALIDATOR = jsonschema.validators.Draft202012Validator(CONFIG_SCHEMA)
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+
5
+ from schemathesis.config._env import resolve
6
+
7
+
8
+ class SchemathesisWarning(str, enum.Enum):
9
+ MISSING_AUTH = "missing_auth"
10
+ MISSING_TEST_DATA = "missing_test_data"
11
+ VALIDATION_MISMATCH = "validation_mismatch"
12
+
13
+ @classmethod
14
+ def from_str(cls, value: str) -> SchemathesisWarning:
15
+ return {
16
+ "missing_auth": cls.MISSING_AUTH,
17
+ "missing_test_data": cls.MISSING_TEST_DATA,
18
+ "validation_mismatch": cls.VALIDATION_MISMATCH,
19
+ }[value.lower()]
20
+
21
+
22
+ def resolve_warnings(value: bool | list[str] | None) -> bool | list[SchemathesisWarning] | None:
23
+ if isinstance(value, list):
24
+ return [SchemathesisWarning.from_str(resolve(item)) for item in value]
25
+ return value