diff --git a/cssselect/xpath.py b/cssselect/xpath.py index c46c500..5f24e33 100644 --- a/cssselect/xpath.py +++ b/cssselect/xpath.py @@ -329,12 +329,18 @@ def _xpath_match_condition(self, selector: Tree) -> str | None: axes for combinators. """ if isinstance(selector, CombinedSelector): - axis = self._reverse_combinator_mapping[selector.combinator] - left = self._xpath_match_condition(selector.selector) - if left is not None: - axis = f"{axis}[{left}]" + steps = [] + left: Tree = selector + while isinstance(left, CombinedSelector): + axis = self._reverse_combinator_mapping[left.combinator] + left = left.selector + condition = self._xpath_match_condition( + left.subselector if isinstance(left, CombinedSelector) else left + ) + steps.append(axis if condition is None else f"{axis}[{condition}]") + path = "/".join(steps) right = self._xpath_match_condition(selector.subselector) - return axis if right is None else f"({right}) and {axis}" + return path if right is None else f"({right}) and {path}" sub_xpath = self.xpath(selector) sub_xpath.add_name_test() return sub_xpath.condition or None diff --git a/tests/test_cssselect.py b/tests/test_cssselect.py index 266bbca..2a65b1f 100644 --- a/tests/test_cssselect.py +++ b/tests/test_cssselect.py @@ -892,7 +892,14 @@ def xpath(css: str) -> str: "e[not((self::b) and preceding-sibling::*[self::a])]" ) assert xpath("e:not(a > b > c)") == ( - "e[not((self::c) and parent::*[(self::b) and parent::*[self::a]])]" + "e[not((self::c) and parent::*[self::b]/parent::*[self::a])]" + ) + assert xpath("e:not(a b ~ c + d > f)") == ( + "e[not((self::f) and parent::*[self::d]/preceding-sibling::*[1][self::c]" + "/preceding-sibling::*[self::b]/ancestor::*[self::a])]" + ) + assert xpath("e:not(a * * f)") == ( + "e[not((self::f) and ancestor::*/ancestor::*/ancestor::*[self::a])]" ) assert xpath("e:not(.a > #b)") == ( "e[not((@id = 'b') and parent::*[@class and contains(@class, 'a') and contains(" @@ -1760,6 +1767,8 @@ def pcss(main: str, *selectors: str, **kwargs: bool) -> list[str]: "seventh-li", ] assert pcss("li:not(#second-li ~ li)") == ["first-li", "second-li"] + assert pcss("li:not(div > ol li + li ~ li)") == ["first-li", "second-li"] + assert pcss("div:not(body > * * *)") == ["outer-div", "foobar-div"] assert pcss("li:has(div):nth-of-type(2)") == ["second-li"] assert pcss("li:has(div):first-of-type") == [] assert pcss("ol:has(li):first-of-type") == ["first-ol"]