Skip to content

Reject :scope after a comma in :has() arguments - #210

Merged
wRAR merged 1 commit into
scrapy:masterfrom
itecz26:has-scope-after-comma
Oct 20, 2026
Merged

wRAR merged 1 commit into
scrapy:masterfrom
itecz26:has-scope-after-comma

Conversation

@itecz26

@itecz26 itecz26 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

:scope after a comma inside :has() is accepted and mistranslated

parse_simple_selector() only accepts :scope when the token before its compound selector is the start of the input or a comma. #168 made :is()/:where()/:matches() pass inside_selector_list because a comma there separates arguments, but parse_relative_selector() (the :has() argument list) calls parse_simple_selector() with no flag, so the result depends on the argument's position:

>>> GenericTranslator().css_to_xpath("a:has(:scope)")
SelectorSyntaxError: Got pseudo-class ":scope" not at the start of a selector
>>> GenericTranslator().css_to_xpath("a:has(b, :scope)")
'descendant-or-self::a[(descendant::b) or (descendant::*[position() = 1])]'

The second XPath matches every <a> that has any child element, so a selector that should be rejected matches far more than its author wrote. With the fix, each relative selector in :has() is parsed with a new inside_relative_selector flag and :scope raises the same SelectorSyntaxError it already raises in :has(:scope) and :is(a, :scope). Other :has() arguments, and :scope at the start of a top-level selector (a, :scope > b), are unchanged.

The :scope check treats a preceding comma as the start of a new
selector, but inside :has() a comma separates relative selectors.
:has(:scope) was rejected while a:has(b, :scope) was accepted and
translated to "any descendant element", like :is() before scrapy#168.
@codecov

codecov Bot commented Oct 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.80%. Comparing base (a18ab0e) to head (f9ece4a).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #210   +/-   ##
=======================================
  Coverage   99.80%   99.80%           
=======================================
  Files           3        3           
  Lines        1002     1002           
  Branches      167      167           
=======================================
  Hits         1000     1000           
  Misses          1        1           
  Partials        1        1           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wRAR
wRAR merged commit 3069b6d into scrapy:master Oct 20, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants