diff --git a/scrapy/tests/test_xpath.py b/scrapy/tests/test_xpath.py index ca9dfce27..ae3f152be 100644 --- a/scrapy/tests/test_xpath.py +++ b/scrapy/tests/test_xpath.py @@ -163,6 +163,20 @@ class XPathTestCase(unittest.TestCase): self.assertEqual(x.x("//p:SecondTestTag").x("./xmlns:price/text()")[0].extract(), '90') self.assertEqual(x.x("//p:SecondTestTag/xmlns:material").extract()[0], '') + @libxml2debug + def test_selector_invalid_xpath(self): + response = XmlResponse(url="http://example.com", body="") + x = HtmlXPathSelector(response) + xpath = "//test[@foo='bar]" + try: + x.x(xpath) + except ValueError, e: + assert xpath in str(e), "Exception message does not contain invalid xpath" + except Exception: + raise AssertionError("A invalid XPath does not raise ValueError") + else: + raise AssertionError("A invalid XPath does not raise an exception") + @libxml2debug def test_http_header_encoding_precedence(self): # u'\xa3' = pound symbol in unicode diff --git a/scrapy/xpath/selector.py b/scrapy/xpath/selector.py index c0a1a5b5e..f1882a37c 100644 --- a/scrapy/xpath/selector.py +++ b/scrapy/xpath/selector.py @@ -45,7 +45,10 @@ class XPathSelector(object): return a XPathSelectorList of the result""" if hasattr(self.xmlNode, 'xpathEval'): self.doc.xpathContext.setContextNode(self.xmlNode) - xpath_result = self.doc.xpathContext.xpathEval(xpath) + try: + xpath_result = self.doc.xpathContext.xpathEval(xpath) + except libxml2.xpathError: + raise ValueError("Invalid XPath: %s" % xpath) cls = type(self) if hasattr(xpath_result, '__iter__'): return XPathSelectorList([cls(node=node, parent=self, expr=xpath, response=self.response)