Skip to content

Commit 8adec8b

Browse files
Add modules= config option for ResourcesBot. (#127)
Allow default post-processing modules to be set in config.ini while keeping -m as a command-line override. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 67b0c04 commit 8adec8b

4 files changed

Lines changed: 112 additions & 15 deletions

File tree

config.example.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ username = ResourcesBot
5656
password = MySecretPassword
5757

5858
# resourcesbot writes language reports to [Paths:languagereports]
59+
# Post-processing modules to run (abbreviations, space-separated).
60+
# Use "all" to run every module (default). Overridden by -m on the command line.
61+
# Run `python3 resourcesbot.py -h` to see available module abbreviations.
62+
modules = all
5963
# Optionally: log to files (will be relative to path defined in [Paths:logs] )
6064
# Three different verbosity levels (warning, info, debug)
6165
logfile = resourcesbot.log

pywikitools/resourcesbot/bot.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,43 @@ def load_module(module_name: str) -> Callable:
5757
raise RuntimeError(f"Couldn't load module {module_name}. Giving up")
5858

5959

60+
def build_module_choices() -> Dict[str, str]:
61+
"""Return module abbreviations mapped to full module names."""
62+
modules: Dict[str, str] = {}
63+
for selected_module in AVAILABLE_MODULES:
64+
module = load_module(selected_module)
65+
modules[module.abbreviation()] = selected_module
66+
return modules
67+
68+
69+
def resolve_run_modules(
70+
modules: Dict[str, str],
71+
config: ConfigParser,
72+
cli_modules: Optional[List[str]] = None,
73+
) -> list[str]:
74+
"""
75+
Resolve which post-processing modules to run.
76+
77+
Command-line ``-m`` overrides ``[resourcesbot] modules`` in config.ini.
78+
"""
79+
if cli_modules is not None:
80+
return [modules[abbr] for abbr in cli_modules]
81+
82+
config_value = config.get("resourcesbot", "modules", fallback="all").strip()
83+
if config_value.lower() == "all":
84+
return AVAILABLE_MODULES
85+
86+
selected = config_value.split()
87+
unknown = [abbr for abbr in selected if abbr not in modules]
88+
if unknown:
89+
available = ", ".join(sorted(modules.keys()))
90+
raise ValueError(
91+
"Unknown module(s) in config.ini [resourcesbot] modules: "
92+
f"{', '.join(unknown)}. Available: {available}"
93+
)
94+
return [modules[abbr] for abbr in selected]
95+
96+
6097
class ResourcesBot:
6198
"""Contains all the logic of our bot"""
6299

pywikitools/test/test_resourcesbot.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414

1515
import pywikibot
1616

17-
from pywikitools.resourcesbot.bot import ResourcesBot, load_module
17+
from pywikitools.resourcesbot.bot import (
18+
AVAILABLE_MODULES,
19+
ResourcesBot,
20+
build_module_choices,
21+
load_module,
22+
resolve_run_modules,
23+
)
1824
from pywikitools.resourcesbot.data_structures import TranslationProgress, WorksheetInfo
1925
from pywikitools.test.test_data_structures import TEST_PROGRESS, TEST_TIME, TEST_URL
2026

@@ -341,5 +347,53 @@ def factory(*args, **kwargs):
341347
# TODO: test_run_with_limit_lang
342348

343349

350+
class TestResolveRunModules(unittest.TestCase):
351+
@classmethod
352+
def setUpClass(cls):
353+
cls.modules = build_module_choices()
354+
355+
def _config_with_modules(self, modules_value: str) -> ConfigParser:
356+
config = ConfigParser()
357+
config.read_dict(
358+
{
359+
"resourcesbot": {
360+
"site": "test",
361+
"username": "TestBotName",
362+
"modules": modules_value,
363+
}
364+
}
365+
)
366+
return config
367+
368+
def test_default_is_all_modules(self):
369+
config = ConfigParser()
370+
config.read_dict({"resourcesbot": {"site": "test", "username": "TestBotName"}})
371+
self.assertEqual(resolve_run_modules(self.modules, config), AVAILABLE_MODULES)
372+
373+
def test_config_all(self):
374+
config = self._config_with_modules("all")
375+
self.assertEqual(resolve_run_modules(self.modules, config), AVAILABLE_MODULES)
376+
377+
def test_config_specific_modules(self):
378+
config = self._config_with_modules("list report")
379+
self.assertEqual(
380+
resolve_run_modules(self.modules, config),
381+
["write_lists", "write_report"],
382+
)
383+
384+
def test_cli_overrides_config(self):
385+
config = self._config_with_modules("list")
386+
self.assertEqual(
387+
resolve_run_modules(self.modules, config, cli_modules=["report"]),
388+
["write_report"],
389+
)
390+
391+
def test_config_unknown_module_raises(self):
392+
config = self._config_with_modules("list unknown")
393+
with self.assertRaises(ValueError) as context:
394+
resolve_run_modules(self.modules, config)
395+
self.assertIn("unknown", str(context.exception))
396+
397+
344398
if __name__ == "__main__":
345399
unittest.main()

resourcesbot.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,14 @@
6565
import sys
6666
import traceback
6767
from configparser import ConfigParser
68-
from typing import Dict, List
68+
from typing import List
6969

70-
from pywikitools.resourcesbot.bot import AVAILABLE_MODULES, ResourcesBot, load_module
70+
from pywikitools.resourcesbot.bot import (
71+
ResourcesBot,
72+
build_module_choices,
73+
load_module,
74+
resolve_run_modules,
75+
)
7176

7277

7378
def parse_arguments() -> ResourcesBot:
@@ -85,16 +90,17 @@ def parse_arguments() -> ResourcesBot:
8590

8691
log_levels: List[str] = ["debug", "info", "warning", "error"]
8792
rewrite_options: List[str] = ["all", "json", "summary"]
88-
modules: Dict[str, str] = {} # abbreviation -> full name
93+
modules = build_module_choices()
8994
modules_help = "Select the modules to be run. Available options are:\n"
90-
# Read module information from the module classes
91-
for selected_module in AVAILABLE_MODULES:
95+
for abbr, selected_module in modules.items():
9296
module = load_module(selected_module)
93-
modules_help += f" - {module.abbreviation()}: {module.help_summary()}\n"
94-
modules[module.abbreviation()] = selected_module
97+
modules_help += f" - {abbr}: {module.help_summary()}\n"
9598
if module.can_be_rewritten():
96-
rewrite_options.append(module.abbreviation())
97-
modules_help += "Default: run all modules"
99+
rewrite_options.append(abbr)
100+
modules_help += (
101+
"Default: all modules, or set [resourcesbot] modules= in config.ini "
102+
"(overridden by -m)"
103+
)
98104

99105
parser.add_argument(
100106
"--read-from-cache",
@@ -133,11 +139,7 @@ def parse_arguments() -> ResourcesBot:
133139
assert isinstance(numeric_level, int)
134140
set_loglevel(config, numeric_level)
135141

136-
# Map abbreviations to full module names
137-
if args.m is None:
138-
run_modules = AVAILABLE_MODULES
139-
else:
140-
run_modules = [modules[abbr] for abbr in args.m]
142+
run_modules = resolve_run_modules(modules, config, args.m)
141143

142144
return ResourcesBot(
143145
config=config,

0 commit comments

Comments
 (0)