From 4111948b6009c306a6a0b56a787c6b48392ab88d Mon Sep 17 00:00:00 2001 From: sushant-suse Date: Tue, 17 Mar 2026 09:06:22 +0530 Subject: [PATCH 1/3] feat #190: provide global option -j/--workers Signed-off-by: sushant-suse --- src/docbuild/cli/cmd_cli.py | 10 ++++++++++ src/docbuild/cli/context.py | 4 ++-- tests/cli/test_worker_option.py | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/cli/test_worker_option.py diff --git a/src/docbuild/cli/cmd_cli.py b/src/docbuild/cli/cmd_cli.py index 6164cfb4..88aa70fe 100644 --- a/src/docbuild/cli/cmd_cli.py +++ b/src/docbuild/cli/cmd_cli.py @@ -96,6 +96,12 @@ def handle_validation_error( ) @click.option("-v", "--verbose", count=True, help="Increase verbosity") @click.option("--dry-run", is_flag=True, help="Run without making changes") +@click.option( + "-j", + "--workers", + "max_workers", + help="Maximum number of concurrent workers (integer, 'all', or 'all2').", +) @click.option( "--debug/--no-debug", default=False, @@ -137,6 +143,7 @@ def cli( debug: bool, app_config: Path, env_config: Path, + max_workers: str | None, **kwargs: dict, ) -> None: """Acts as a main entry point for CLI tool. @@ -177,6 +184,9 @@ def cli( raw_appconfig = cast(dict[str, Any], raw_appconfig) + if max_workers is not None: + raw_appconfig["max_workers"] = max_workers + try: context.appconfig = AppConfig.from_dict(raw_appconfig) except (ValueError, ValidationError) as e: diff --git a/src/docbuild/cli/context.py b/src/docbuild/cli/context.py index 4d26eccf..49f5dbda 100644 --- a/src/docbuild/cli/context.py +++ b/src/docbuild/cli/context.py @@ -2,8 +2,8 @@ from dataclasses import dataclass from pathlib import Path -from typing import Any +from ..models.config.app import AppConfig from ..models.config.env import EnvConfig from ..models.doctype import Doctype @@ -24,7 +24,7 @@ class DocBuildContext: appconfig_from_defaults: bool = False """If set, the app's config was loaded from defaults""" - appconfig: dict[str, Any] | None = None + appconfig: AppConfig | None = None """The accumulated content of all app config files""" envconfigfiles: tuple[str | Path, ...] | None = None diff --git a/tests/cli/test_worker_option.py b/tests/cli/test_worker_option.py new file mode 100644 index 00000000..2f54037c --- /dev/null +++ b/tests/cli/test_worker_option.py @@ -0,0 +1,27 @@ +from click.testing import CliRunner + +from docbuild.cli.cmd_cli import cli + + +def test_workers_option_integration(): + runner = CliRunner() + + # We use a command that exists but we don't care if it fails + # due to the directory permissions seen above. + # We are testing the CLI parsing logic here. + result = runner.invoke(cli, ["-j", "1", "config", "show"]) + + # If '-j' was invalid, exit code would be 2 (Click usage error) + # Since it's valid, it should proceed to validation logic (exit code 1 or 0) + assert result.exit_code != 2 + assert "no such option: -j" not in result.output + +def test_workers_option_invalid_value(): + runner = CliRunner() + + # Test an invalid string that should trigger our Pydantic ValueError + result = runner.invoke(cli, ["-j", "not-a-number", "config", "show"]) + + # This should trigger our 'handle_validation_error' logic + assert result.exit_code == 1 + assert "Invalid max_workers value" in result.output From 0c206c95eccd4836650b15bb545ce8b4eadb32bc Mon Sep 17 00:00:00 2001 From: sushant-suse Date: Wed, 18 Mar 2026 10:55:21 +0530 Subject: [PATCH 2/3] fix #190: addressed Tom's comments Signed-off-by: sushant-suse --- src/docbuild/cli/cmd_cli.py | 2 ++ src/docbuild/models/config/app.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/docbuild/cli/cmd_cli.py b/src/docbuild/cli/cmd_cli.py index 88aa70fe..ae127ec5 100644 --- a/src/docbuild/cli/cmd_cli.py +++ b/src/docbuild/cli/cmd_cli.py @@ -100,6 +100,8 @@ def handle_validation_error( "-j", "--workers", "max_workers", + default="half", + show_default=True, help="Maximum number of concurrent workers (integer, 'all', or 'all2').", ) @click.option( diff --git a/src/docbuild/models/config/app.py b/src/docbuild/models/config/app.py index 8025c877..4ffb9ae7 100644 --- a/src/docbuild/models/config/app.py +++ b/src/docbuild/models/config/app.py @@ -190,14 +190,14 @@ class AppConfig(BaseModel): ) # Added max_workers with support for ints and descriptive strings - max_workers: int | str = Field( - default="half", + max_workers: int = Field( + default="half", # type: ignore description="Max concurrent workers. Supports integers or 'all', 'all2'/'half'.", ) model_config = ConfigDict(extra="allow") - @field_validator("max_workers") + @field_validator("max_workers", mode="before") @classmethod def _resolve_worker_count(cls, v: int | str) -> int: """Resolve keywords 'all', 'half', 'all2' into concrete integers.""" From 7d32bb9ab19787fcd64c2f8de3d7369a9ad925dc Mon Sep 17 00:00:00 2001 From: sushant-suse Date: Wed, 18 Mar 2026 13:20:49 +0530 Subject: [PATCH 3/3] feat: added fragment file for issue #189 and #190 Signed-off-by: sushant-suse --- changelog.d/189.feature.rst | 1 + changelog.d/190.feature.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/189.feature.rst create mode 100644 changelog.d/190.feature.rst diff --git a/changelog.d/189.feature.rst b/changelog.d/189.feature.rst new file mode 100644 index 00000000..521a18fe --- /dev/null +++ b/changelog.d/189.feature.rst @@ -0,0 +1 @@ +Introduce the ``max_workers`` configuration key in the application config. This allows users to define concurrency limits using integers or hardware-aware keywords such as ``all``, ``half``, or ``all2``. \ No newline at end of file diff --git a/changelog.d/190.feature.rst b/changelog.d/190.feature.rst new file mode 100644 index 00000000..ba716243 --- /dev/null +++ b/changelog.d/190.feature.rst @@ -0,0 +1 @@ +Add a global CLI option ``-j``/``--workers`` to manage concurrency. This option overrides the configuration file and supports both explicit integer values and dynamic keywords (``all``, ``half``, ``all2``). \ No newline at end of file