schemathesis 4.0.0a10__py3-none-any.whl → 4.0.0a11__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 (92) hide show
  1. schemathesis/__init__.py +3 -7
  2. schemathesis/checks.py +17 -7
  3. schemathesis/cli/commands/__init__.py +51 -3
  4. schemathesis/cli/commands/data.py +10 -0
  5. schemathesis/cli/commands/run/__init__.py +147 -260
  6. schemathesis/cli/commands/run/context.py +2 -3
  7. schemathesis/cli/commands/run/events.py +4 -0
  8. schemathesis/cli/commands/run/executor.py +60 -73
  9. schemathesis/cli/commands/run/filters.py +15 -165
  10. schemathesis/cli/commands/run/handlers/cassettes.py +105 -104
  11. schemathesis/cli/commands/run/handlers/junitxml.py +5 -4
  12. schemathesis/cli/commands/run/handlers/output.py +26 -47
  13. schemathesis/cli/commands/run/loaders.py +35 -50
  14. schemathesis/cli/commands/run/validation.py +36 -161
  15. schemathesis/cli/core.py +5 -3
  16. schemathesis/cli/ext/fs.py +7 -5
  17. schemathesis/cli/ext/options.py +0 -21
  18. schemathesis/config/__init__.py +188 -0
  19. schemathesis/config/_auth.py +51 -0
  20. schemathesis/config/_checks.py +268 -0
  21. schemathesis/config/_diff_base.py +99 -0
  22. schemathesis/config/_env.py +21 -0
  23. schemathesis/config/_error.py +156 -0
  24. schemathesis/config/_generation.py +150 -0
  25. schemathesis/config/_health_check.py +24 -0
  26. schemathesis/config/_operations.py +313 -0
  27. schemathesis/config/_output.py +171 -0
  28. schemathesis/config/_parameters.py +19 -0
  29. schemathesis/config/_phases.py +151 -0
  30. schemathesis/config/_projects.py +495 -0
  31. schemathesis/config/_rate_limit.py +17 -0
  32. schemathesis/config/_report.py +116 -0
  33. schemathesis/config/_validator.py +9 -0
  34. schemathesis/config/schema.json +837 -0
  35. schemathesis/core/__init__.py +2 -0
  36. schemathesis/core/compat.py +16 -9
  37. schemathesis/core/errors.py +19 -2
  38. schemathesis/core/failures.py +6 -7
  39. schemathesis/core/hooks.py +20 -0
  40. schemathesis/core/output/__init__.py +14 -37
  41. schemathesis/core/output/sanitization.py +3 -146
  42. schemathesis/core/validation.py +16 -0
  43. schemathesis/engine/__init__.py +2 -4
  44. schemathesis/engine/context.py +41 -43
  45. schemathesis/engine/core.py +7 -5
  46. schemathesis/engine/phases/__init__.py +10 -0
  47. schemathesis/engine/phases/probes.py +8 -8
  48. schemathesis/engine/phases/stateful/_executor.py +68 -43
  49. schemathesis/engine/phases/unit/__init__.py +23 -15
  50. schemathesis/engine/phases/unit/_executor.py +77 -17
  51. schemathesis/engine/phases/unit/_pool.py +1 -1
  52. schemathesis/errors.py +2 -0
  53. schemathesis/filters.py +2 -3
  54. schemathesis/generation/__init__.py +6 -31
  55. schemathesis/generation/case.py +5 -3
  56. schemathesis/generation/coverage.py +153 -123
  57. schemathesis/generation/hypothesis/builder.py +40 -14
  58. schemathesis/generation/meta.py +3 -3
  59. schemathesis/generation/overrides.py +37 -1
  60. schemathesis/generation/stateful/state_machine.py +8 -1
  61. schemathesis/graphql/loaders.py +21 -12
  62. schemathesis/openapi/checks.py +12 -8
  63. schemathesis/openapi/generation/filters.py +10 -8
  64. schemathesis/openapi/loaders.py +22 -13
  65. schemathesis/pytest/lazy.py +2 -5
  66. schemathesis/pytest/plugin.py +11 -2
  67. schemathesis/schemas.py +13 -61
  68. schemathesis/specs/graphql/schemas.py +11 -15
  69. schemathesis/specs/openapi/_hypothesis.py +12 -8
  70. schemathesis/specs/openapi/checks.py +16 -18
  71. schemathesis/specs/openapi/examples.py +4 -3
  72. schemathesis/specs/openapi/formats.py +2 -2
  73. schemathesis/specs/openapi/negative/__init__.py +2 -2
  74. schemathesis/specs/openapi/patterns.py +46 -16
  75. schemathesis/specs/openapi/references.py +2 -3
  76. schemathesis/specs/openapi/schemas.py +11 -20
  77. schemathesis/specs/openapi/stateful/__init__.py +10 -5
  78. schemathesis/transport/prepare.py +7 -6
  79. schemathesis/transport/requests.py +3 -1
  80. schemathesis/transport/wsgi.py +3 -4
  81. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/METADATA +7 -8
  82. schemathesis-4.0.0a11.dist-info/RECORD +166 -0
  83. schemathesis/cli/commands/run/checks.py +0 -79
  84. schemathesis/cli/commands/run/hypothesis.py +0 -78
  85. schemathesis/cli/commands/run/reports.py +0 -72
  86. schemathesis/cli/hooks.py +0 -36
  87. schemathesis/engine/config.py +0 -59
  88. schemathesis/experimental/__init__.py +0 -72
  89. schemathesis-4.0.0a10.dist-info/RECORD +0 -153
  90. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/WHEEL +0 -0
  91. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/entry_points.txt +0 -0
  92. {schemathesis-4.0.0a10.dist-info → schemathesis-4.0.0a11.dist-info}/licenses/LICENSE +0 -0
schemathesis/__init__.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from schemathesis import auths, contrib, engine, errors, experimental, graphql, hooks, openapi, pytest, python
3
+ from schemathesis import auths, contrib, engine, errors, graphql, hooks, openapi, pytest, python
4
4
  from schemathesis.checks import CheckContext, CheckFunction, check
5
- from schemathesis.core.output import OutputConfig, sanitization
5
+ from schemathesis.core.output import sanitization
6
6
  from schemathesis.core.transport import Response
7
7
  from schemathesis.core.version import SCHEMATHESIS_VERSION
8
- from schemathesis.generation import GenerationConfig, GenerationMode, HeaderConfig
8
+ from schemathesis.generation import GenerationMode
9
9
  from schemathesis.generation.case import Case
10
10
  from schemathesis.generation.targets import TargetContext, TargetFunction, target
11
11
  from schemathesis.hooks import HookContext
@@ -22,9 +22,6 @@ __all__ = [
22
22
  "CheckContext",
23
23
  "CheckFunction",
24
24
  "GenerationMode",
25
- "GenerationConfig",
26
- "HeaderConfig",
27
- "OutputConfig",
28
25
  "Response",
29
26
  "TargetContext",
30
27
  "TargetFunction",
@@ -36,7 +33,6 @@ __all__ = [
36
33
  "contrib",
37
34
  "engine",
38
35
  "errors",
39
- "experimental",
40
36
  "graphql",
41
37
  "hook",
42
38
  "hooks",
schemathesis/checks.py CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations
3
3
  import json
4
4
  from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Optional
5
5
 
6
+ from schemathesis.config import ChecksConfig
6
7
  from schemathesis.core.failures import (
7
8
  CustomFailure,
8
9
  Failure,
9
10
  FailureGroup,
10
11
  MalformedJson,
11
- MaxResponseTimeConfig,
12
12
  ResponseTimeExceeded,
13
13
  ServerError,
14
14
  )
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
23
23
  from schemathesis.generation.case import Case
24
24
 
25
25
  CheckFunction = Callable[["CheckContext", "Response", "Case"], Optional[bool]]
26
- ChecksConfig = dict[CheckFunction, Any]
27
26
 
28
27
 
29
28
  class CheckContext:
@@ -38,8 +37,9 @@ class CheckContext:
38
37
  config: ChecksConfig
39
38
  transport_kwargs: dict[str, Any] | None
40
39
  recorder: ScenarioRecorder | None
40
+ checks: list[CheckFunction]
41
41
 
42
- __slots__ = ("override", "auth", "headers", "config", "transport_kwargs", "recorder")
42
+ __slots__ = ("override", "auth", "headers", "config", "transport_kwargs", "recorder", "checks")
43
43
 
44
44
  def __init__(
45
45
  self,
@@ -56,6 +56,13 @@ class CheckContext:
56
56
  self.config = config
57
57
  self.transport_kwargs = transport_kwargs
58
58
  self.recorder = recorder
59
+ self.checks = []
60
+ for check in CHECKS.get_all():
61
+ name = check.__name__
62
+ if self.config.get_by_name(name=name).enabled:
63
+ self.checks.append(check)
64
+ if self.config.max_response_time.enabled:
65
+ self.checks.append(max_response_time)
59
66
 
60
67
  def find_parent(self, *, case_id: str) -> Case | None:
61
68
  if self.recorder is not None:
@@ -102,15 +109,18 @@ def not_a_server_error(ctx: CheckContext, response: Response, case: Case) -> boo
102
109
  return None
103
110
 
104
111
 
112
+ DEFAULT_MAX_RESPONSE_TIME = 10.0
113
+
114
+
105
115
  def max_response_time(ctx: CheckContext, response: Response, case: Case) -> bool | None:
106
- config = ctx.config.get(max_response_time, MaxResponseTimeConfig())
116
+ limit = ctx.config.max_response_time.limit or DEFAULT_MAX_RESPONSE_TIME
107
117
  elapsed = response.elapsed
108
- if elapsed > config.limit:
118
+ if elapsed > limit:
109
119
  raise ResponseTimeExceeded(
110
120
  operation=case.operation.label,
111
- message=f"Actual: {elapsed:.2f}ms\nLimit: {config.limit * 1000:.2f}ms",
121
+ message=f"Actual: {elapsed:.2f}ms\nLimit: {limit * 1000:.2f}ms",
112
122
  elapsed=elapsed,
113
- deadline=config.limit,
123
+ deadline=limit,
114
124
  )
115
125
  return None
116
126
 
@@ -4,20 +4,68 @@ from dataclasses import dataclass
4
4
  from typing import Any
5
5
 
6
6
  import click
7
+ from tomli import TOMLDecodeError
7
8
 
8
- from schemathesis.cli import hooks
9
+ from schemathesis.cli.commands.data import Data
9
10
  from schemathesis.cli.commands.run import run as run_command
11
+ from schemathesis.cli.commands.run.handlers.output import display_header
12
+ from schemathesis.cli.constants import EXTENSIONS_DOCUMENTATION_URL
10
13
  from schemathesis.cli.core import get_terminal_width
11
14
  from schemathesis.cli.ext.groups import CommandWithGroupedOptions, GroupedOption
15
+ from schemathesis.config import ConfigError, SchemathesisConfig
16
+ from schemathesis.core.errors import HookError, format_exception
17
+ from schemathesis.core.version import SCHEMATHESIS_VERSION
12
18
 
13
19
  CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
14
20
 
15
21
 
16
22
  @click.group(context_settings=CONTEXT_SETTINGS) # type: ignore[misc]
23
+ @click.option( # type: ignore[misc]
24
+ "--config-file",
25
+ "config_file",
26
+ help="The path to `schemathesis.toml` file to use for configuration",
27
+ metavar="PATH",
28
+ type=str,
29
+ )
30
+ @click.pass_context # type: ignore[misc]
17
31
  @click.version_option() # type: ignore[misc]
18
- def schemathesis() -> None:
32
+ def schemathesis(ctx: click.Context, config_file: str | None) -> None:
19
33
  """Property-based API testing for OpenAPI and GraphQL."""
20
- hooks.load()
34
+ try:
35
+ if config_file is not None:
36
+ config = SchemathesisConfig.from_path(config_file)
37
+ else:
38
+ config = SchemathesisConfig.discover()
39
+ except (TOMLDecodeError, ConfigError) as exc:
40
+ display_header(SCHEMATHESIS_VERSION)
41
+ click.secho(
42
+ f"❌ Failed to load configuration file{f' from {config_file}' if config_file else ''}",
43
+ fg="red",
44
+ bold=True,
45
+ )
46
+ if isinstance(exc, TOMLDecodeError):
47
+ detail = "The configuration file content is not valid TOML"
48
+ else:
49
+ detail = "The loaded configuration is incorrect"
50
+ click.echo(f"\n{detail}\n\n{exc}")
51
+ ctx.exit(1)
52
+ except HookError as exc:
53
+ click.secho("Unable to load Schemathesis extension hooks", fg="red", bold=True)
54
+ formatted_module_name = click.style(f"'{exc.module_path}'", bold=True)
55
+ cause = exc.__cause__
56
+ assert isinstance(cause, Exception)
57
+ if isinstance(cause, ModuleNotFoundError) and cause.name == exc.module_path:
58
+ click.echo(
59
+ f"\nAn attempt to import the module {formatted_module_name} failed because it could not be found."
60
+ )
61
+ click.echo("\nEnsure the module name is correctly spelled and reachable from the current directory.")
62
+ else:
63
+ click.echo(f"\nAn error occurred while importing the module {formatted_module_name}. Traceback:")
64
+ message = format_exception(cause, with_traceback=True, skip_frames=1)
65
+ click.secho(f"\n{message}", fg="red")
66
+ click.echo(f"\nFor more information on how to work with hooks, visit {EXTENSIONS_DOCUMENTATION_URL}")
67
+ ctx.exit(1)
68
+ ctx.obj = Data(config=config)
21
69
 
22
70
 
23
71
  @dataclass
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+
3
+ from schemathesis.config import SchemathesisConfig
4
+
5
+
6
+ @dataclass
7
+ class Data:
8
+ config: SchemathesisConfig
9
+
10
+ __slots__ = ("config",)