fix(robots): don't patch robotparser on Python 3.14+ - #2278
Conversation
The wildcard monkey patch in utils.py overrides RuleLine.applies_to
unconditionally. Python 3.14 rewrote urllib.robotparser with native
wildcard, '$' and RFC 9309 longest-match support, where applies_to
returns the match *length* used to rank competing rules. The patch
returns a bool, so every wildcard rule collapses to the lowest
priority and Allow: overrides stop working:
User-agent: *
Disallow: /
Allow: /public/*.html
denied /public/a.html on 3.14. Gate the patch to Python < 3.14, where
robotparser has no wildcard support and still needs it.
Follow-up to #2229, which fixed the 'Disallow: /*?' half of #2225.
Refs #2225
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Kept this separate from #2229 instead of rolling it in — it's really a different bug that just happens to sit in the same few lines. #2229 fixed the rule text; this one is about the patch itself doing the wrong thing on 3.14. If you're on 3.14, this is the one to look at. The stdlib parser there is already RFC 9309 compliant, so we were overriding good behavior with worse. Nothing changes below 3.14. Thanks @Nalhin for #2229 — the |
Review follow-up on the previous commit, which gated the RuleLine
monkey patch but left _preserve_bare_query running on every Python.
On 3.14 that rewrite is not just unnecessary, it is wrong. The stdlib
ranks rules by match length, and '/*?*' matches to end of string, so
it outranks a narrower competing Allow:
User-agent: *
Allow: /*?q=
Disallow: /*?
/search?q=1 is allowed by the stdlib and denied after the rewrite, so
Crawl4AI skipped pages robots.txt permits. Gate the call with the same
sys.version_info < (3, 14) as the patch, and say so in the docstring,
which claimed the two forms were always equivalent.
Also move the RuleLine import inside the branch that uses it and drop
a duplicate 'import re'.
Refs #2225
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Both findings were right — fixed in 7e17809. 1 (medium). Confirmed before fixing, on 3.14.7: with Worth noting the split is real, not something to delete: on 3.12 the rewrite gives the right answers ( 2 (low). Done — Added two tests. Suite: 32 passed / 3 skipped on 3.14.7, 29 passed / 6 skipped on 3.12.9, full |
Summary
Follow-up to #2229, which fixed the
Disallow: /*?half of #2225. This fixes two more bugs in the same block of code, both of which only bite on Python 3.14+.Python 3.14 rewrote
urllib.robotparser: native wildcards,$, and RFC 9309 longest-match ranking. Crawl4AI carries two workarounds for the old parser, and on 3.14 each one now makes correct stdlib behaviour worse.1. The wildcard monkey patch flattens rule ranking
crawl4ai/utils.pyreplacesRuleLine.applies_tounconditionally. On 3.14 that method returns the match length, used to rank competing rules; our patch returns abool, soTrue == 1and every wildcard rule drops to the lowest possible priority./public/a.html/private/a.html2. The bare-
?rewrite outranks a narrowerAllow:_preserve_bare_queryrewrites/*?to/*?*so the?survives path normalization. Below 3.14 that is exactly right and still needed. On 3.14 it is wrong:/*?*matches to end of string, so it now outranks a narrowerAllow:that the raw/*?would have lost to./search?q=1/search?x=1Net effect of both: on 3.14, Crawl4AI skipped pages robots.txt explicitly permits.
The change
Gate both workarounds behind
sys.version_info < (3, 14). Below 3.14 nothing changes — the old parser has no wildcard support and does drop the?, so both are still required. From 3.14 the stdlib is already correct and we get out of its way. The matcher itself is untouched.Also: moved the
RuleLineimport into the branch that uses it, and dropped a duplicateimport re.Tests
Five tests in
tests/unit/test_robots_query_rules.py:test_wildcard_patch_is_scoped_to_old_pythons— the patch is installed exactly where neededtest_wildcard_rules_work_on_every_python—Disallow: /*.phpstill works after the gatetest_allow_overrides_broad_disallow— regression for bug 1 (3.14+)test_query_allow_outranks_bare_query_disallow— regression for bug 2 (3.14+)test_query_allow_still_wins_below_py314— same rules, same answers below 3.14, via the rewritetest_robots_query_rules.py, Python 3.14.7tests/unit, 3.12.9pypdfnot installed)tests/general/test_robot_parser.pyfails ondevelopas well — itsassert duration < 0.03is timing- and network-sensitive. Unrelated to this change.Not fixed here
On Python <= 3.13 the
$end-anchor (Disallow: /*.pdf$) still does not work:quote()mangles the$before the patch sees it. Pre-existing, untouched by this PR, and fixed by the stdlib from 3.14 on. Happy to do it separately.Refs #2225