schemathesis 4.0.0a9__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 (93) 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 +6 -5
  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 +3 -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 +174 -134
  57. schemathesis/generation/hypothesis/__init__.py +7 -1
  58. schemathesis/generation/hypothesis/builder.py +40 -14
  59. schemathesis/generation/meta.py +3 -3
  60. schemathesis/generation/overrides.py +37 -1
  61. schemathesis/generation/stateful/state_machine.py +8 -1
  62. schemathesis/graphql/loaders.py +21 -12
  63. schemathesis/openapi/checks.py +12 -8
  64. schemathesis/openapi/generation/filters.py +10 -8
  65. schemathesis/openapi/loaders.py +22 -13
  66. schemathesis/pytest/lazy.py +2 -5
  67. schemathesis/pytest/plugin.py +11 -2
  68. schemathesis/schemas.py +13 -61
  69. schemathesis/specs/graphql/schemas.py +11 -15
  70. schemathesis/specs/openapi/_hypothesis.py +12 -8
  71. schemathesis/specs/openapi/checks.py +16 -18
  72. schemathesis/specs/openapi/examples.py +4 -3
  73. schemathesis/specs/openapi/formats.py +2 -2
  74. schemathesis/specs/openapi/negative/__init__.py +2 -2
  75. schemathesis/specs/openapi/patterns.py +46 -16
  76. schemathesis/specs/openapi/references.py +2 -3
  77. schemathesis/specs/openapi/schemas.py +11 -20
  78. schemathesis/specs/openapi/stateful/__init__.py +10 -5
  79. schemathesis/transport/prepare.py +7 -6
  80. schemathesis/transport/requests.py +3 -1
  81. schemathesis/transport/wsgi.py +3 -4
  82. {schemathesis-4.0.0a9.dist-info → schemathesis-4.0.0a11.dist-info}/METADATA +7 -8
  83. schemathesis-4.0.0a11.dist-info/RECORD +166 -0
  84. schemathesis/cli/commands/run/checks.py +0 -79
  85. schemathesis/cli/commands/run/hypothesis.py +0 -78
  86. schemathesis/cli/commands/run/reports.py +0 -72
  87. schemathesis/cli/hooks.py +0 -36
  88. schemathesis/engine/config.py +0 -59
  89. schemathesis/experimental/__init__.py +0 -72
  90. schemathesis-4.0.0a9.dist-info/RECORD +0 -153
  91. {schemathesis-4.0.0a9.dist-info → schemathesis-4.0.0a11.dist-info}/WHEEL +0 -0
  92. {schemathesis-4.0.0a9.dist-info → schemathesis-4.0.0a11.dist-info}/entry_points.txt +0 -0
  93. {schemathesis-4.0.0a9.dist-info → schemathesis-4.0.0a11.dist-info}/licenses/LICENSE +0 -0
@@ -1,52 +1,43 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from random import Random
5
- from typing import Any, Sequence
4
+ from typing import Any, Callable
6
5
 
7
6
  import click
8
7
  from click.utils import LazyFile
9
8
 
10
- from schemathesis import contrib, experimental
9
+ from schemathesis import contrib
11
10
  from schemathesis.checks import CHECKS
12
11
  from schemathesis.cli.commands.run import executor, validation
13
- from schemathesis.cli.commands.run.checks import CheckArguments
14
- from schemathesis.cli.commands.run.filters import FilterArguments, with_filters
15
- from schemathesis.cli.commands.run.hypothesis import (
16
- HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER,
17
- HealthCheck,
18
- prepare_health_checks,
19
- prepare_phases,
20
- prepare_settings,
21
- )
22
- from schemathesis.cli.commands.run.reports import DEFAULT_REPORT_DIRECTORY, ReportConfig, ReportFormat
12
+ from schemathesis.cli.commands.run.filters import with_filters
23
13
  from schemathesis.cli.constants import DEFAULT_WORKERS, MAX_WORKERS, MIN_WORKERS
24
14
  from schemathesis.cli.core import ensure_color
25
15
  from schemathesis.cli.ext.groups import group, grouped_option
26
16
  from schemathesis.cli.ext.options import (
27
17
  CsvChoice,
28
18
  CsvEnumChoice,
29
- CsvListChoice,
30
19
  CustomHelpMessageChoice,
31
20
  RegistryChoice,
32
21
  )
33
- from schemathesis.core.output import OutputConfig
22
+ from schemathesis.config import DEFAULT_REPORT_DIRECTORY, HealthCheck, ReportFormat, SchemathesisConfig
23
+ from schemathesis.core import HYPOTHESIS_IN_MEMORY_DATABASE_IDENTIFIER
34
24
  from schemathesis.core.transport import DEFAULT_RESPONSE_TIMEOUT
35
- from schemathesis.engine.config import EngineConfig, ExecutionConfig, NetworkConfig
36
- from schemathesis.engine.phases import PhaseName
37
- from schemathesis.generation import DEFAULT_GENERATOR_MODES, GenerationConfig, GenerationMode
38
- from schemathesis.generation.overrides import Override
39
- from schemathesis.generation.targets import TARGETS
25
+ from schemathesis.generation import DEFAULT_GENERATOR_MODES, GenerationMode
26
+ from schemathesis.generation.targets import TARGETS, TargetFunction
40
27
 
41
28
  # NOTE: Need to explicitly import all registered checks
42
29
  from schemathesis.specs.openapi.checks import * # noqa: F401, F403
43
30
 
44
31
  COLOR_OPTIONS_INVALID_USAGE_MESSAGE = "Can't use `--no-color` and `--force-color` simultaneously"
45
32
 
46
- DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
33
+ DEFAULT_PHASES = ["examples", "coverage", "fuzzing", "stateful"]
47
34
 
48
35
 
49
- @click.argument("schema", type=str) # type: ignore[misc]
36
+ @click.argument( # type: ignore[misc]
37
+ "location",
38
+ type=str,
39
+ callback=validation.validate_schema_location,
40
+ )
50
41
  @group("Options")
51
42
  @grouped_option(
52
43
  "--url",
@@ -61,7 +52,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
61
52
  @grouped_option(
62
53
  "--workers",
63
54
  "-w",
64
- "workers_num",
55
+ "workers",
65
56
  help="Number of concurrent workers for testing. Auto-adjusts if 'auto' is specified",
66
57
  type=CustomHelpMessageChoice(
67
58
  ["auto", *list(map(str, range(MIN_WORKERS, MAX_WORKERS + 1)))],
@@ -100,7 +91,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
100
91
  multiple=True,
101
92
  help="Comma-separated list of checks to run against API responses",
102
93
  type=RegistryChoice(CHECKS, with_all=True),
103
- default=("not_a_server_error",),
94
+ default=None,
104
95
  callback=validation.reduce_list,
105
96
  show_default=True,
106
97
  metavar="",
@@ -111,7 +102,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
111
102
  multiple=True,
112
103
  help="Comma-separated list of checks to skip during testing",
113
104
  type=RegistryChoice(CHECKS, with_all=True),
114
- default=(),
105
+ default=None,
115
106
  callback=validation.reduce_list,
116
107
  show_default=True,
117
108
  metavar="",
@@ -153,12 +144,14 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
153
144
  "include_by",
154
145
  type=str,
155
146
  metavar="EXPR",
147
+ callback=validation.validate_filter_expression,
156
148
  help="Include using custom expression",
157
149
  )
158
150
  @grouped_option(
159
151
  "--exclude-by",
160
152
  "exclude_by",
161
153
  type=str,
154
+ callback=validation.validate_filter_expression,
162
155
  metavar="EXPR",
163
156
  help="Exclude using custom expression",
164
157
  )
@@ -246,6 +239,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
246
239
  )
247
240
  @grouped_option(
248
241
  "--report-dir",
242
+ "report_directory",
249
243
  help="Directory to store all report files",
250
244
  type=click.Path(file_okay=False, dir_okay=True),
251
245
  default=DEFAULT_REPORT_DIRECTORY,
@@ -295,53 +289,6 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
295
289
  metavar="BOOLEAN",
296
290
  callback=validation.convert_boolean_string,
297
291
  )
298
- @group("Experimental options")
299
- @grouped_option(
300
- "--experimental",
301
- "experiments",
302
- help="Enable experimental features",
303
- type=click.Choice(sorted([experiment.label for experiment in experimental.GLOBAL_EXPERIMENTS.available])),
304
- callback=validation.convert_experimental,
305
- multiple=True,
306
- metavar="FEATURES",
307
- )
308
- @grouped_option(
309
- "--experimental-coverage-unexpected-methods",
310
- "coverage_unexpected_methods",
311
- help="HTTP methods to use when generating test cases with methods not specified in the API during the coverage phase.",
312
- type=CsvChoice(["get", "put", "post", "delete", "options", "head", "patch", "trace"], case_sensitive=False),
313
- callback=validation.convert_http_methods,
314
- metavar="",
315
- default=None,
316
- envvar="SCHEMATHESIS_EXPERIMENTAL_COVERAGE_UNEXPECTED_METHODS",
317
- )
318
- @grouped_option(
319
- "--experimental-missing-required-header-allowed-statuses",
320
- "missing_required_header_allowed_statuses",
321
- help="Comma-separated list of status codes expected for test cases with a missing required header",
322
- type=CsvListChoice(),
323
- callback=validation.convert_status_codes,
324
- metavar="",
325
- envvar="SCHEMATHESIS_EXPERIMENTAL_MISSING_REQUIRED_HEADER_ALLOWED_STATUSES",
326
- )
327
- @grouped_option(
328
- "--experimental-positive-data-acceptance-allowed-statuses",
329
- "positive_data_acceptance_allowed_statuses",
330
- help="Comma-separated list of status codes considered as successful responses",
331
- type=CsvListChoice(),
332
- callback=validation.convert_status_codes,
333
- metavar="",
334
- envvar="SCHEMATHESIS_EXPERIMENTAL_POSITIVE_DATA_ACCEPTANCE_ALLOWED_STATUSES",
335
- )
336
- @grouped_option(
337
- "--experimental-negative-data-rejection-allowed-statuses",
338
- "negative_data_rejection_allowed_statuses",
339
- help="Comma-separated list of status codes expected for rejected negative data",
340
- type=CsvListChoice(),
341
- callback=validation.convert_status_codes,
342
- metavar="",
343
- envvar="SCHEMATHESIS_EXPERIMENTAL_NEGATIVE_DATA_REJECTION_ALLOWED_STATUSES",
344
- )
345
292
  @group("Data generation options")
346
293
  @grouped_option(
347
294
  "--mode",
@@ -378,7 +325,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
378
325
  help="Enables deterministic mode, which eliminates random variation between tests",
379
326
  is_flag=True,
380
327
  is_eager=True,
381
- default=None,
328
+ default=False,
382
329
  show_default=True,
383
330
  )
384
331
  @grouped_option(
@@ -404,7 +351,7 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
404
351
  help="Guide input generation to values more likely to expose bugs via targeted property-based testing",
405
352
  type=RegistryChoice(TARGETS),
406
353
  default=None,
407
- callback=validation.reduce_list,
354
+ callback=validation.convert_maximize,
408
355
  show_default=True,
409
356
  metavar="METRIC",
410
357
  )
@@ -452,98 +399,57 @@ DEFAULT_PHASES = ("examples", "coverage", "fuzzing", "stateful")
452
399
  show_default=True,
453
400
  metavar="BOOLEAN",
454
401
  )
455
- @group("Open API options")
456
- @grouped_option(
457
- "--set-query",
458
- "set_query",
459
- help=r"OpenAPI: Override a specific query parameter by specifying 'parameter=value'",
460
- multiple=True,
461
- type=str,
462
- callback=validation.validate_set_query,
463
- )
464
- @grouped_option(
465
- "--set-header",
466
- "set_header",
467
- help=r"OpenAPI: Override a specific header parameter by specifying 'parameter=value'",
468
- multiple=True,
469
- type=str,
470
- callback=validation.validate_set_header,
471
- )
472
- @grouped_option(
473
- "--set-cookie",
474
- "set_cookie",
475
- help=r"OpenAPI: Override a specific cookie parameter by specifying 'parameter=value'",
476
- multiple=True,
477
- type=str,
478
- callback=validation.validate_set_cookie,
479
- )
480
- @grouped_option(
481
- "--set-path",
482
- "set_path",
483
- help=r"OpenAPI: Override a specific path parameter by specifying 'parameter=value'",
484
- multiple=True,
485
- type=str,
486
- callback=validation.validate_set_path,
487
- )
488
402
  @group("Global options")
489
403
  @grouped_option("--no-color", help="Disable ANSI color escape codes", type=bool, is_flag=True)
490
404
  @grouped_option("--force-color", help="Explicitly tells to enable ANSI color escape codes", type=bool, is_flag=True)
491
405
  @click.pass_context # type: ignore[misc]
492
406
  def run(
493
407
  ctx: click.Context,
494
- schema: str,
408
+ *,
409
+ location: str,
495
410
  auth: tuple[str, str] | None,
496
411
  headers: dict[str, str],
497
- set_query: dict[str, str],
498
- set_header: dict[str, str],
499
- set_cookie: dict[str, str],
500
- set_path: dict[str, str],
501
- experiments: list,
502
- coverage_unexpected_methods: set[str] | None,
503
- missing_required_header_allowed_statuses: list[str],
504
- positive_data_acceptance_allowed_statuses: list[str],
505
- negative_data_rejection_allowed_statuses: list[str],
506
- included_check_names: Sequence[str],
507
- excluded_check_names: Sequence[str],
412
+ included_check_names: list[str] | None,
413
+ excluded_check_names: list[str] | None,
508
414
  max_response_time: float | None = None,
509
- phases: Sequence[str] = DEFAULT_PHASES,
415
+ phases: list[str] = DEFAULT_PHASES,
510
416
  max_failures: int | None = None,
511
- continue_on_failure: bool = False,
512
- include_path: Sequence[str] = (),
513
- include_path_regex: str | None = None,
514
- include_method: Sequence[str] = (),
515
- include_method_regex: str | None = None,
516
- include_name: Sequence[str] = (),
517
- include_name_regex: str | None = None,
518
- include_tag: Sequence[str] = (),
519
- include_tag_regex: str | None = None,
520
- include_operation_id: Sequence[str] = (),
521
- include_operation_id_regex: str | None = None,
522
- exclude_path: Sequence[str] = (),
523
- exclude_path_regex: str | None = None,
524
- exclude_method: Sequence[str] = (),
525
- exclude_method_regex: str | None = None,
526
- exclude_name: Sequence[str] = (),
527
- exclude_name_regex: str | None = None,
528
- exclude_tag: Sequence[str] = (),
529
- exclude_tag_regex: str | None = None,
530
- exclude_operation_id: Sequence[str] = (),
531
- exclude_operation_id_regex: str | None = None,
532
- include_by: str | None = None,
533
- exclude_by: str | None = None,
417
+ continue_on_failure: bool | None = None,
418
+ include_path: tuple[str, ...],
419
+ include_path_regex: str | None,
420
+ include_method: tuple[str, ...],
421
+ include_method_regex: str | None,
422
+ include_name: tuple[str, ...],
423
+ include_name_regex: str | None,
424
+ include_tag: tuple[str, ...],
425
+ include_tag_regex: str | None,
426
+ include_operation_id: tuple[str, ...],
427
+ include_operation_id_regex: str | None,
428
+ exclude_path: tuple[str, ...],
429
+ exclude_path_regex: str | None,
430
+ exclude_method: tuple[str, ...],
431
+ exclude_method_regex: str | None,
432
+ exclude_name: tuple[str, ...],
433
+ exclude_name_regex: str | None,
434
+ exclude_tag: tuple[str, ...],
435
+ exclude_tag_regex: str | None,
436
+ exclude_operation_id: tuple[str, ...],
437
+ exclude_operation_id_regex: str | None,
438
+ include_by: Callable | None = None,
439
+ exclude_by: Callable | None = None,
534
440
  exclude_deprecated: bool = False,
535
- workers_num: int = DEFAULT_WORKERS,
536
- base_url: str | None = None,
441
+ workers: int = DEFAULT_WORKERS,
442
+ base_url: str | None,
537
443
  wait_for_schema: float | None = None,
444
+ suppress_health_check: list[HealthCheck] | None,
538
445
  rate_limit: str | None = None,
539
- suppress_health_check: list[HealthCheck] | None = None,
540
446
  request_timeout: int | None = None,
541
447
  request_tls_verify: bool = True,
542
448
  request_cert: str | None = None,
543
449
  request_cert_key: str | None = None,
544
450
  request_proxy: str | None = None,
545
- report_formats: list[ReportFormat] | None = None,
546
- report_dir: Path = DEFAULT_REPORT_DIRECTORY,
451
+ report_formats: list[ReportFormat] | None,
452
+ report_directory: Path | str = DEFAULT_REPORT_DIRECTORY,
547
453
  report_junit_path: LazyFile | None = None,
548
454
  report_vcr_path: LazyFile | None = None,
549
455
  report_har_path: LazyFile | None = None,
@@ -551,11 +457,11 @@ def run(
551
457
  output_sanitize: bool = True,
552
458
  output_truncate: bool = True,
553
459
  contrib_openapi_fill_missing_examples: bool = False,
554
- generation_modes: tuple[GenerationMode, ...] = DEFAULT_GENERATOR_MODES,
460
+ generation_modes: list[GenerationMode] = DEFAULT_GENERATOR_MODES,
555
461
  generation_seed: int | None = None,
556
462
  generation_max_examples: int | None = None,
557
- generation_maximize: Sequence[str] | None = None,
558
- generation_deterministic: bool | None = None,
463
+ generation_maximize: list[TargetFunction] | None,
464
+ generation_deterministic: bool = False,
559
465
  generation_database: str | None = None,
560
466
  generation_unique_inputs: bool = False,
561
467
  generation_allow_x00: bool = True,
@@ -573,126 +479,107 @@ def run(
573
479
  """
574
480
  if no_color and force_color:
575
481
  raise click.UsageError(COLOR_OPTIONS_INVALID_USAGE_MESSAGE)
576
- ensure_color(ctx, no_color, force_color)
577
482
 
578
- validation.validate_schema(schema, base_url)
483
+ config: SchemathesisConfig = ctx.obj.config
484
+
485
+ # First, set the right color
486
+ color: bool | None
487
+ if force_color:
488
+ color = True
489
+ elif no_color:
490
+ color = False
491
+ else:
492
+ color = config.color
493
+ ensure_color(ctx, color)
579
494
 
580
- _hypothesis_phases = prepare_phases(generation_no_shrink)
581
- _hypothesis_suppress_health_check = prepare_health_checks(suppress_health_check)
495
+ validation.validate_auth_overlap(auth, headers)
582
496
 
583
- for experiment in experiments:
584
- experiment.enable()
585
497
  if contrib_openapi_fill_missing_examples:
586
498
  contrib.openapi.fill_missing_examples.install()
587
499
 
588
- override = Override(query=set_query, headers=set_header, cookies=set_cookie, path_parameters=set_path)
589
-
590
- validation.validate_auth_overlap(auth, headers, override)
591
-
592
- filter_set = FilterArguments(
593
- include_path=include_path,
594
- include_method=include_method,
595
- include_name=include_name,
596
- include_tag=include_tag,
597
- include_operation_id=include_operation_id,
598
- include_path_regex=include_path_regex,
599
- include_method_regex=include_method_regex,
600
- include_name_regex=include_name_regex,
601
- include_tag_regex=include_tag_regex,
602
- include_operation_id_regex=include_operation_id_regex,
603
- exclude_path=exclude_path,
604
- exclude_method=exclude_method,
605
- exclude_name=exclude_name,
606
- exclude_tag=exclude_tag,
607
- exclude_operation_id=exclude_operation_id,
608
- exclude_path_regex=exclude_path_regex,
609
- exclude_method_regex=exclude_method_regex,
610
- exclude_name_regex=exclude_name_regex,
611
- exclude_tag_regex=exclude_tag_regex,
612
- exclude_operation_id_regex=exclude_operation_id_regex,
613
- include_by=include_by,
614
- exclude_by=exclude_by,
615
- exclude_deprecated=exclude_deprecated,
616
- ).into()
617
-
618
- selected_checks, checks_config = CheckArguments(
500
+ # Then override the global config from CLI options
501
+ config.update(
502
+ color=color,
503
+ suppress_health_check=suppress_health_check,
504
+ seed=generation_seed,
505
+ wait_for_schema=wait_for_schema,
506
+ max_failures=max_failures,
507
+ )
508
+ config.output.sanitization.update(enabled=output_sanitize)
509
+ config.output.truncation.update(enabled=output_truncate)
510
+ config.reports.update(
511
+ formats=report_formats,
512
+ junit_path=report_junit_path.name if report_junit_path else None,
513
+ vcr_path=report_vcr_path.name if report_vcr_path else None,
514
+ har_path=report_har_path.name if report_har_path else None,
515
+ directory=Path(report_directory),
516
+ preserve_bytes=report_preserve_bytes,
517
+ )
518
+ # Other CLI options work as an override for all defined projects
519
+ config.projects.override.update(
520
+ base_url=base_url,
521
+ headers=headers if headers else None,
522
+ basic_auth=auth,
523
+ workers=workers,
524
+ continue_on_failure=continue_on_failure,
525
+ rate_limit=rate_limit,
526
+ request_timeout=request_timeout,
527
+ tls_verify=request_tls_verify,
528
+ request_cert=request_cert,
529
+ request_cert_key=request_cert_key,
530
+ proxy=request_proxy,
531
+ )
532
+ # These are filters for what API operations should be tested
533
+ filter_set = {
534
+ "include_path": include_path,
535
+ "include_method": include_method,
536
+ "include_name": include_name,
537
+ "include_tag": include_tag,
538
+ "include_operation_id": include_operation_id,
539
+ "include_path_regex": include_path_regex,
540
+ "include_method_regex": include_method_regex,
541
+ "include_name_regex": include_name_regex,
542
+ "include_tag_regex": include_tag_regex,
543
+ "include_operation_id_regex": include_operation_id_regex,
544
+ "exclude_path": exclude_path,
545
+ "exclude_method": exclude_method,
546
+ "exclude_name": exclude_name,
547
+ "exclude_tag": exclude_tag,
548
+ "exclude_operation_id": exclude_operation_id,
549
+ "exclude_path_regex": exclude_path_regex,
550
+ "exclude_method_regex": exclude_method_regex,
551
+ "exclude_name_regex": exclude_name_regex,
552
+ "exclude_tag_regex": exclude_tag_regex,
553
+ "exclude_operation_id_regex": exclude_operation_id_regex,
554
+ "include_by": include_by,
555
+ "exclude_by": exclude_by,
556
+ "exclude_deprecated": exclude_deprecated,
557
+ }
558
+ config.projects.override.phases.update(phases=phases)
559
+ config.projects.override.checks.update(
619
560
  included_check_names=included_check_names,
620
561
  excluded_check_names=excluded_check_names,
621
- positive_data_acceptance_allowed_statuses=positive_data_acceptance_allowed_statuses,
622
- missing_required_header_allowed_statuses=missing_required_header_allowed_statuses,
623
- negative_data_rejection_allowed_statuses=negative_data_rejection_allowed_statuses,
624
562
  max_response_time=max_response_time,
625
- ).into()
626
-
627
- report_config = None
628
- if report_formats or report_junit_path or report_vcr_path or report_har_path:
629
- report_config = ReportConfig(
630
- formats=report_formats,
631
- directory=Path(report_dir),
632
- junit_path=report_junit_path if report_junit_path else None,
633
- vcr_path=report_vcr_path if report_vcr_path else None,
634
- har_path=report_har_path if report_har_path else None,
635
- preserve_bytes=report_preserve_bytes,
636
- sanitize_output=output_sanitize,
637
- )
638
-
639
- # Use the same seed for all tests unless `derandomize=True` is used
640
- seed: int | None
641
- if generation_seed is None and not generation_deterministic:
642
- seed = Random().getrandbits(128)
643
- else:
644
- seed = generation_seed
645
-
646
- phases_ = [PhaseName.PROBING] + [PhaseName.from_str(phase) for phase in phases]
563
+ )
564
+ config.projects.override.generation.update(
565
+ modes=generation_modes,
566
+ max_examples=generation_max_examples,
567
+ no_shrink=generation_no_shrink,
568
+ maximize=generation_maximize,
569
+ deterministic=generation_deterministic,
570
+ database=generation_database,
571
+ unique_inputs=generation_unique_inputs,
572
+ allow_x00=generation_allow_x00,
573
+ graphql_allow_null=generation_graphql_allow_null,
574
+ with_security_parameters=generation_with_security_parameters,
575
+ codec=generation_codec,
576
+ )
647
577
 
648
- config = executor.RunConfig(
649
- location=schema,
650
- base_url=base_url,
651
- engine=EngineConfig(
652
- execution=ExecutionConfig(
653
- phases=phases_,
654
- checks=selected_checks,
655
- targets=TARGETS.get_by_names(generation_maximize or []),
656
- hypothesis_settings=prepare_settings(
657
- database=generation_database,
658
- derandomize=generation_deterministic,
659
- max_examples=generation_max_examples,
660
- phases=_hypothesis_phases,
661
- suppress_health_check=_hypothesis_suppress_health_check,
662
- ),
663
- generation=GenerationConfig(
664
- modes=list(generation_modes),
665
- allow_x00=generation_allow_x00,
666
- graphql_allow_null=generation_graphql_allow_null,
667
- codec=generation_codec,
668
- with_security_parameters=generation_with_security_parameters,
669
- unexpected_methods=coverage_unexpected_methods,
670
- ),
671
- max_failures=max_failures,
672
- continue_on_failure=continue_on_failure,
673
- unique_inputs=generation_unique_inputs,
674
- seed=seed,
675
- workers_num=workers_num,
676
- ),
677
- network=NetworkConfig(
678
- auth=auth,
679
- headers=headers,
680
- timeout=request_timeout,
681
- tls_verify=request_tls_verify,
682
- proxy=request_proxy,
683
- cert=(request_cert, request_cert_key)
684
- if request_cert is not None and request_cert_key is not None
685
- else request_cert,
686
- ),
687
- override=override,
688
- checks_config=checks_config,
689
- ),
578
+ executor.execute(
579
+ location=location,
690
580
  filter_set=filter_set,
691
- wait_for_schema=wait_for_schema,
692
- rate_limit=rate_limit,
693
- output=OutputConfig(sanitize=output_sanitize, truncate=output_truncate),
694
- report=report_config,
581
+ # We don't the project yet, so pass the default config
582
+ config=config.projects.get_default(),
695
583
  args=ctx.args,
696
584
  params=ctx.params,
697
585
  )
698
- executor.execute(config)
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass, field
4
4
  from typing import TYPE_CHECKING, Generator
5
5
 
6
+ from schemathesis.config import ProjectConfig
6
7
  from schemathesis.core.failures import Failure
7
- from schemathesis.core.output import OutputConfig
8
8
  from schemathesis.core.result import Err, Ok
9
9
  from schemathesis.core.transforms import UNRESOLVABLE
10
10
  from schemathesis.core.transport import Response
@@ -176,12 +176,11 @@ class GroupedFailures:
176
176
  class ExecutionContext:
177
177
  """Storage for the current context of the execution."""
178
178
 
179
+ config: ProjectConfig
179
180
  statistic: Statistic = field(default_factory=Statistic)
180
181
  exit_code: int = 0
181
- output_config: OutputConfig = field(default_factory=OutputConfig)
182
182
  initialization_lines: list[str | Generator[str, None, None]] = field(default_factory=list)
183
183
  summary_lines: list[str | Generator[str, None, None]] = field(default_factory=list)
184
- seed: int | None = None
185
184
 
186
185
  def add_initialization_line(self, line: str | Generator[str, None, None]) -> None:
187
186
  self.initialization_lines.append(line)
@@ -1,6 +1,7 @@
1
1
  import time
2
2
  import uuid
3
3
 
4
+ from schemathesis.config import ProjectConfig
4
5
  from schemathesis.core import Specification
5
6
  from schemathesis.engine import events
6
7
  from schemathesis.schemas import ApiStatistic
@@ -26,6 +27,7 @@ class LoadingFinished(events.EngineEvent):
26
27
  "specification",
27
28
  "statistic",
28
29
  "schema",
30
+ "config",
29
31
  )
30
32
 
31
33
  def __init__(
@@ -38,6 +40,7 @@ class LoadingFinished(events.EngineEvent):
38
40
  specification: Specification,
39
41
  statistic: ApiStatistic,
40
42
  schema: dict,
43
+ config: ProjectConfig,
41
44
  ) -> None:
42
45
  self.id = uuid.uuid4()
43
46
  self.timestamp = time.time()
@@ -48,3 +51,4 @@ class LoadingFinished(events.EngineEvent):
48
51
  self.statistic = statistic
49
52
  self.schema = schema
50
53
  self.base_path = base_path
54
+ self.config = config