Anonymous avatar Anonymous committed 27a6952

overloaded the PseudoFunction class from cssselect

Comments (0)

Files changed (3)

pyquery/README.txt

     >>> p.text()
     'you know Python rocks'
 
-You can use some of the pseudo classes :first :last :even :odd that are
-available in jQuery but that are not standard in css::
+You can use some of the pseudo classes that are available in jQuery but that
+are not standard in css such as :first :last :even :odd :eq :lt :gt::
 
     >>> d('p:first')
-    [<p#hello.hello>]
-    >>> d('p:last')
-    [<p#test>]
-    >>> d('p:even')
-    [<p#hello.hello>]
-    >>> d('p:odd')
-    [<p#test>]
 
 
 Attributes

pyquery/pyquery.py

 # Copyright (C) 2008 - Olivier Lauzanne <olauzanne@gmail.com>
 #
 # Distributed under the BSD license, see LICENSE.txt
-from lxml.cssselect import css_to_xpath, Pseudo, XPathExpr, XPathExprOr
+from lxml.cssselect import css_to_xpath, Pseudo, XPathExpr, XPathExprOr, \
+                           Function
 from lxml import etree, cssselect
 from copy import deepcopy
 
 class JQueryPseudo(Pseudo):
     """This class is used to implement the css pseudo classes
-    (:checked, :first-child, ...)that are not defined in the css standard,
+    (:first, :last, ...) that are not defined in the css standard,
     but are defined in the jquery API.
     """
     def _xpath_first(self, xpath):
-        xpath.add_star_prefix()
-        xpath.add_name_test()
+        """Matches the first selected element.
+        """
         xpath.add_post_condition('position() = 1')
         return xpath
 
     def _xpath_last(self, xpath):
-        xpath.add_star_prefix()
-        xpath.add_name_test()
+        """Matches the last selected element.
+        """
         xpath.add_post_condition('position() = last()')
         return xpath
 
     def _xpath_even(self, xpath):
-        xpath.add_star_prefix()
-        xpath.add_name_test()
-
-        # the first element is 1 in xpath and 0 in python
+        """Matches even elements, zero-indexed.
+        """
+        # the first element is 1 in xpath and 0 in python and js
         xpath.add_post_condition('position() mod 2 = 1')
         return xpath
 
     def _xpath_odd(self, xpath):
-        xpath.add_star_prefix()
-        xpath.add_name_test()
+        """Matches odd elements, zero-indexed.
+        """
         xpath.add_post_condition('position() mod 2 = 0')
         return xpath
 
 cssselect.Pseudo = JQueryPseudo
 
+class JQueryFunction(Function):
+    """Represents selector:name(expr) that are present in JQuery but not in the
+    css standard.
+    """
+    def _xpath_eq(self, xpath, expr):
+        """Matches a single element by its index.
+        """
+        xpath.add_post_condition('position() = %s' % int(expr+1))
+        return xpath
+
+    def _xpath_gt(self, xpath, expr):
+        """Matches all elements with an index over the given one.
+        """
+        xpath.add_post_condition('position() > %s' % int(expr+1))
+        return xpath
+
+    def _xpath_lt(self, xpath, expr):
+        """Matches all elements with an index below the given one.
+        """
+        xpath.add_post_condition('position() < %s' % int(expr+1))
+        return xpath
+
+cssselect.Function = JQueryFunction
+
 class AdvancedXPathExpr(XPathExpr):
     def __init__(self, prefix=None, path=None, element='*', condition=None,
                  post_condition=None, star_prefix=False):
             self.post_condition = post_condition
 
     def __str__(self):
-        path = ''
-        if self.prefix is not None:
-            path += str(self.prefix)
-        if self.path is not None:
-            path += str(self.path)
-        path += str(self.element)
-        if self.condition:
-            path += '[%s]' % self.condition
+        path = XPathExpr.__str__(self)
         if self.post_condition:
             path = '(%s)[%s]' % (path, self.post_condition)
         return path
 
+    def join(self, combiner, other):
+        XPathExpr.join(self, combiner, other)
+        self.post_condition = other.post_condition
+
 cssselect.XPathExpr = AdvancedXPathExpr
 
 class AdvancedXPathExprOr(XPathExprOr):
 
 def selector_to_xpath(selector):
     """JQuery selector to xpath.
-    TODO: patch cssselect to add :first, :last, ...
     """
     selector = selector.replace('[@', '[')
     return css_to_xpath(selector)
     def map(self, func):
         """Returns a new PyQuery after transforming current items with func.
 
-        func should take two arguments - 'index' and 'element'.  Elements can 
+        func should take two arguments - 'index' and 'element'.  Elements can
         also be referred to as 'this' inside of func.
         """
         items = []
     return resp(environ, start_response)
 
 class TestReadme(doctest.DocFileCase):
-
     path = os.path.join(dirname, 'README.txt')
 
     def __init__(self, *args, **kwargs):
            </html>
            """
 
+    html3 = """
+           <html>
+            <body>
+              <div>node1</div>
+              <div id="node2">node2</div>
+              <div class="node3">node3</div>
+            </body>
+           </html>
+           """
+
     def test_selector_from_doc(self):
         doc = etree.fromstring(self.html)
         assert len(self.klass(doc)) == 1
         assert isinstance(n, self.klass)
         assert n._parent is e
 
+    def test_pseudo_classes(self):
+        e = self.klass(self.html)
+        self.assertEqual(e('div:first').text(), 'node1')
+        self.assertEqual(e('div:last').text(), 'node3')
+        self.assertEqual(e('div:even').text(), 'node1 node3')
+        self.assertEqual(e('div div:even').text(), None)
+        self.assertEqual(e('body div:even').text(), 'node1 node3')
+        self.assertEqual(e('div:gt(0)').text(), 'node2 node3')
+        self.assertEqual(e('div:lt(1)').text(), 'node1')
+        self.assertEqual(e('div:eq(2)').text(), 'node3')
+
 class TestTraversal(unittest.TestCase):
     klass = pq
     html = """
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.