[Lxml-checkins] r43301 - in lxml/trunk: . src/lxml src/lxml/tests

scoder at codespeak.net scoder at codespeak.net
Sat May 12 17:34:00 CEST 2007


Author: scoder
Date: Sat May 12 17:34:00 2007
New Revision: 43301

Modified:
   lxml/trunk/CHANGES.txt
   lxml/trunk/src/lxml/tests/test_xpathevaluator.py
   lxml/trunk/src/lxml/xmlerror.pxi
   lxml/trunk/src/lxml/xpath.pxi
Log:
new XPathEvalError for evaluation errors (instead of always raising XPathSyntaxError)

Modified: lxml/trunk/CHANGES.txt
==============================================================================
--- lxml/trunk/CHANGES.txt	(original)
+++ lxml/trunk/CHANGES.txt	Sat May 12 17:34:00 2007
@@ -8,6 +8,10 @@
 Features added
 --------------
 
+* Error specific messages in XPath parsing and evaluation
+  NOTE: for evaluation errors, you will now get an XPathEvalError instead of
+  an XPathSyntaxError.  To catch both, you can except on ``XPathError``
+
 * The regular expression functions in XPath now support passing a node-set
   instead of a string
 

Modified: lxml/trunk/src/lxml/tests/test_xpathevaluator.py
==============================================================================
--- lxml/trunk/src/lxml/tests/test_xpathevaluator.py	(original)
+++ lxml/trunk/src/lxml/tests/test_xpathevaluator.py	Sat May 12 17:34:00 2007
@@ -114,7 +114,20 @@
 
     def test_xpath_error(self):
         tree = self.parse('<a/>')
-        self.assertRaises(SyntaxError, tree.xpath, '\\fad')
+        self.assertRaises(etree.XPathEvalError, tree.xpath, '\\fad')
+
+    def test_xpath_class_error(self):
+        self.assertRaises(SyntaxError, etree.XPath, '\\fad')
+        self.assertRaises(etree.XPathSyntaxError, etree.XPath, '\\fad')
+
+    def test_xpath_prefix_error(self):
+        tree = self.parse('<a/>')
+        self.assertRaises(etree.XPathEvalError, tree.xpath, '/fa:d')
+
+    def test_xpath_class_prefix_error(self):
+        tree = self.parse('<a/>')
+        xpath = etree.XPath("/fa:d")
+        self.assertRaises(etree.XPathEvalError, xpath, tree)
 
     def test_elementtree_getpath(self):
         a  = etree.Element("a")

Modified: lxml/trunk/src/lxml/xmlerror.pxi
==============================================================================
--- lxml/trunk/src/lxml/xmlerror.pxi	(original)
+++ lxml/trunk/src/lxml/xmlerror.pxi	Sat May 12 17:34:00 2007
@@ -5,8 +5,9 @@
 # module level API functions
 
 def clearErrorLog():
-    """Clear the global error log.
-    Note that this log is already bounded to a fixed size."""
+    """Clear the global error log.  Note that this log is already bound to a
+    fixed size.
+    """
     __GLOBAL_ERROR_LOG.clear()
 
 # dummy function: no debug output at all
@@ -145,6 +146,15 @@
     def __len__(self):
         return len(self._entries)
 
+    def __contains__(self, error_type):
+        for entry in self._entries:
+            if entry.type == error_type:
+                return True
+        return False
+
+    def __nonzero__(self):
+        return bool(self._entries)
+
     def filter_domains(self, domains):
         cdef _LogEntry entry
         filtered = []

Modified: lxml/trunk/src/lxml/xpath.pxi
==============================================================================
--- lxml/trunk/src/lxml/xpath.pxi	(original)
+++ lxml/trunk/src/lxml/xpath.pxi	Sat May 12 17:34:00 2007
@@ -1,14 +1,36 @@
 # XPath evaluation
 
-class XPathContextError(XPathError):
+class XPathSyntaxError(LxmlSyntaxError, XPathError):
     pass
 
-class XPathSyntaxError(LxmlSyntaxError, XPathError):
+class XPathEvalError(XPathError):
     pass
 
 ################################################################################
 # XPath
 
+cdef object _XPATH_SYNTAX_ERRORS
+_XPATH_SYNTAX_ERRORS = (
+    xmlerror.XML_XPATH_NUMBER_ERROR,
+    xmlerror.XML_XPATH_UNFINISHED_LITERAL_ERROR,
+    xmlerror.XML_XPATH_VARIABLE_REF_ERROR,
+    xmlerror.XML_XPATH_INVALID_PREDICATE_ERROR,
+    xmlerror.XML_XPATH_UNCLOSED_ERROR,
+    xmlerror.XML_XPATH_INVALID_CHAR_ERROR
+)
+
+cdef object _XPATH_EVAL_ERRORS
+_XPATH_EVAL_ERRORS = (
+    xmlerror.XML_XPATH_UNDEF_VARIABLE_ERROR,
+    xmlerror.XML_XPATH_UNDEF_PREFIX_ERROR,
+    xmlerror.XML_XPATH_UNKNOWN_FUNC_ERROR,
+    xmlerror.XML_XPATH_INVALID_OPERAND,
+    xmlerror.XML_XPATH_INVALID_TYPE,
+    xmlerror.XML_XPATH_INVALID_ARITY,
+    xmlerror.XML_XPATH_INVALID_CTXT_SIZE,
+    xmlerror.XML_XPATH_INVALID_CTXT_POSITION
+)
+
 cdef int _register_xpath_function(void* ctxt, name_utf, ns_utf):
     if ns_utf is None:
         return xpath.xmlXPathRegisterFunc(
@@ -76,11 +98,17 @@
     cdef xpath.xmlXPathContext* _xpathCtxt
     cdef _XPathContext _context
     cdef python.PyThread_type_lock _eval_lock
+    cdef _ErrorLog _error_log
 
     def __init__(self, namespaces, extensions, enable_regexp):
+        self._error_log = _ErrorLog()
         self._context = _XPathContext(namespaces, extensions,
                                       enable_regexp, None)
 
+    property error_log:
+        def __get__(self):
+            return self._error_log.copy()
+
     def __dealloc__(self):
         if self._xpathCtxt is not NULL:
             xpath.xmlXPathFreeContext(self._xpathCtxt)
@@ -127,6 +155,12 @@
             python.PyThread_release_lock(self._eval_lock)
 
     cdef _raise_parse_error(self):
+        entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
+        if entries:
+            entry = entries[0]
+            if entry is not None and entry.message:
+                raise XPathSyntaxError, entry.message
+
         if self._xpathCtxt is not NULL and \
                self._xpathCtxt.lastError.message is not NULL:
             message = funicode(self._xpathCtxt.lastError.message)
@@ -134,6 +168,24 @@
             message = "error in xpath expression"
         raise XPathSyntaxError, message
 
+    cdef _raise_eval_error(self):
+        entries = self._error_log.filter_types(_XPATH_EVAL_ERRORS)
+        if entries:
+            entry = entries[0]
+            if entry is not None and entry.message:
+                raise XPathEvalError, entry.message
+        entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
+        if entries:
+            entry = entries[0]
+            if entry is not None and entry.message:
+                raise XPathSyntaxError, entry.message
+        if self._xpathCtxt is not NULL and \
+               self._xpathCtxt.lastError.message is not NULL:
+            message = funicode(self._xpathCtxt.lastError.message)
+        else:
+            message = "error in xpath evaluation"
+        raise XPathEvalError, message
+
     cdef object _handle_result(self, xpath.xmlXPathObject* xpathObj, _Document doc):
         if self._context._exc._has_raised():
             if xpathObj is not NULL:
@@ -144,7 +196,7 @@
 
         if xpathObj is NULL:
             self._context._release_temp_refs()
-            self._raise_parse_error()
+            self._raise_eval_error()
 
         try:
             result = _unwrapXPathObject(xpathObj, doc)
@@ -176,7 +228,7 @@
         _XPathEvaluatorBase.__init__(self, namespaces, extensions, regexp)
         xpathCtxt = xpath.xmlXPathNewContext(doc._c_doc)
         if xpathCtxt is NULL:
-            raise XPathContextError, "Unable to create new XPath context"
+            python.PyErr_NoMemory()
         self.set_context(xpathCtxt)
 
     def registerNamespace(self, prefix, uri):
@@ -207,6 +259,7 @@
         doc = self._element._doc
 
         self._lock()
+        self._error_log.connect()
         self._xpathCtxt.node = self._element._c_node
         try:
             self._context.register_context(doc)
@@ -217,6 +270,7 @@
             python.PyEval_RestoreThread(state)
             result = self._handle_result(xpathObj, doc)
         finally:
+            self._error_log.disconnect()
             self._context.unregister_context()
             self._unlock()
 
@@ -249,6 +303,7 @@
         doc = self._element._doc
 
         self._lock()
+        self._error_log.connect()
         try:
             self._context.register_context(doc)
             c_doc = _fakeRootDoc(doc._c_doc, self._element._c_node)
@@ -265,6 +320,7 @@
                 _destroyFakeDoc(doc._c_doc, c_doc)
                 self._context.unregister_context()
         finally:
+            self._error_log.disconnect()
             self._unlock()
 
         return result
@@ -308,9 +364,11 @@
         path = _utf8(path)
         xpathCtxt = xpath.xmlXPathNewContext(NULL)
         if xpathCtxt is NULL:
-            raise XPathContextError, "Unable to create new XPath context"
+            python.PyErr_NoMemory()
         self.set_context(xpathCtxt)
+        self._error_log.connect()
         self._xpath = xpath.xmlXPathCtxtCompile(xpathCtxt, _cstr(path))
+        self._error_log.disconnect()
         if self._xpath is NULL:
             self._raise_parse_error()
 
@@ -325,6 +383,7 @@
         element  = _rootNodeOrRaise(_etree_or_element)
 
         self._lock()
+        self._error_log.connect()
         self._xpathCtxt.doc  = document._c_doc
         self._xpathCtxt.node = element._c_node
 
@@ -337,6 +396,7 @@
             python.PyEval_RestoreThread(state)
             result = self._handle_result(xpathObj, document)
         finally:
+            self._error_log.disconnect()
             self._context.unregister_context()
             self._unlock()
         return result


More information about the lxml-checkins mailing list