pytest-selective 1.3.0__tar.gz → 1.4.0__tar.gz
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.
- pytest_selective-1.3.0/.github/test-lanes.json → pytest_selective-1.4.0/.github/plan-lanes.json +1 -21
- pytest_selective-1.4.0/.github/test-lanes.json +22 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/ci.yml +19 -18
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/public-actions.yml +6 -4
- pytest_selective-1.4.0/.release-please-manifest.json +3 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/AGENTS.md +4 -3
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/CHANGELOG.md +7 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/CONTRIBUTING.md +37 -17
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/PKG-INFO +1 -1
- pytest_selective-1.4.0/contracts/selection/conftest.py.gv-dependency.yaml +5 -0
- pytest_selective-1.4.0/contracts/selection/test_selection_contract.py.gv-dependency.yaml +6 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/ci-toolkit.md +8 -1
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/ci.md +24 -0
- pytest_selective-1.4.0/documentation/case-study.md +293 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/mkdocs.yml +14 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/pyproject.toml +1 -1
- pytest_selective-1.4.0/scripts/check_lane_routing.py +145 -0
- pytest_selective-1.4.0/scripts/ci_file_config.py +18 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/smoke_distribution.py +2 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/ci.py +15 -11
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_graph.py +11 -3
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_ci_admission.py +2 -1
- pytest_selective-1.4.0/tests/test_ci_admission.py.gv-dependency.yaml +5 -0
- pytest_selective-1.4.0/tests/test_release_workflow.py.gv-dependency.yaml +5 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_repository_ci.py +76 -71
- pytest_selective-1.4.0/tests/test_repository_ci.py.gv-dependency.yaml +7 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_ci.py +76 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/uv.lock +1 -1
- pytest_selective-1.3.0/.release-please-manifest.json +0 -3
- pytest_selective-1.3.0/scripts/ci_file_config.py +0 -13
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.dry-lint/baseline.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.dry-lint/progress.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.dry-lint.toml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.gitattributes +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/dependabot.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/validation-inputs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/docs.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/dry-lint-audit.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/dry-lint-judge.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/dry-lint.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/release.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/weekly-checkpoint.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.gitignore +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.pre-commit-config.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/.readthedocs.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/CLAUDE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/LICENSE +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/PROVENANCE.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/RELEASING.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/actions/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/actions/plan-lanes/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/actions/test-impact/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/actions/validation-gate/action.yml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/agentsync.toml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/conftest.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/quint-connect.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/quint.ini +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/runtime_deps.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/selection.qnt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/selection_driver.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/contracts/selection/test_selection_contract.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/README.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/actions.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/agents.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/banner.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/banner.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/brand.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/icon.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/logo.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/social-preview.png +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/configuration.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/evidence.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/local-development.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/selection-health.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/shared-plan-execution.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/tutorial.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/tutorials/adoption-checklist.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/tutorials/sidecar-dependencies.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/tutorials/skip-unrelated-ci.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/graph-based-inputs-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/check_site.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/ecosystem.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/hooks.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/index.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/llms.txt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/overrides/main.html +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/publishing.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/requirements.in +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/documentation/requirements.txt +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/release-please-config.json +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/check_docs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/check_metadata.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/compare_impacted.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/measure_ci_runs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/public_actions.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/render_brand.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/scripts/tutorial.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/skills/pytest-selective/SKILL.md +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/checkpoint.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/execution.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/gate.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/git_operations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_audit_declarations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_cli.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_explanation.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_models.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/local.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_checkpoint_policy.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_public_actions.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_release_workflow.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/test_tutorial.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/__init__.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/git_repo.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_gate.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_audit_declarations.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_graph.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_portability.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_pull_request_merge_base.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-1.3.0 → pytest_selective-1.4.0}/tests/testing/test_working_tree.py +0 -0
pytest_selective-1.3.0/.github/test-lanes.json → pytest_selective-1.4.0/.github/plan-lanes.json
RENAMED
|
@@ -25,27 +25,6 @@
|
|
|
25
25
|
"src/**",
|
|
26
26
|
"tests/**"
|
|
27
27
|
]
|
|
28
|
-
},
|
|
29
|
-
"contracts": {
|
|
30
|
-
"roots": [
|
|
31
|
-
"contracts"
|
|
32
|
-
],
|
|
33
|
-
"inputs": [
|
|
34
|
-
"src/**",
|
|
35
|
-
"contracts/**",
|
|
36
|
-
"tests/testing/git_repo.py"
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
"package": {
|
|
40
|
-
"inputs": [
|
|
41
|
-
"src/**",
|
|
42
|
-
"pyproject.toml",
|
|
43
|
-
"uv.lock",
|
|
44
|
-
"README.md",
|
|
45
|
-
"LICENSE",
|
|
46
|
-
"scripts/check_metadata.py",
|
|
47
|
-
"scripts/smoke_distribution.py"
|
|
48
|
-
]
|
|
49
28
|
}
|
|
50
29
|
},
|
|
51
30
|
"global_inputs": [
|
|
@@ -56,6 +35,7 @@
|
|
|
56
35
|
"pyproject.toml",
|
|
57
36
|
"**/uv.lock",
|
|
58
37
|
"uv.lock",
|
|
38
|
+
".github/plan-lanes.json",
|
|
59
39
|
".github/test-lanes.json",
|
|
60
40
|
".github/workflows/ci.yml",
|
|
61
41
|
".github/workflows/release.yml",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "workflow",
|
|
3
|
+
"lanes": {
|
|
4
|
+
"tests": {
|
|
5
|
+
"roots": ["tests"]
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
"rules": [
|
|
9
|
+
{
|
|
10
|
+
"name": "python-environment",
|
|
11
|
+
"when": ["pyproject.toml", "**/pyproject.toml", "uv.lock", "**/uv.lock", ".python-version"],
|
|
12
|
+
"select": ["all"],
|
|
13
|
+
"reason": "the locked test environment; documentation/requirements*.txt belongs to Read the Docs"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "distribution-smoke",
|
|
17
|
+
"when": ["scripts/smoke_distribution.py"],
|
|
18
|
+
"select": ["lane:tests"],
|
|
19
|
+
"reason": "the distributions job runs the smoke only when the tests lane is planned"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -13,6 +13,9 @@ concurrency:
|
|
|
13
13
|
group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}
|
|
14
14
|
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}
|
|
15
15
|
jobs:
|
|
16
|
+
# Plans whole lanes before any runner starts: each lane is the name a job's condition reads, and a change selects it
|
|
17
|
+
# through the tests, scripts and actions that job runs. `ci-esta lanes` shows the lanes and why; .github/test-lanes.json
|
|
18
|
+
# holds only what the workflow cannot show.
|
|
16
19
|
select-tests:
|
|
17
20
|
runs-on: ubuntu-latest
|
|
18
21
|
permissions:
|
|
@@ -31,7 +34,7 @@ jobs:
|
|
|
31
34
|
CONTRACT_LANE_JOBS: '{"selection":"Selection contract (Quint)"}'
|
|
32
35
|
outputs:
|
|
33
36
|
# Planned lanes after reuse: a reusing lane is false only when every one of its jobs is proven by a receipt.
|
|
34
|
-
lanes: ${{ steps.
|
|
37
|
+
lanes: ${{ steps.contract-evidence.outputs.admitted-lanes || steps.plan.outputs.lanes }}
|
|
35
38
|
# The plan before reuse, for steps that follow changed runtime inputs rather than test execution.
|
|
36
39
|
planned: ${{ steps.plan.outputs.lanes }}
|
|
37
40
|
mode: ${{ steps.files.outputs.mode }}
|
|
@@ -49,9 +52,10 @@ jobs:
|
|
|
49
52
|
if: github.event_name == 'pull_request'
|
|
50
53
|
run: 'true'
|
|
51
54
|
- id: plan
|
|
52
|
-
uses:
|
|
55
|
+
uses: gigaverse-app/ci-esta/actions/plan-work@v1
|
|
53
56
|
with:
|
|
54
57
|
config: .github/test-lanes.json
|
|
58
|
+
# File selection has no workflow graph, so it reads the complete lane file public-actions.yml plans with.
|
|
55
59
|
- name: Share the repository graph and execution inputs with file selection
|
|
56
60
|
env:
|
|
57
61
|
PYTHONPATH: src
|
|
@@ -82,6 +86,8 @@ jobs:
|
|
|
82
86
|
input-paths: ${{ steps.inputs.outputs.tests }}
|
|
83
87
|
reuse-after-merge: 'true'
|
|
84
88
|
reuse-version-only-release: 'true'
|
|
89
|
+
planned-lanes: ${{ steps.plan.outputs.lanes }}
|
|
90
|
+
lane-keys: '{"tests":["py311-ubuntu","py311-windows","py311-macos","py314-ubuntu","py314-windows","py314-macos","minimum"]}'
|
|
85
91
|
- id: contract-evidence
|
|
86
92
|
if: >-
|
|
87
93
|
${{ (github.event_name == 'pull_request' || github.event_name == 'push') &&
|
|
@@ -93,21 +99,9 @@ jobs:
|
|
|
93
99
|
input-paths: ${{ steps.inputs.outputs.contracts }}
|
|
94
100
|
reuse-after-merge: 'true'
|
|
95
101
|
reuse-version-only-release: 'true'
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
PLANNED_LANES: ${{ steps.plan.outputs.lanes }}
|
|
100
|
-
REUSED: ${{ steps.evidence.outputs.reused || '{}' }}
|
|
101
|
-
CONTRACT_REUSED: ${{ steps.contract-evidence.outputs.reused || '{}' }}
|
|
102
|
-
run: |
|
|
103
|
-
admitted=$(jq -c \
|
|
104
|
-
--argjson tests "$TEST_LANE_JOBS" --argjson reused "$REUSED" \
|
|
105
|
-
--argjson contracts "$CONTRACT_LANE_JOBS" --argjson contract_reused "$CONTRACT_REUSED" '
|
|
106
|
-
def proven($jobs; $proof): [$jobs | keys[] | $proof[.] == true] | all;
|
|
107
|
-
(if proven($tests; $reused) then .tests = false else . end)
|
|
108
|
-
| (if proven($contracts; $contract_reused) then .contracts = false else . end)' <<<"$PLANNED_LANES")
|
|
109
|
-
echo "reused=$REUSED contracts=$CONTRACT_REUSED"
|
|
110
|
-
echo "lanes=$admitted" | tee -a "$GITHUB_OUTPUT"
|
|
102
|
+
# Chained: the tests step already admitted its lane; this step admits the contracts lane on top.
|
|
103
|
+
planned-lanes: ${{ steps.evidence.outputs.admitted-lanes }}
|
|
104
|
+
lane-keys: '{"contracts":["selection"]}'
|
|
111
105
|
quality:
|
|
112
106
|
runs-on: ubuntu-latest
|
|
113
107
|
steps:
|
|
@@ -119,11 +113,18 @@ jobs:
|
|
|
119
113
|
- run: uv run --locked pre-commit run --all-files
|
|
120
114
|
env:
|
|
121
115
|
SKIP: dry-lint
|
|
116
|
+
# Each lane-gated job's condition, the gate's lane-jobs and the lanes the workflow defines must agree.
|
|
122
117
|
- name: Check aggregate gate against the execution lanes
|
|
123
118
|
run: >-
|
|
124
119
|
uv run --no-project --python 3.14
|
|
125
|
-
--with "ci-esta>=1.
|
|
120
|
+
--with "ci-esta[selection]>=1.13.0,<2.0.0"
|
|
126
121
|
ci-esta check-workflow --workflow .github/workflows/ci.yml --lanes .github/test-lanes.json
|
|
122
|
+
# Plans one-file changes with CI-esta against real commits and fails on any lane a change should not run.
|
|
123
|
+
- name: Check the lane routing
|
|
124
|
+
run: >-
|
|
125
|
+
uv run --no-project --python 3.14
|
|
126
|
+
--with "ci-esta[selection]>=1.13.0,<2.0.0"
|
|
127
|
+
python scripts/check_lane_routing.py
|
|
127
128
|
- name: Validate GitHub Actions
|
|
128
129
|
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
|
|
129
130
|
tests:
|
|
@@ -15,10 +15,12 @@ jobs:
|
|
|
15
15
|
- uses: actions/checkout@v7
|
|
16
16
|
with:
|
|
17
17
|
fetch-depth: 0
|
|
18
|
+
# This workflow smokes the repository's own actions, so its planner is plan-lanes itself, run selectively on every
|
|
19
|
+
# change against .github/plan-lanes.json; CI's lanes come from CI-esta's plan-work.
|
|
18
20
|
- id: select
|
|
19
21
|
uses: ./actions/plan-lanes
|
|
20
22
|
with:
|
|
21
|
-
config: .github/
|
|
23
|
+
config: .github/plan-lanes.json
|
|
22
24
|
artifact-name: public-action-selection
|
|
23
25
|
public-actions:
|
|
24
26
|
needs: select-tests
|
|
@@ -35,7 +37,7 @@ jobs:
|
|
|
35
37
|
python - <<'PY'
|
|
36
38
|
import json
|
|
37
39
|
from pathlib import Path
|
|
38
|
-
graph = json.loads(Path('.github/
|
|
40
|
+
graph = json.loads(Path('.github/plan-lanes.json').read_text())['graph']
|
|
39
41
|
Path('smoke-impact.json').write_text(json.dumps(graph))
|
|
40
42
|
PY
|
|
41
43
|
- uses: ./
|
|
@@ -57,7 +59,7 @@ jobs:
|
|
|
57
59
|
- uses: ./actions/plan-lanes
|
|
58
60
|
id: lanes
|
|
59
61
|
with:
|
|
60
|
-
config: .github/
|
|
62
|
+
config: .github/plan-lanes.json
|
|
61
63
|
base: ${{ github.sha }}
|
|
62
64
|
full: 'true'
|
|
63
65
|
artifact-name: ''
|
|
@@ -68,7 +70,7 @@ jobs:
|
|
|
68
70
|
python - <<'PY'
|
|
69
71
|
import json, os
|
|
70
72
|
# A full checkpoint admits every configured lane.
|
|
71
|
-
configured = json.load(open('.github/
|
|
73
|
+
configured = json.load(open('.github/plan-lanes.json', encoding='utf-8'))['lanes']
|
|
72
74
|
assert json.loads(os.environ['LANES']) == dict.fromkeys(configured, True)
|
|
73
75
|
PY
|
|
74
76
|
- name: Require successful planning after fallback execution
|
|
@@ -19,9 +19,10 @@ on pytest-selective itself.
|
|
|
19
19
|
- Planning supports committed revisions and explicit working-tree snapshots. Preserve full execution on
|
|
20
20
|
operational uncertainty and diagnostics for invalid declarations. An empty
|
|
21
21
|
selection is not an empty pytest argument list.
|
|
22
|
-
- Select consumer routing regressions by their declared execution/configuration inputs.
|
|
23
|
-
`.github/test-lanes.json`
|
|
24
|
-
|
|
22
|
+
- Select consumer routing regressions by their declared execution/configuration inputs. Declare a
|
|
23
|
+
file a test or CI script reads by path next to its reader; `.github/test-lanes.json` holds only
|
|
24
|
+
what CI-esta cannot see in the workflow, and `scripts/check_lane_routing.py` pins the lanes. Preserve
|
|
25
|
+
explicit full/manual checkpoints and the `test:full` override.
|
|
25
26
|
- The README's performance numbers are scenario-specific observations. Keep
|
|
26
27
|
claims linked to [raw evidence](docs/benchmarks/2026-10-22-routing.md); distinguish
|
|
27
28
|
runner-seconds, elapsed time and billing. Skill/example edits need usable links
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.3.0...v1.4.0) (2026-11-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **lanes:** partition test roots by filename patterns ([27c10ec](https://github.com/gigaverse-app/pytest-selective/commit/27c10ec608f052a36f58d9b10730048ab9d38598))
|
|
9
|
+
|
|
3
10
|
## [1.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.2.0...v1.3.0) (2026-11-09)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -98,24 +98,44 @@ limitations. The pull request template provides a short checklist. See
|
|
|
98
98
|
|
|
99
99
|
## CI lane selection
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
CI plans whole lanes with [CI-esta](https://github.com/gigaverse-app/ci-esta)'s
|
|
102
|
+
`plan-work` before any runner starts: `tests` (the Python matrix and
|
|
103
|
+
`pytest-minimum`), `contracts` (the Quint selection contract) and `package` (the
|
|
104
|
+
distribution build), the names the jobs' conditions read. A change selects a lane
|
|
105
|
+
through what its jobs run: the tests that import it, the files tests declare they
|
|
106
|
+
read, and the scripts and actions each job executes. Documentation-only PRs keep
|
|
102
107
|
quality checks (including Markdown links) and the documentation build, while
|
|
103
|
-
skipping every
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
skipping every lane. A README or LICENSE change runs the `package` lane, because
|
|
109
|
+
both distributions embed them; a README change also runs `contracts`, whose job
|
|
110
|
+
checks the README's contract report, and the one test whose metadata check reads it. `pyproject.toml` and `uv.lock` run every
|
|
111
|
+
lane. `ci-esta lanes --base origin/main` shows what a change would run and why.
|
|
112
|
+
[.github/test-lanes.json](.github/test-lanes.json) extends the workflow and holds
|
|
113
|
+
only what it cannot show: the `tests` lane's roots (its jobs run `pytest-selective
|
|
114
|
+
execute-plan`, which CI-esta does not read as a pytest command), a
|
|
115
|
+
`python-environment` rule that leaves the documentation site's requirements out,
|
|
116
|
+
and a rule that plans the tests lane when the installed-distribution smoke script
|
|
117
|
+
changes, because `distributions` smokes only when that lane is planned. When a test
|
|
118
|
+
or a script starts reading a file by path, declare it next to the reader (a
|
|
119
|
+
`<test>.gv-dependency.yaml` sidecar, which `pytest-selective audit
|
|
120
|
+
--write-declarations` writes, or a `# gv:test-dependency: PATH` comment).
|
|
121
|
+
`quality` runs [scripts/check_lane_routing.py](scripts/check_lane_routing.py),
|
|
122
|
+
which plans one-file changes with CI-esta against real Git commits, including a
|
|
123
|
+
full checkpoint, and `ci-esta check-workflow`, which keeps the job conditions, the
|
|
124
|
+
gate and the lane file in agreement. Manual/called/release runs and the
|
|
125
|
+
`test:full` label run everything; a failed planner also runs all tests.
|
|
126
|
+
Installed-distribution smoke checks follow the tests lane's plan; README-only
|
|
127
|
+
changes skip them while still verifying the README embedded in both release
|
|
128
|
+
archives.
|
|
129
|
+
|
|
130
|
+
Within an admitted lane, the repository's public file-selection action selects
|
|
131
|
+
affected test files once; the platform matrix and minimum-pytest job execute that
|
|
132
|
+
same plan. CI-esta receives both lane and file explanations, learns JUnit timings,
|
|
133
|
+
and validates the aggregate gate. File selection has no workflow graph, so it reads
|
|
134
|
+
[.github/plan-lanes.json](.github/plan-lanes.json), a complete lane file: its graph
|
|
135
|
+
plus the execution inputs that run every test file (scripts, workflows, actions,
|
|
136
|
+
dependency and pytest policy). `public-actions.yml` plans with the same file through
|
|
137
|
+
this repository's own `plan-lanes` action, selectively on every change, and smokes
|
|
138
|
+
its full checkpoint. Declare subprocess
|
|
119
139
|
and non-import dependencies at their readers. Full plans collect whole-suite
|
|
120
140
|
coverage; scoped plans produce JUnit timing evidence without presenting partial
|
|
121
141
|
coverage as a whole-suite result. The public [shared-plan executor](docs/shared-plan-execution.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pytest-selective
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Explainable Python test impact analysis: select affected pytest files from Git changes
|
|
5
5
|
Project-URL: Homepage, https://github.com/gigaverse-app/pytest-selective
|
|
6
6
|
Project-URL: Documentation, https://pytest-selective.readthedocs.io/en/latest/
|
|
@@ -36,7 +36,14 @@ PR, on a version-only release PR, and on a PR push that changes nothing the
|
|
|
36
36
|
tests read. A `PR evidence #N head SHA` step ties each PR run to its pull
|
|
37
37
|
request, because GitHub often leaves that link out. The distribution smoke test
|
|
38
38
|
still follows the plan before reuse (`planned`), and partial proof runs the
|
|
39
|
-
whole lane.
|
|
39
|
+
whole lane. The contract lane works the same way with its own receipt and inputs
|
|
40
|
+
(including `README.md`, which its README check reads).
|
|
41
|
+
|
|
42
|
+
CI-esta decides admission: each evidence step gets the planned lanes and
|
|
43
|
+
`lane-keys`, the keys of every job a lane gates, and reports `admitted-lanes`.
|
|
44
|
+
The contract step chains on the tests step's result. `ci-esta check-workflow`
|
|
45
|
+
then proves `lane-keys` name every job each lane gates, matrix cells included,
|
|
46
|
+
so a newly added cell can never be skipped as proven.
|
|
40
47
|
|
|
41
48
|
The unconditional CI job runs `ci-esta check-workflow` against this workflow and
|
|
42
49
|
lane configuration, so renamed lanes or omitted metadata prerequisites cannot
|
|
@@ -285,3 +285,27 @@ The action also uploads the report as the `test-lane-selection` artifact; set
|
|
|
285
285
|
pull-request sticky's lane section: which lanes ran and why, and the runner time
|
|
286
286
|
the skipped jobs took on the default branch's last full run. See
|
|
287
287
|
[how the two fit together in a Python project](https://github.com/gigaverse-app/ci-esta/blob/main/docs/integrations.md#getting-the-most-from-both-in-a-python-project).
|
|
288
|
+
|
|
289
|
+
## Split whole lanes by test filename
|
|
290
|
+
|
|
291
|
+
Lanes sharing a directory can partition its tests with `test_patterns` and
|
|
292
|
+
`exclude_patterns`. Both match repository-relative POSIX paths using the same
|
|
293
|
+
globs as lane inputs; `*` also matches path separators. Roots still limit the
|
|
294
|
+
search. The defaults include every test under the roots and exclude none.
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"unit": {"roots": ["tests"], "exclude_patterns": ["*_integration.py"]},
|
|
299
|
+
"integration": {"roots": ["tests"], "test_patterns": ["*_integration.py"]}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Put these entries under `lanes` in the lane configuration. A source dependency
|
|
304
|
+
still selects every lane containing an affected test. Deleted tests use their
|
|
305
|
+
base-revision membership; an affected test claimed by no lane causes a full
|
|
306
|
+
fallback. Explicit inputs and full checkpoints are unaffected by these filters.
|
|
307
|
+
|
|
308
|
+
Same-named modules in independent module roots are unrelated unless an import
|
|
309
|
+
connects them. An actual ambiguous bare import conservatively retains every
|
|
310
|
+
possible target. Use qualified imports to make independent demo modules
|
|
311
|
+
unambiguous; selection does not guess a runtime import order.
|