Repository: scrapy/cssselect

Issue:
[bug] parse() fails if :scope present in second element of selector list
As of v1.1.0,  cssselect.parse() seems to have problems parsing the ":scope" psuedo-class if the input string is a selector list, and ":scope" occurs in any clause besides the first one.

Ones that successfully parse as expected: 
* ``parse(":scope > th")``
* ``parse(":scope > th, td")``
* ``parse(":scope > th, table > td")``

However, all of the following unexpectedly (at least to me) throw *SelectorSyntaxError('Got immediate child pseudo-element ":scope" not at the start of a selector')*:
* ``parse("th, :scope > td")``
* ``parse("table > th, :scope > td")``
* ``parse(":scope > th, :scope > td")``

(I'd submit a PR for this, but looking at the location of the error, I'm not familiar enough with the internals to suggest what the right thing to do is!).

Discussion:

Patch:
diff --git a/cssselect/parser.py b/cssselect/parser.py
index 97e146b..584dfea 100644
--- a/cssselect/parser.py
+++ b/cssselect/parser.py
@@ -625,6 +625,12 @@ def parse_simple_selector(stream, inside_negation=False):
                     if not (
                         len(stream.used) == 2
                         or (len(stream.used) == 3 and stream.used[0].type == "S")
+                        or (len(stream.used) >= 3 and stream.used[-3].is_delim(","))
+                        or (
+                            len(stream.used) >= 4
+                            and stream.used[-3].type == "S"
+                            and stream.used[-4].is_delim(",")
+                        )
                     ):
                         raise SelectorSyntaxError(
                             'Got immediate child pseudo-element ":scope" '


Test patch:
diff --git a/tests/test_cssselect.py b/tests/test_cssselect.py
index 589a081..693684b 100644
--- a/tests/test_cssselect.py
+++ b/tests/test_cssselect.py
@@ -230,6 +230,14 @@ class TestCssselect(unittest.TestCase):
             ("CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]", None),
             ("CombinedSelector[Element[foo] <followed> Element[bar]]", None),
         ]
+        assert parse_pseudo("foo bar, :scope > div") == [
+            ("CombinedSelector[Element[foo] <followed> Element[bar]]", None),
+            ("CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]", None),
+        ]
+        assert parse_pseudo("foo bar,:scope > div") == [
+            ("CombinedSelector[Element[foo] <followed> Element[bar]]", None),
+            ("CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]", None),
+        ]
         assert parse_pseudo("foo:before, bar, baz:after") == [
             ("Element[foo]", "before"),
             ("Element[bar]", None),