From 84cfe690472749a4db567355bac4827924120175 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Sat, 25 Jul 2026 12:30:15 -0700 Subject: [PATCH] Support negative indexes in the :eq() selector `:eq(-1)` silently selected nothing instead of the last element: `xpath_eq_function` always emitted `position() = value + 1`, so a negative argument produced a position that can never match. jQuery counts a negative :eq() index back from the end, and pyquery's own `.eq(-1)` method already does. Negative values now translate to `position() = last()-n`, leaving the non-negative path byte-identical. --- pyquery/cssselectpatch.py | 12 +++++++++++- tests/test_pyquery.py | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pyquery/cssselectpatch.py b/pyquery/cssselectpatch.py index 1af5716..dd5c102 100644 --- a/pyquery/cssselectpatch.py +++ b/pyquery/cssselectpatch.py @@ -375,6 +375,11 @@ def xpath_eq_function(self, xpath, function): >>> d('h1:eq(1)') [] + A negative index counts back from the last element:: + + >>> d('h1:eq(-1)') + [] + .. """ if function.argument_types() != ['NUMBER']: @@ -382,7 +387,12 @@ def xpath_eq_function(self, xpath, function): "Expected a single integer for :eq(), got %r" % ( function.arguments,)) value = int(function.arguments[0].value) - xpath.add_post_condition('position() = %s' % (value + 1)) + if value < 0: + xpath.add_post_condition( + 'position() = last()%s' % ( + '%+d' % (value + 1) if value != -1 else '')) + else: + xpath.add_post_condition('position() = %s' % (value + 1)) return xpath def xpath_gt_function(self, xpath, function): diff --git a/tests/test_pyquery.py b/tests/test_pyquery.py index 7adf243..1405f06 100644 --- a/tests/test_pyquery.py +++ b/tests/test_pyquery.py @@ -219,6 +219,13 @@ def test_pseudo_classes(self): assert len(e(":empty")) == 1 assert len(e(":contains('Heading')")) == 8 + def test_eq_negative_index(self): + e = self.klass(self.html) + self.assertEqual(e('div:eq(-1)').text(), 'node3') + self.assertEqual(e('div:eq(-2)').text(), 'node2') + self.assertEqual(e('div:eq(-3)').text(), 'node1') + self.assertEqual(e('div:eq(-4)').text(), '') + def test_on_the_fly_dom_creation(self): e = self.klass(self.html) assert e('

Hello world

').text() == 'Hello world'