Repository navigation
Conversation
- dumps.wikimedia.org now answers requests with the default `Python-urllib/3.x` user agent with HTTP 403. Send a descriptive `wikimapper/<version> (<url>)` user agent, set per request instead of via `install_opener`, which would change the requests of the whole process. The rest behaves like `urlretrieve` (temporary file, progress hook, `ContentTooShortError` on incomplete downloads). - `urljoin` dropped the last path segment of mirrors without a trailing slash, e.g. `https://mirror.accum.se/mirror/wikimedia.org/dumps` turned into `.../wikimedia.org/barwiki/...` (HTTP 404). - Use a 60 s timeout per network operation; a stalled server made the download (and the test suite) hang forever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since 2025, the Wikimedia SQL dumps put every row of an INSERT statement on its own line instead of on the `INSERT INTO ... VALUES` line. The old parser only looked at the INSERT lines, so `create_index` finished without an error but created an index with 0 rows. Rows are now matched with regular expressions on their leading columns, which works for both layouts. Only the rows that are needed match (namespace 0, `wikibase_item`), and the result is the same index as before: same table, same indexes, same redirect handling. Also check that all three dumps exist before deleting an existing index, and raise an error if no pages could be parsed at all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`test_wikipedia_id_to_title` was defined twice, so pytest only collected the second definition and the 15 cases of the first one never ran. Rename the second one to `test_title_to_wikipedia_id`, which is the method it tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The code uses f-strings, so it needs Python 3.6, not 3.5. - `ubuntu-latest` is Ubuntu 24.04, for which `actions/setup-python` has no Python 3.7, so the CI job for 3.7 cannot start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Development: `--no-site-packages` was removed from virtualenv 20; `tox` has no configuration in this repository and runs no tests. - CLI help: Python 3.10+ prints `options:` instead of `optional arguments:`. - FAQ: describe the new parser; add the index creation time for a current English dump. - Makefile: remove the `html` target, there is no `docs` folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe the goal (the `mapping` table and how WikiMapper uses it), the input dumps with real example rows, both INSERT layouts and the four steps of the index creation. Explain every row pattern piece by piece, what each function yields and why, and each step of `create_index`. Only docstrings and comments change; the code is identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
wikimapper no longer works with the current Wikimedia dumps:
wikimapper downloadfails withHTTP Error 403: Forbidden, because dumps.wikimedia.org rejects the defaultPython-urllib/3.xuser agent.wikimapper createsilently creates an empty index. Since 2025 the SQL dumps put every row on its own line, and the old parser only read theINSERT INTOline. With the current English dump it finishes with exit code 0 and 0 rows.This PR fixes both. The patched
processor.pyparses both the old dump format (all rows on theINSERT INTO ... VALUESline) and the new format (one row per line), with the same code path. It produces the same index as before: same table, same indexes, same redirect handling.Every change in this PR was reproduced first with real dumps or a real run (output below). Things that turned out not to be a problem were left unchanged; see Investigated, but not changed.
Results with the current English Wikipedia dump
Dumps:
enwiki-latest-{page,page_props,redirect}.sql.gzfrom October 2026 (2.4 GB, 484 MB and 188 MB).masterwikimapper download enwiki-latestHTTPError: HTTP Error 403: Forbiddenwikimapper create enwiki-latestcreate(ASUS ROG Zephyrus G14, GA403UV)wikimapper title2id index_enwiki-latest.db GermanyQ183The index was built twice, with two stages of this branch, and both builds were identical row by row (0 differing rows in either direction).
The dump format change
masterlooks for the rows on theINSERTline itself:Old format (what
masterexpects): all rows of anINSERTstatement on one line.New format (current dumps):
INSERT INTO ... VALUESalone on a line, then one row per line.On the new format,
find("VALUES ")finds nothing on theINSERTline (it ends withVALUES\n), and the row lines don't start withINSERT INTO`, so they are skipped. Nothing is parsed and no error is raised.Changes
1.
wikimapper/processor.py: parse both dump formatsThe rows are matched with regular expressions on their leading columns. That works the same whether a line holds one row or thousands. Only the rows that are needed match at all: namespace 0 for pages and redirects,
wikibase_itemfor page properties. Everything else is skipped by the regex engine.create_indexkeeps its signature and the database layout. The parsed rows go straight intoexecutemany, and redirects are resolved in SQL, with the same rule as before (a redirect gets its target's Wikidata ID if the target has one):The file decoding is unchanged:
pageis decoded strictly, andpage_propsandredirectwitherrors="ignore"(#12). In the current English dump,pageandredirectcontain no invalid UTF-8, whilepage_propshas 74,333 lines with binary (gzip-compressedtemplatedata) values.The
csv/ctypescode is gone, and with it thecsv.field_size_limitworkaround for #7.Both formats produce the same index. I rewrote the current barwiki dumps into the old one-line format and built the index from both:
master, old formatFor simplewiki (400,693 pages) in the old format, the parsed rows are identical to those of
master's parser.Two failure modes that were silent before now raise an error. Both were reproduced with real barwiki dumps:
A missing dump file:
masterdeleted the existing index first and only failed at the missing redirect dump, after the pages and page properties were already in. A good index (44,273 mapped) was replaced by a broken one: 27,312 mapped, redirects not resolved (Brezel→None), noidx_wikidata_idindex. The three dumps are now checked before anything is deleted:No pages parsed: this raises
ValueError("No pages could be parsed from [...], the dump format may have changed")instead of silently writing an empty index.Parsing speed
Parsing time added on top of reading and decompressing the dump, full English dump (66 M
pagerows, 46 Mpage_propsrows, 15.5 Mredirectrows). All variants produce exactly the same rows:csvmodulestr.find)pagepage_propsredirectThe
csvand string-scanning variants only handle one row per line. For the old format (all rows on one line),pagein simplewiki: this PR 0.37 s,master'scsvparser 2.98 s, string scanning 4.23 s. Most of the remaining build time is resolving the redirects in SQLite (about 300 of the 481 s).2.
wikimapper/download.py: User-Agent, mirror URLs, timeoutUser-Agent (HTTP 403). The Wikimedia User-Agent policy blocks generic user agents:
urlretrievecannot set headers for a single request. Installing a global opener (urllib.request.install_opener) would work, but I checked it: it replaced the opener of the program using wikimapper, so that program's own later requests lost their headers. So the request is sent with its own header, and the rest is whaturlretrievedoes: 8 KiB blocks, the same progress hook, a temporary file that is only moved to the target when complete, andContentTooShortErrorfor incomplete downloads.Mirror URLs with a path.
urljoindrops the last path segment of a mirror URL without a trailing slash. With a real mirror:Timeout. During testing, a download from dumps.wikimedia.org hung: the connection was established and the temporary file was created, but it stayed at 0 bytes for more than 10 minutes, because
urlopenhad no timeout.urlopennow getstimeout=60. That applies to each network operation, not to the whole download, so large dumps are not affected.3. Tests
test_create_index_parses_dump_layouts[one row per line](…)inside a property valuetest_create_index_parses_dump_layouts[many rows per line]test_create_index_fails_for_empty_dumpValueErrortest_create_index_fails_for_missing_dumpFileNotFoundError, existing index untouchedtest_download_request(4 cases)test_download_times_out_on_stalled_serversocket.timeout, no file left behindThese tests need no network access. Run against
master, the new-format, empty-dump and missing-dump tests fail and the download tests can't even import (USER_AGENTdoesn't exist there). The old-format test passes onmastertoo, as it should. In addition,tests/test_mapper.pydefinedtest_wikipedia_id_to_titletwice, so the 15 cases of the first definition never ran. The second one is renamed totest_title_to_wikipedia_id.4. Packaging, CI, docs
setup.py:python_requires=">=3.6"instead of">=3.5.0", and the 3.5 classifier is removed. The code uses f-strings (cli.py); vermin reports a minimum of 3.6, for bothmasterand this PR..github/workflows/run_tests.yml: Python 3.7 is removed from the matrix.ubuntu-latestis Ubuntu 24.04 (runner images), and the actions/python-versions manifest has no 3.7 build for it, so that job cannot start. 3.8 and 3.9 are available on both Ubuntu 24.04 and Windows.README.rst:virtualenv … --no-site-packages: the option was removed in virtualenv 20 (virtualenv: error: unrecognized arguments: --no-site-packages).toxis removed: there is notox.ini/setup.cfg/pyproject.toml, sotoxruns 0 tests and still reportscongratulations :).options:, as printed by Python 3.10+, instead ofoptional arguments:.Makefile: thehtmltarget is removed; it runscd docs && make html, and there is nodocsfolder.Investigated, but not changed
These looked suspicious, but checking them with real data showed no problem, so they are left as they are:
rd_interwikiset, e.g.'de') are looked up as local titles, as before. The current English dump has none of them (all 15,551,857 rows have an emptyrd_interwiki), and in barwiki (52) and simplewiki (141) none changes the index.masteralready raisesContentTooShortErrorand never moves an incomplete file to the target (tested with a server that closes the connection early). This PR keeps that behavior.setup.pywarnings (test_suite,tests_require, license classifier) don't affect the build, andfind_packages(exclude="tests")already produces a wheel without the tests.Germany→Q183,wikipedia_id_to_title(3342)→Bundesrepublik_Deutschland, …). The one exception isid_to_titles("Q183"): Wikipedia has changed since then, soLand_der_Dichter_und_Denkernow redirects toCulture_of_GermanyandJermanis its own article.Notes for the maintainer
publish_to_pypi.ymluses Python 3.8 onubuntu-latestandpypa/gh-action-pypi-publish@master. I didn't change it, because I can't test the release pipeline.0.2.1-devis normalized to0.2.1.dev0by current setuptools (just a warning).🤖 Generated with Claude Code