Skip to content

Commit a048fdc

Browse files
committed
Add --simulate dry-run mode to ResourcesBot
Run resourcesbot.py directly for wiki previews without login, writes, git push, or local exports. Centralize page saves in pywikibot_io. Remove outdated pbw.py and user-config.py references
1 parent df3d144 commit a048fdc

21 files changed

Lines changed: 274 additions & 76 deletions

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333

3434
- name: Generate coverage report
3535
run: |
36-
coverage run --source=pywikitools/,pywikitools/correctbot/ --omit=pywikitools/user-config.py -m unittest discover -s pywikitools/test
36+
coverage run --source=pywikitools/,pywikitools/correctbot/ -m unittest discover -s pywikitools/test
3737
coverage xml
3838
3939
- name: Upload coverage to Codecov

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ format: ## auto-format code with ruff
4242
$(RUFF) format .
4343

4444
coverage: ## run tests with coverage report and open HTML in browser
45-
$(COVERAGE) run --source=pywikitools/,pywikitools/correctbot/ --omit=pywikitools/user-config.py -m unittest discover -s pywikitools/test
45+
$(COVERAGE) run --source=pywikitools/,pywikitools/correctbot/ -m unittest discover -s pywikitools/test
4646
$(COVERAGE) report -m
4747
$(COVERAGE) html
4848
$(BROWSER) htmlcov/index.html

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ set the logging level to INFO (default is WARN) by adding `-l info`.
8787
$ python path/to/script args
8888
```
8989
90+
Bots use `config.ini` for site and username settings and run directly with Python,
91+
not via pywikibot's `pwb.py` wrapper.
92+
9093
If you're not yet logged in, `pywikibot` will ask you for the password for the
9194
user you defined in `config.ini`. After successful login, the login cookie is
9295
stored in `pywikibot-[UserName].lwp` so you don't have to log in every time.

pyproject.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
line-length = 88
33
exclude = [
44
"env",
5-
"pywikitools/user-config.py",
6-
"pywikitools/correctbot/user-config.py",
75
]
86

97
[tool.ruff.format]

pywikitools/correctbot/bot.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ def save_to_mediawiki(self, results: List[CorrectionResult]) -> bool:
244244
"""
245245
Write corrections back to mediawiki
246246
247-
You should disable pywikibot throttling to avoid CorrectBot runs to take quite long:
248-
`put_throttle = 0` in user-config.py
247+
Throttling is disabled in __init__ via site.throttle.set_delays(...).
249248
250249
Returns:
251250
bool: Did we save any corrections to the mediawiki system?

pywikitools/family.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,22 @@
11
"""Family file for connecting to 4training.net with pywikibot
22
3-
This is intended to be used without using the traditional pywikibot approach
4-
of using user-config.py for configuration.
5-
Instead we use pywikibot short site creation (requires pywikibot >= 7.3)
6-
7-
However, if you still want to use the traditional approach, you could just take
8-
this file, rename it to 4training_family.py and put in the right folder
9-
(pywikibot families folder or some other folder that you specify with
10-
user_families_paths in user-config.py).
11-
12-
Then use a user-config.py with the following configuration to connect to test.4training.net:
13-
family = '4training'
14-
mylang = 'test'
15-
usernames['4training']['test'] = 'YourUserName'
3+
Bots connect via pywikibot short site creation (requires pywikibot >= 7.3) and
4+
config.ini for site/username settings — no user-config.py needed:
5+
6+
family = Family()
7+
self.site = pywikibot.Site(code='test', fam=family, user='username')
8+
9+
code can be
10+
'4training' => www.4training.net
11+
'test' => test.4training.net
12+
'local' => localhost:8082
1613
"""
1714

1815
import pywikibot
1916

2017

2118
class Family(pywikibot.family.SubdomainFamily):
22-
"""
23-
The family class for our 4training.net website
24-
for connecting to it via pywikibot short site creation (new feature of pywikibot 7.3):
25-
No cumbersome user-config.py is necessary anymore, instead we can write
26-
family = Family()
27-
self.site = pywikibot.Site(code='test', fam=family, user='username')
28-
code can be either '4training' or 'test' to connect to www.4training.net / test.4training.net
29-
"""
19+
"""The family class for our 4training.net website."""
3020

3121
name = "4training"
3222

pywikitools/pywikibot_io.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""Shared pywikibot helpers for pywikitools bots."""
2+
3+
import pywikibot
4+
5+
6+
def save_page(
7+
page: pywikibot.page.BasePage,
8+
new_text: str,
9+
summary: str,
10+
*,
11+
simulate: bool = False,
12+
) -> None:
13+
"""Assign new page text and save; in simulate mode show a pywikibot diff only."""
14+
if simulate:
15+
pywikibot.showDiff(page.text, new_text)
16+
return
17+
page.text = new_text
18+
page.save(summary)

pywikitools/resourcesbot/bot.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
WorksheetInfo,
2121
json_decode,
2222
)
23+
from pywikitools.pywikibot_io import save_page
2324
from pywikitools.resourcesbot.modules.post_processing import LanguagePostProcessor
2425
from pywikitools.resourcesbot.modules.write_summary import WriteSummary
2526

@@ -33,6 +34,10 @@
3334
"write_sidebar_messages",
3435
]
3536

37+
SIMULATE_SKIP_MODULES: Final[frozenset[str]] = frozenset(
38+
{"export_html", "export_pdf", "export_repository"}
39+
)
40+
3641

3742
def load_module(module_name: str) -> Callable:
3843
"""Load the post-processing module from modules/ and return it
@@ -62,6 +67,7 @@ def __init__(
6267
limit_to_lang: Optional[str] = None,
6368
modules: list[str] = AVAILABLE_MODULES,
6469
rewrite: Optional[str] = None,
70+
simulate: bool = False,
6571
):
6672
"""
6773
Args:
@@ -72,8 +78,13 @@ def __init__(
7278
limit processing to one language (string with a language code)
7379
modules:
7480
specify which post-processing modules should be executed
81+
simulate:
82+
dry-run: show pywikibot diffs, skip login, writes, and export modules
7583
"""
84+
self._simulate = simulate
7685
self.modules = modules
86+
if simulate:
87+
self.modules = [m for m in self.modules if m not in SIMULATE_SKIP_MODULES]
7788
# read-only list of download file types
7889
self._file_types: Final[List[str]] = ["pdf", "odt", "odg", "printPdf"]
7990
self._config = config
@@ -128,6 +139,18 @@ def __init__(
128139
self._changelog: Dict[str, ChangeLog] = {}
129140

130141
def run(self):
142+
previous_simulate = pywikibot.config.simulate
143+
if self._simulate:
144+
pywikibot.config.simulate = True
145+
self.logger.info(
146+
"Simulate mode: no MediaWiki writes, skipping export modules"
147+
)
148+
try:
149+
self._run()
150+
finally:
151+
pywikibot.config.simulate = previous_simulate
152+
153+
def _run(self):
131154
if self._read_from_cache:
132155
try:
133156
# List of languages to be read from cache
@@ -175,7 +198,9 @@ def run(self):
175198
# failed with a WARNING from pywikibot:
176199
# "No user is logged in on site 4training:en"-> better check and try
177200
# to log in if necessary
178-
if not self.site.logged_in():
201+
if self._simulate:
202+
self.logger.info("Simulate mode: skipping login")
203+
elif not self.site.logged_in():
179204
self.logger.info("We're not logged in. Trying to log in...")
180205
self.site.login()
181206
if not self.site.logged_in():
@@ -208,7 +233,10 @@ def run(self):
208233

209234
for selected_module in self.modules:
210235
module = load_module(selected_module)(
211-
self.fortraininglib, self._config, self.site
236+
self.fortraininglib,
237+
self._config,
238+
self.site,
239+
simulate=self._simulate,
212240
)
213241
for lang in self._result:
214242
module.run(
@@ -222,7 +250,7 @@ def run(self):
222250

223251
# Now run all GlobalPostProcessors
224252
if not self._limit_to_lang:
225-
write_summary = WriteSummary(self.site)
253+
write_summary = WriteSummary(self.site, simulate=self._simulate)
226254
write_summary.run(
227255
self._result,
228256
self._changelog,
@@ -457,8 +485,12 @@ def _sync_and_compare(self, language_info: LanguageInfo) -> ChangeLog:
457485
self.logger.warning(
458486
f"{page.full_url()} doesn't seem to exist yet. Creating..."
459487
)
460-
page.text = encoded_json
461-
page.save("Created JSON data structure")
488+
save_page(
489+
page,
490+
encoded_json,
491+
"Created JSON data structure",
492+
simulate=self._simulate,
493+
)
462494
rewrite_json = False
463495
else:
464496
# Load "old" data structure of this language (from previous resourcesbot run)
@@ -481,8 +513,12 @@ def _sync_and_compare(self, language_info: LanguageInfo) -> ChangeLog:
481513

482514
if rewrite_json:
483515
# Write the updated JSON structure
484-
page.text = encoded_json
485-
page.save("Updated JSON data structure")
516+
save_page(
517+
page,
518+
encoded_json,
519+
"Updated JSON data structure",
520+
simulate=self._simulate,
521+
)
486522
self.logger.info(f"Updated 4training:{lang}.json")
487523

488524
return changes
@@ -510,8 +546,9 @@ def _save_languages_list(self):
510546
# TODO compare language_list and json.loads(previous_json) to find out if a new
511547
# language was added
512548
if previous_json != encoded_json:
513-
page.text = encoded_json
514-
page.save("Updated list of languages")
549+
save_page(
550+
page, encoded_json, "Updated list of languages", simulate=self._simulate
551+
)
515552
self.logger.info("Updated 4training:languages.json")
516553

517554
def _save_number_of_languages(self):
@@ -546,8 +583,12 @@ def _save_number_of_languages(self):
546583

547584
if previous_number_of_languages != number_of_languages:
548585
try:
549-
page.text = number_of_languages
550-
page.save("Updated number of languages")
586+
save_page(
587+
page,
588+
str(number_of_languages),
589+
"Updated number of languages",
590+
simulate=self._simulate,
591+
)
551592
self.logger.info(
552593
f"Updated MediaWiki:Numberoflanguages to {number_of_languages}"
553594
)

pywikitools/resourcesbot/modules/consistency_checks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ def __init__(
4444
fortraininglib: ForTrainingLib,
4545
config: ConfigParser = None,
4646
site: pywikibot.site.APISite = None,
47+
*,
48+
simulate: bool = False,
4749
):
48-
super().__init__(fortraininglib, config, site)
50+
super().__init__(fortraininglib, config, site, simulate=simulate)
4951
self.logger = logging.getLogger(
5052
"pywikitools.resourcesbot.modules.consistency_checks"
5153
)

pywikitools/resourcesbot/modules/export_html.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ def __init__(
6161
fortraininglib: ForTrainingLib,
6262
config: ConfigParser,
6363
site: pywikibot.site.APISite,
64+
*,
65+
simulate: bool = False,
6466
):
65-
super().__init__(fortraininglib, config, site)
67+
super().__init__(fortraininglib, config, site, simulate=simulate)
6668
self._base_folder: str = self._config.get("Paths", "htmlexport", fallback="")
6769
self.logger: Final[logging.Logger] = logging.getLogger(
6870
"pywikitools.resourcesbot.modules.export_html"

0 commit comments

Comments
 (0)