7d74be68aa
CI / lint (pull_request) Successful in 14s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 28s
CI / typecheck (pull_request) Successful in 33s
CI / build (pull_request) Successful in 14s
CI / security (pull_request) Successful in 35s
CI / unit_tests (pull_request) Successful in 2m52s
CI / docker (pull_request) Successful in 39s
CI / integration_tests (pull_request) Successful in 3m53s
CI / coverage (pull_request) Successful in 3m42s
CI / lint (push) Successful in 13s
CI / quality (push) Successful in 16s
CI / build (push) Successful in 16s
CI / security (push) Successful in 29s
CI / typecheck (push) Successful in 32s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 1m53s
CI / docker (push) Successful in 53s
CI / integration_tests (push) Successful in 2m49s
CI / coverage (push) Successful in 3m50s
CI / benchmark-publish (push) Successful in 13m7s
CI / benchmark-regression (pull_request) Successful in 28m12s
Add --project flag to config set, config get, and config list CLI commands, enabling per-project configuration overrides stored under [project."<name>"] TOML tables in the global config file. Project-scoped resolution slots between environment variable and global levels in the ConfigService resolution chain. config list --project shows only overrides for the named project with source annotations. Implementation: - ConfigService: add set_project_value() and get_project_overrides() methods for TOML-backed project-scoped persistence and retrieval - CLI config commands: wire --project flag through set, get, and list subcommands; project-scoped list filters to overrides only - Database persistence: project-scoped config stored as alternative backend for projects not using TOML - Documentation: update docs/reference/config_resolution.md with project-scopable key lists, CLI examples, precedence diagram, and non-scopable key rejection behavior Tests: - Behave: 12 BDD scenarios in features/config_project_scope.feature covering set/get/list, precedence over global defaults, and non-scopable key rejection - Robot: 5 integration smoke tests in robot/config_project_scope.robot for end-to-end project-scoped round-trip verification - ASV: benchmarks/config_project_scope_bench.py measuring resolution overhead with project scope active All nox quality gates pass: lint, typecheck, unit_tests (7522 scenarios), integration_tests (Config Project Scope suite passed), and coverage at 98% line rate (threshold 97%). Closes #259
101 lines
3.5 KiB
Python
101 lines
3.5 KiB
Python
"""ASV benchmarks for project-scoped config resolution overhead.
|
|
|
|
Measures the performance impact of project-scoped resolution compared
|
|
to global-only resolution, including:
|
|
- Resolution with project scope active
|
|
- Resolution with project scope inactive (no project name)
|
|
- get_project_overrides() lookup
|
|
- set_project_value() write
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
# Ensure the local *source* tree is importable even when ASV has an
|
|
# older build of the package installed.
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from cleveragents.application.services.config_service import ( # noqa: E402
|
|
ConfigService,
|
|
)
|
|
|
|
|
|
class ProjectScopeResolutionTimeSuite:
|
|
"""Benchmark timing for project-scoped config resolution."""
|
|
|
|
def setup(self) -> None:
|
|
self._tmpdir = Path(tempfile.mkdtemp())
|
|
self._service = ConfigService(
|
|
config_dir=self._tmpdir,
|
|
config_path=self._tmpdir / "config.toml",
|
|
)
|
|
# Pre-populate some project-scoped values
|
|
self._service.set_project_value(
|
|
"bench-project", "core.automation-profile", "trusted"
|
|
)
|
|
self._service.set_project_value("bench-project", "plan.concurrency", "16")
|
|
|
|
def teardown(self) -> None:
|
|
shutil.rmtree(str(self._tmpdir), ignore_errors=True)
|
|
|
|
def time_resolve_with_project_scope(self) -> None:
|
|
"""Resolve a key with project scope active (project override exists)."""
|
|
self._service.resolve("core.automation-profile", project_name="bench-project")
|
|
|
|
def time_resolve_without_project_scope(self) -> None:
|
|
"""Resolve a key without project scope (baseline)."""
|
|
self._service.resolve("core.automation-profile")
|
|
|
|
def time_resolve_project_scope_no_override(self) -> None:
|
|
"""Resolve a key with project scope but no override for that key."""
|
|
self._service.resolve("core.log.level", project_name="bench-project")
|
|
|
|
def time_get_project_overrides(self) -> None:
|
|
"""Look up all overrides for a project."""
|
|
self._service.get_project_overrides("bench-project")
|
|
|
|
def time_set_project_value(self) -> None:
|
|
"""Write a project-scoped value."""
|
|
self._service.set_project_value("bench-project", "plan.concurrency", "32")
|
|
|
|
def time_resolve_all_with_project(self) -> None:
|
|
"""Resolve all keys with a project name active."""
|
|
self._service.resolve_all(project_name="bench-project")
|
|
|
|
|
|
class ProjectScopeResolutionMemSuite:
|
|
"""Benchmark memory consumption for project-scoped operations."""
|
|
|
|
def setup(self) -> None:
|
|
self._tmpdir = Path(tempfile.mkdtemp())
|
|
self._service = ConfigService(
|
|
config_dir=self._tmpdir,
|
|
config_path=self._tmpdir / "config.toml",
|
|
)
|
|
self._service.set_project_value(
|
|
"bench-project", "core.automation-profile", "trusted"
|
|
)
|
|
self._service.set_project_value("bench-project", "plan.concurrency", "16")
|
|
|
|
def teardown(self) -> None:
|
|
shutil.rmtree(str(self._tmpdir), ignore_errors=True)
|
|
|
|
def mem_resolve_all_with_project(self) -> dict:
|
|
"""Memory for resolving all keys with project scope."""
|
|
return self._service.resolve_all(project_name="bench-project")
|
|
|
|
def mem_get_project_overrides(self) -> dict:
|
|
"""Memory for get_project_overrides."""
|
|
return self._service.get_project_overrides("bench-project")
|