[Lxml-checkins] r40628 - in lxml/branch/extension_refactoring: . src/lxml

scoder at codespeak.net scoder at codespeak.net
Sat Mar 17 07:03:29 CET 2007


Author: scoder
Date: Sat Mar 17 07:03:26 2007
New Revision: 40628

Modified:
   lxml/branch/extension_refactoring/CHANGES.txt
   lxml/branch/extension_refactoring/src/lxml/extensions.pxi
   lxml/branch/extension_refactoring/src/lxml/nsclasses.pxi
   lxml/branch/extension_refactoring/src/lxml/xpath.pxi
   lxml/branch/extension_refactoring/src/lxml/xslt.pxd
   lxml/branch/extension_refactoring/src/lxml/xslt.pxi
Log:
initial branch import, mainly complete but buggy

Modified: lxml/branch/extension_refactoring/CHANGES.txt
==============================================================================
--- lxml/branch/extension_refactoring/CHANGES.txt	(original)
+++ lxml/branch/extension_refactoring/CHANGES.txt	Sat Mar 17 07:03:26 2007
@@ -2,6 +2,25 @@
 lxml changelog
 ==============
 
+Under Development
+=================
+
+Features added
+--------------
+
+* EXSLT RegExp support in standard XPath (not only XSLT)
+
+Bugs fixed
+----------
+
+* Thread safety in XPath evaluators
+
+Other changes
+-------------
+
+* major refactoring in XPath/XSLT extension function code
+
+
 1.3beta (2007-02-27)
 ====================
 

Modified: lxml/branch/extension_refactoring/src/lxml/extensions.pxi
==============================================================================
--- lxml/branch/extension_refactoring/src/lxml/extensions.pxi	(original)
+++ lxml/branch/extension_refactoring/src/lxml/extensions.pxi	Sat Mar 17 07:03:26 2007
@@ -12,11 +12,14 @@
 ################################################################################
 # Base class for XSLT and XPath evaluation contexts: functions, namespaces, ...
 
+ctypedef int _register_function(void* ctxt, name_utf, ns_uri_utf)
+
 cdef class _BaseContext:
     cdef xpath.xmlXPathContext* _xpathCtxt
     cdef _Document _doc
     cdef object _extensions
     cdef object _namespaces
+    cdef object _registered_namespaces
     cdef object _utf_refs
     cdef object _function_cache
     cdef object _function_cache_ns
@@ -28,9 +31,9 @@
     def __init__(self, namespaces, extensions):
         self._xpathCtxt = NULL
         self._utf_refs = {}
+        self._registered_namespaces = []
         self._function_cache = {}
         self._function_cache_ns = {}
-        self._called_function = None
 
         if extensions is not None:
             # convert extensions to UTF-8
@@ -90,7 +93,8 @@
             self.registerNamespaces(namespaces)
 
     cdef _unregister_context(self):
-        xpath.xmlXPathRegisteredNsCleanup(self._xpathCtxt)
+        self._unregisterNamespaces()
+#        xpath.xmlXPathRegisteredNsCleanup(self._xpathCtxt)
         self._free_context()
 
     cdef _free_context(self):
@@ -112,12 +116,86 @@
             self.registerNamespace(prefix, uri)
     
     cdef registerNamespace(self, prefix, ns_uri):
+        if prefix is None:
+            raise TypeError, "empty prefix is not supported in XPath"
         prefix_utf = self._to_utf(prefix)
         ns_uri_utf = self._to_utf(ns_uri)
-        xpath.xmlXPathRegisterNs(self._xpathCtxt, prefix_utf, ns_uri_utf)
+        python.PyList_Append(self._registered_namespaces, prefix_utf)
+        xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                 _cstr(prefix_utf), _cstr(ns_uri_utf))
+
+    cdef _registerNamespace(self, prefix_utf, ns_uri_utf):
+        python.PyList_Append(self._registered_namespaces, prefix_utf)
+        xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                 _cstr(prefix_utf), _cstr(ns_uri_utf))
+    
+    cdef void _unregisterNamespaces(self):
+        if python.PyList_GET_SIZE(self._registered_namespaces) > 0:
+            for prefix_utf in self._registered_namespaces:
+                sys.stderr.write(prefix_utf)
+                sys.stderr.flush()
+                xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                         _cstr(prefix_utf), NULL)
+            self._registered_namespaces = []
+    
+    cdef void _unregisterNamespace(self, prefix_utf):
+        xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                 _cstr(prefix_utf), NULL)
     
     # extension functions
 
+    cdef void _addLocalExtensionFunction(self, ns_utf, name_utf, function):
+        if self._extensions is None:
+            self._extensions = {}
+        python.PyDict_SetItem(self._extensions, (ns_utf, name_utf), function)
+
+    cdef void _registerAllFunctions(self, void* ctxt,
+                                    _register_function reg_func):
+        cdef python.PyObject* dict_result
+        for ns_utf, ns_functions in _iter_ns_extension_functions():
+            if ns_utf is None:
+                d = self._function_cache
+            else:
+                dict_result = python.PyDict_GetItem(
+                    self._function_cache_ns, ns_utf)
+                if dict_result is NULL:
+                    d = {}
+                    python.PyDict_SetItem(
+                        self._function_cache_ns, ns_utf, d)
+                else:
+                    d = <object>dict_result
+            for name_utf, function in ns_functions.iteritems():
+                python.PyDict_SetItem(d, name_utf, function)
+                reg_func(ctxt, name_utf, ns_utf)
+        if self._extensions is None:
+            return # done
+        last_ns = None
+        d = self._function_cache
+        for (ns_utf, name_utf), function in self._extensions.iteritems():
+            if ns_utf is not last_ns:
+                last_ns = ns_utf
+                if ns_utf is None:
+                    d = self._function_cache
+                else:
+                    dict_result = python.PyDict_GetItem(
+                        self._function_cache_ns, ns_utf)
+                    if dict_result is NULL:
+                        d = {}
+                        python.PyDict_SetItem(self._function_cache_ns,
+                                              ns_utf, d)
+                    else:
+                        d = <object>dict_result
+            python.PyDict_SetItem(d, name_utf, function)
+            reg_func(ctxt, name_utf, ns_utf)
+
+    cdef void _unregisterAllFunctions(self, void* ctxt,
+                                      _register_function unreg_func):
+        for name_utf in self._function_cache:
+            unreg_func(ctxt, name_utf, None)
+        for ns_utf, functions in self._function_cache_ns.iteritems():
+            for name_utf in functions:
+                unreg_func(ctxt, name_utf, ns_utf)
+
     cdef _find_cached_function(self, char* c_ns_uri, char* c_name):
         """Lookup an extension function in the cache and return it.
 
@@ -233,10 +311,99 @@
 
 
 ################################################################################
+# EXSLT regexp implementation
+
+cdef class _ExsltRegExp:
+    cdef object _compile_map
+    def __init__(self):
+        self._compile_map = {}
+
+    cdef _make_string(self, value):
+        if _isString(value):
+            return value
+        else:
+            raise TypeError, "Invalid argument type %s" % type(value)
+
+    cdef _compile(self, rexp, ignore_case):
+        cdef python.PyObject* c_result
+        rexp = self._make_string(rexp)
+        key = (rexp, ignore_case)
+        c_result = python.PyDict_GetItem(self._compile_map, key)
+        if c_result is not NULL:
+            return <object>c_result
+        py_flags = re.UNICODE
+        if ignore_case:
+            py_flags = py_flags | re.IGNORECASE
+        rexp_compiled = re.compile(rexp, py_flags)
+        python.PyDict_SetItem(self._compile_map, key, rexp_compiled)
+        return rexp_compiled
+
+    def test(self, ctxt, s, rexp, flags=''):
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        if rexpc.search(s) is None:
+            return False
+        else:
+            return True
+
+    def match(self, ctxt, s, rexp, flags=''):
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        if 'g' in flags:
+            results = rexpc.findall(s)
+            if not results:
+                return ()
+        else:
+            result = rexpc.search(s)
+            if not result:
+                return ()
+            results = [ result.group() ]
+            results.extend( result.groups('') )
+        result_list = []
+        root = Element('matches')
+        join_groups = ''.join
+        for s_match in results:
+            if python.PyTuple_CheckExact(s_match):
+                s_match = join_groups(s_match)
+            elem = SubElement(root, 'match')
+            elem.text = s_match
+            python.PyList_Append(result_list, elem)
+        return result_list
+
+    def replace(self, ctxt, s, rexp, flags, replacement):
+        replacement = self._make_string(replacement)
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        if 'g' in flags:
+            count = 0
+        else:
+            count = 1
+        return rexpc.sub(replacement, s, count)
+
+    cdef _register_in_context(self, _BaseContext context):
+        ns = "http://exslt.org/regular-expressions"
+        context._addLocalExtensionFunction(ns, "test",    self.test)
+        context._addLocalExtensionFunction(ns, "match",   self.match)
+        context._addLocalExtensionFunction(ns, "replace", self.replace)
+
+
+################################################################################
 # helper functions
 
 cdef xpath.xmlXPathFunction _function_check(void* ctxt,
                                             char* c_name, char* c_ns_uri):
+    cdef python.PyGILState_STATE gil_state
+    cdef xpath.xmlXPathFunction c_func
+    gil_state = python.PyGILState_Ensure()
+    c_func = _python_function_check(ctxt, c_name, c_ns_uri)
+    python.PyGILState_Release(gil_state)
+    return c_func
+
+cdef xpath.xmlXPathFunction _python_function_check(void* ctxt,
+                                                   char* c_name, char* c_ns_uri):
     "Module level lookup function for XPath/XSLT functions"
     cdef xpath.xmlXPathFunction c_func
     cdef _BaseContext context
@@ -405,7 +572,7 @@
             fref = "{%s}%s" % (rctxt.functionURI, rctxt.function)
         else:
             fref = rctxt.function
-        xpath.xmlXPathErr(ctxt, xpath.XML_XPATH_UNKNOWN_FUNC_ERROR)
+        xpath.xmlXPathErr(ctxt, xpath.XPATH_UNKNOWN_FUNC_ERROR)
         exception = XPathFunctionError("XPath function '%s' not found" % fref)
         context._exc._store_exception(exception)
 

Modified: lxml/branch/extension_refactoring/src/lxml/nsclasses.pxi
==============================================================================
--- lxml/branch/extension_refactoring/src/lxml/nsclasses.pxi	(original)
+++ lxml/branch/extension_refactoring/src/lxml/nsclasses.pxi	Sat Mar 17 07:03:26 2007
@@ -75,6 +75,11 @@
             name = _utf8(name)
         return self._get(name)
 
+    def __delitem__(self, name):
+        if name is not None:
+            name = _utf8(name)
+        python.PyDict_DelItem(self._entries, name)
+
     cdef object _get(self, object name):
         cdef python.PyObject* dict_result
         dict_result = python.PyDict_GetItem(self._entries, name)
@@ -99,7 +104,7 @@
         return self._entries.iteritems()
 
     def clear(self):
-        self._entries.clear()
+        python.PyDict_Clear(self._entries)
 
 cdef class _ClassNamespaceRegistry(_NamespaceRegistry):
     "Dictionary-like registry for namespace implementation classes"
@@ -130,32 +135,39 @@
 cdef class _XPathFunctionNamespaceRegistry(_FunctionNamespaceRegistry):
     cdef object _prefix
     cdef object _prefix_utf
+
     property prefix:
         "Namespace prefix for extension functions."
         def __del__(self):
             self._prefix = None # no prefix configured
+            self._prefix_utf = None
         def __get__(self):
-            return self._prefix
+            if self._prefix is None:
+                return ''
+            else:
+                return self._prefix
         def __set__(self, prefix):
+            if prefix == '':
+                prefix = None # empty prefix
             if prefix is None:
-                prefix = '' # empty prefix
-            self._prefix_utf = _utf8(prefix)
+                self._prefix_utf = None
+            else:
+                self._prefix_utf = _utf8(prefix)
             self._prefix = prefix
 
 cdef object _find_all_extension_prefixes():
     "Internal lookup function to find all function prefixes for XSLT/XPath."
     cdef _XPathFunctionNamespaceRegistry registry
-    ns_prefixes = {}
-    for (ns_utf, registry) in __FUNCTION_NAMESPACE_REGISTRIES.iteritems():
+    ns_prefixes = []
+    for registry in __FUNCTION_NAMESPACE_REGISTRIES.itervalues():
         if registry._prefix_utf is not None:
-            ns_prefixes[registry._prefix_utf] = ns_utf
+            if registry._ns_uri_utf is not None:
+                python.PyList_Append(
+                    ns_prefixes, (registry._prefix_utf, registry._ns_uri_utf))
     return ns_prefixes
 
-cdef object _iter_extension_function_names():
-    l = []
-    for (ns_utf, registry) in __FUNCTION_NAMESPACE_REGISTRIES.iteritems():
-        python.PyList_Append(l, (ns_utf, registry))
-    return l
+cdef object _iter_ns_extension_functions():
+    return __FUNCTION_NAMESPACE_REGISTRIES.iteritems()
 
 cdef object _find_extension(ns_uri_utf, name_utf):
     cdef python.PyObject* dict_result

Modified: lxml/branch/extension_refactoring/src/lxml/xpath.pxi
==============================================================================
--- lxml/branch/extension_refactoring/src/lxml/xpath.pxi	(original)
+++ lxml/branch/extension_refactoring/src/lxml/xpath.pxi	Sat Mar 17 07:03:26 2007
@@ -9,6 +9,25 @@
 ################################################################################
 # XPath
 
+cdef int _register_xpath_function(void* ctxt, name_utf, ns_utf):
+    if ns_utf is None:
+        return xpath.xmlXPathRegisterFunc(
+            <xpath.xmlXPathContext*>ctxt, _cstr(name_utf),
+            _xpath_function_call)
+    else:
+        return xpath.xmlXPathRegisterFuncNS(
+            <xpath.xmlXPathContext*>ctxt, _cstr(name_utf), _cstr(ns_utf),
+            _xpath_function_call)
+
+cdef int _unregister_xpath_function(void* ctxt, name_utf, ns_utf):
+    if ns_utf is None:
+        return xpath.xmlXPathRegisterFunc(
+            <xpath.xmlXPathContext*>ctxt, _cstr(name_utf), NULL)
+    else:
+        return xpath.xmlXPathRegisterFuncNS(
+            <xpath.xmlXPathContext*>ctxt, _cstr(name_utf), _cstr(ns_utf), NULL)
+
+
 cdef class _XPathContext(_BaseContext):
     cdef object _variables
     def __init__(self, namespaces, extensions, variables):
@@ -18,13 +37,13 @@
     cdef register_context(self, xpath.xmlXPathContext* xpathCtxt, _Document doc):
         self._set_xpath_context(xpathCtxt)
         ns_prefixes = _find_all_extension_prefixes()
-        if ns_prefixes:
-            self.registerNamespaces(ns_prefixes)
+        if python.PyList_GET_SIZE(ns_prefixes) > 0:
+            for (prefix, ns_uri) in ns_prefixes:
+                self._registerNamespace(prefix, ns_uri)
         self._register_context(doc)
         if self._variables is not None:
             self.registerVariables(self._variables)
-        xpath.xmlXPathRegisterFuncLookup(
-            self._xpathCtxt, _function_check, <python.PyObject*>self)
+        self._registerAllFunctions(xpathCtxt, _register_xpath_function)
 
     cdef unregister_context(self):
         cdef xpath.xmlXPathContext* xpathCtxt
@@ -32,15 +51,16 @@
         if xpathCtxt is NULL:
             return
         xpath.xmlXPathRegisteredVariablesCleanup(xpathCtxt)
+        self._unregisterAllFunctions(xpathCtxt, _unregister_xpath_function)
         self._unregister_context()
 
-    def registerVariables(self, variable_dict):
+    cdef registerVariables(self, variable_dict):
         for name, value in variable_dict.items():
             name_utf = self._to_utf(name)
             xpath.xmlXPathRegisterVariable(
                 self._xpathCtxt, _cstr(name_utf), _wrapXPathObject(value))
 
-    def registerVariable(self, name, value):
+    cdef registerVariable(self, name, value):
         name_utf = self._to_utf(name)
         xpath.xmlXPathRegisterVariable(
             self._xpathCtxt, _cstr(name_utf), _wrapXPathObject(value))
@@ -55,9 +75,14 @@
 cdef class _XPathEvaluatorBase:
     cdef xpath.xmlXPathContext* _xpathCtxt
     cdef _XPathContext _context
+    cdef python.PyThread_type_lock _eval_lock
 
-    def __init__(self, namespaces, extensions, variables=None):
-        self._context = _XPathContext(namespaces, extensions, variables)
+    def __init__(self, namespaces, extensions, regexp):
+        cdef _ExsltRegExp _regexp 
+        self._context = _XPathContext(namespaces, extensions, None)
+        if regexp:
+            _regexp = _ExsltRegExp()
+            _regexp._register_in_context(self._context)
 
     def __dealloc__(self):
         if self._xpathCtxt is not NULL:
@@ -84,6 +109,22 @@
             c = path[0]
         return c == c'/'
 
+    cdef int _lock(self) except -1:
+        cdef python.PyThreadState* state
+        cdef int result
+        if config.ENABLE_THREADING and self._eval_lock != NULL:
+            state = python.PyEval_SaveThread()
+            result = python.PyThread_acquire_lock(
+                self._eval_lock, python.WAIT_LOCK)
+            python.PyEval_RestoreThread(state)
+            if result == 0:
+                raise ParserError, "parser locking failed"
+        return 0
+
+    cdef void _unlock(self):
+        if config.ENABLE_THREADING and self._eval_lock != NULL:
+            python.PyThread_release_lock(self._eval_lock)
+
     cdef _raise_parse_error(self):
         if self._xpathCtxt is not NULL and \
                self._xpathCtxt.lastError.message is not NULL:
@@ -119,10 +160,13 @@
     Absolute XPath expressions (starting with '/') will be evaluated against
     the ElementTree as returned by getroottree().
 
-    XPath evaluators must not be shared between threads.
+    Additional namespace declarations can be passed with the 'namespace'
+    keyword argument.  EXSLT regular expression support can be disabled with
+    the 'regexp' boolean keyword (defaults to True).
     """
     cdef _Element _element
-    def __init__(self, _Element element not None, namespaces=None, extensions=None):
+    def __init__(self, _Element element not None, namespaces=None,
+                 extensions=None, regexp=True):
         cdef xpath.xmlXPathContext* xpathCtxt
         cdef int ns_register_status
         cdef _Document doc
@@ -133,7 +177,7 @@
             raise XPathContextError, "Unable to create new XPath context"
         _setupDict(xpathCtxt)
         self._element = element
-        _XPathEvaluatorBase.__init__(self, namespaces, extensions)
+        _XPathEvaluatorBase.__init__(self, namespaces, extensions, regexp)
 
     def registerNamespace(self, prefix, uri):
         """Register a namespace with the XPath context.
@@ -155,6 +199,7 @@
         Absolute XPath expressions (starting with '/') will be evaluated
         against the ElementTree as returned by getroottree().
         """
+        cdef python.PyThreadState* state
         cdef xpath.xmlXPathContext* xpathCtxt
         cdef xpath.xmlXPathObject*  xpathObj
         cdef _Document doc
@@ -164,12 +209,16 @@
         xpathCtxt.node = self._element._c_node
         doc = self._element._doc
 
+        self._lock()
         self._context.register_context(xpathCtxt, doc)
         try:
             self._context.registerVariables(_variables)
+            state = python.PyEval_SaveThread()
             xpathObj = xpath.xmlXPathEvalExpression(_cstr(path), xpathCtxt)
         finally:
+            python.PyEval_RestoreThread(state)
             self._context.unregister_context()
+            self._unlock()
 
         return self._handle_result(xpathObj, doc)
 
@@ -177,11 +226,14 @@
 cdef class XPathDocumentEvaluator(XPathElementEvaluator):
     """Create an XPath evaluator for an ElementTree.
 
-    XPath evaluators must not be shared between threads.
+    Additional namespace declarations can be passed with the 'namespace'
+    keyword argument.  EXSLT regular expression support can be disabled with
+    the 'regexp' boolean keyword (defaults to True).
     """
-    def __init__(self, _ElementTree etree not None, namespaces=None, extensions=None):
+    def __init__(self, _ElementTree etree not None, namespaces=None,
+                 extensions=None, regexp=True):
         XPathElementEvaluator.__init__(
-            self, etree._context_node, namespaces, extensions)
+            self, etree._context_node, namespaces, extensions, regexp)
 
     def __call__(self, _path, **_variables):
         """Evaluate an XPath expression on the document.
@@ -189,6 +241,7 @@
         Variables may be provided as keyword arguments.  Note that namespaces
         are currently not supported for variables.
         """
+        cdef python.PyThreadState* state
         cdef xpath.xmlXPathContext* xpathCtxt
         cdef xpath.xmlXPathObject*  xpathObj
         cdef xmlDoc* c_doc
@@ -197,47 +250,57 @@
         xpathCtxt = self._xpathCtxt
         doc = self._element._doc
 
+        self._lock()
         self._context.register_context(xpathCtxt, doc)
         c_doc = _fakeRootDoc(doc._c_doc, self._element._c_node)
         try:
             self._context.registerVariables(_variables)
+            state = python.PyEval_SaveThread()
             xpathCtxt.doc  = c_doc
             xpathCtxt.node = tree.xmlDocGetRootElement(c_doc)
             xpathObj = xpath.xmlXPathEvalExpression(_cstr(path), xpathCtxt)
         finally:
+            python.PyEval_RestoreThread(state)
             _destroyFakeDoc(doc._c_doc, c_doc)
             self._context.unregister_context()
+            self._unlock()
 
         return self._handle_result(xpathObj, doc)
 
 
-def XPathEvaluator(etree_or_element, namespaces=None, extensions=None):
+def XPathEvaluator(etree_or_element, namespaces=None, extensions=None,
+                   regexp=True):
     """Creates an XPath evaluator for an ElementTree or an Element.
 
     The resulting object can be called with an XPath expression as argument
     and XPath variables provided as keyword arguments.
 
-    XPath evaluators must not be shared between threads.
+    Additional namespace declarations can be passed with the 'namespace'
+    keyword argument.  EXSLT regular expression support can be disabled with
+    the 'regexp' boolean keyword (defaults to True).
     """
     if isinstance(etree_or_element, _ElementTree):
-        return XPathDocumentEvaluator(etree_or_element, namespaces, extensions)
+        return XPathDocumentEvaluator(etree_or_element, namespaces,
+                                      extensions, regexp)
     else:
-        return XPathElementEvaluator(etree_or_element, namespaces, extensions)
+        return XPathElementEvaluator(etree_or_element, namespaces,
+                                     extensions, regexp)
 
 
 cdef class XPath(_XPathEvaluatorBase):
     """A compiled XPath expression that can be called on Elements and
     ElementTrees.
 
-    Besides the XPath expression, you can pass namespace mappings and
-    extensions to the constructor through the keyword arguments ``namespaces``
-    and ``extensions``.
+    Besides the XPath expression, you can pass prefix-namespace mappings and
+    extension functions to the constructor through the keyword arguments
+    ``namespaces`` and ``extensions``.  EXSLT regular expression support can
+    be disabled with the 'regexp' boolean keyword (defaults to True).
     """
     cdef xpath.xmlXPathCompExpr* _xpath
     cdef readonly object path
 
-    def __init__(self, path, namespaces=None, extensions=None):
-        _XPathEvaluatorBase.__init__(self, namespaces, extensions)
+    def __init__(self, path, namespaces=None, extensions=None, regexp=True):
+        _XPathEvaluatorBase.__init__(self, namespaces, extensions, regexp)
         self._xpath = NULL
         self.path = path
         path = _utf8(path)
@@ -258,19 +321,21 @@
         document = _documentOrRaise(_etree_or_element)
         element  = _rootNodeOrRaise(_etree_or_element)
 
-        xpathCtxt = self._xpathCtxt
-        xpathCtxt.doc  = document._c_doc
-        xpathCtxt.node = element._c_node
+        self._lock()
+        self._xpathCtxt.doc  = document._c_doc
+        self._xpathCtxt.node = element._c_node
 
         context = self._context
-        context.register_context(xpathCtxt, document)
+        context.register_context(self._xpathCtxt, document)
+        context.registerVariables(_variables)
         try:
-            context.registerVariables(_variables)
             state = python.PyEval_SaveThread()
-            xpathObj = xpath.xmlXPathCompiledEval(self._xpath, xpathCtxt)
-            python.PyEval_RestoreThread(state)
+            xpathObj = xpath.xmlXPathCompiledEval(
+                self._xpath, self._xpathCtxt)
         finally:
+            python.PyEval_RestoreThread(state)
             context.unregister_context()
+            self._unlock()
         return self._handle_result(xpathObj, document)
 
     def __dealloc__(self):

Modified: lxml/branch/extension_refactoring/src/lxml/xslt.pxd
==============================================================================
--- lxml/branch/extension_refactoring/src/lxml/xslt.pxd	(original)
+++ lxml/branch/extension_refactoring/src/lxml/xslt.pxd	Sat Mar 17 07:03:26 2007
@@ -35,6 +35,8 @@
                                            xmlXPathFunction function)
     cdef int xsltUnregisterExtModuleFunction(char* name, char* URI)
     cdef xmlXPathFunction xsltExtModuleFunctionLookup(char* name, char* URI)
+    cdef int xsltRegisterExtPrefix(xsltStylesheet* style, 
+                                   char* prefix, char* URI)
 
 cdef extern from "libxslt/documents.h":
     ctypedef enum xsltLoadType:

Modified: lxml/branch/extension_refactoring/src/lxml/xslt.pxi
==============================================================================
--- lxml/branch/extension_refactoring/src/lxml/xslt.pxi	(original)
+++ lxml/branch/extension_refactoring/src/lxml/xslt.pxi	Sat Mar 17 07:03:26 2007
@@ -193,6 +193,21 @@
 ################################################################################
 # XSLT
 
+cdef int _register_xslt_function(void* ctxt, name_utf, ns_utf):
+    if ns_utf is None:
+        return 0
+    return xslt.xsltRegisterExtFunction(
+        <xslt.xsltTransformContext*>ctxt, _cstr(name_utf), _cstr(ns_utf),
+        _xpath_function_call)
+
+cdef int _unregister_xslt_function(void* ctxt, name_utf, ns_utf):
+    if ns_utf is None:
+        return 0
+    return xslt.xsltRegisterExtFunction(
+        <xslt.xsltTransformContext*>ctxt, _cstr(name_utf), _cstr(ns_utf),
+        NULL)
+
+
 cdef class _XSLTContext(_BaseContext):
     cdef xslt.xsltTransformContext* _xsltCtxt
     def __init__(self, namespaces, extensions):
@@ -207,7 +222,7 @@
         self._set_xpath_context(xsltCtxt.xpathCtxt)
         self._register_context(doc)
         xsltCtxt.xpathCtxt.userData = <void*>self
-        self._registerExtensionFunctions()
+        self._registerAllFunctions(xsltCtxt, _register_xslt_function)
 
     cdef free_context(self):
         cdef xslt.xsltTransformContext* xsltCtxt
@@ -219,49 +234,6 @@
         xslt.xsltFreeTransformContext(xsltCtxt)
         self._release_temp_refs()
 
-    cdef void _addLocalExtensionFunction(self, ns_utf, name_utf, function):
-        if self._extensions is None:
-            self._extensions = {}
-        python.PyDict_SetItem(self._extensions, (ns_utf, name_utf), function)
-
-    cdef void _registerExtensionFunctions(self):
-        cdef python.PyObject* dict_result
-        for ns_utf, functions in _iter_extension_function_names():
-            if ns_utf is None:
-                continue
-            dict_result = python.PyDict_GetItem(self._function_cache_ns, ns_utf)
-            if dict_result is NULL:
-                d = {}
-                python.PyDict_SetItem(self._function_cache_ns, ns_utf, d)
-            else:
-                d = <object>dict_result
-            for name_utf, function in functions.iteritems():
-                python.PyDict_SetItem(d, name_utf, function)
-                xslt.xsltRegisterExtFunction(
-                    self._xsltCtxt, _cstr(name_utf), _cstr(ns_utf),
-                    _xpath_function_call)
-        if self._extensions is None:
-            return # done
-        last_ns = None
-        for (ns_utf, name_utf), function in self._extensions.iteritems():
-            if ns_utf is None:
-                raise ValueError, \
-                      "extensions must have non empty namespaces"
-            elif ns_utf is not last_ns:
-                last_ns = ns_utf
-                dict_result = python.PyDict_GetItem(
-                    self._function_cache_ns, ns_utf)
-                if dict_result is NULL:
-                    d = {}
-                    python.PyDict_SetItem(self._function_cache_ns, ns_utf, d)
-                else:
-                    d = <object>dict_result
-            python.PyDict_SetItem(d, name_utf, function)
-            xslt.xsltRegisterExtFunction(
-                self._xsltCtxt, _cstr(name_utf), _cstr(ns_utf),
-                _xpath_function_call)
-
-cdef class _ExsltRegExp # forward declaration
 
 cdef class XSLT:
     """Turn a document into an XSLT object.
@@ -279,7 +251,6 @@
     cdef xslt.xsltStylesheet* _c_style
     cdef _XSLTResolverContext _xslt_resolver_context
     cdef XSLTAccessControl _access_control
-    cdef _ExsltRegExp _regexp
     cdef _ErrorLog _error_log
 
     def __init__(self, xslt_input, extensions=None, regexp=True, access_control=None):
@@ -289,6 +260,7 @@
         cdef xmlDoc* fake_c_doc
         cdef _Document doc
         cdef _Element root_node
+        cdef _ExsltRegExp _regexp 
 
         doc = _documentOrRaise(xslt_input)
         root_node = _rootNodeOrRaise(xslt_input)
@@ -329,11 +301,8 @@
 
         self._context = _XSLTContext(None, extensions)
         if regexp:
-            self._regexp = _ExsltRegExp()
-            self._regexp._register_in_context(self._context)
-        else:
-            self._regexp  = None
-        # XXX is it worthwile to use xsltPrecomputeStylesheet here?
+            _regexp = _ExsltRegExp()
+            _regexp._register_in_context(self._context)
 
     def __dealloc__(self):
         if self._xslt_resolver_context is not None and \
@@ -649,82 +618,3 @@
             if attr == key:
                 return value
         return default
-
-################################################################################
-# EXSLT regexp implementation
-
-cdef class _ExsltRegExp:
-    cdef object _compile_map
-    def __init__(self):
-        self._compile_map = {}
-
-    cdef _make_string(self, value):
-        if _isString(value):
-            return value
-        else:
-            raise TypeError, "Invalid argument type %s" % type(value)
-
-    cdef _compile(self, rexp, ignore_case):
-        cdef python.PyObject* c_result
-        rexp = self._make_string(rexp)
-        key = (rexp, ignore_case)
-        c_result = python.PyDict_GetItem(self._compile_map, key)
-        if c_result is not NULL:
-            return <object>c_result
-        py_flags = re.UNICODE
-        if ignore_case:
-            py_flags = py_flags | re.IGNORECASE
-        rexp_compiled = re.compile(rexp, py_flags)
-        python.PyDict_SetItem(self._compile_map, key, rexp_compiled)
-        return rexp_compiled
-
-    def test(self, ctxt, s, rexp, flags=''):
-        flags = self._make_string(flags)
-        s = self._make_string(s)
-        rexpc = self._compile(rexp, 'i' in flags)
-        if rexpc.search(s) is None:
-            return False
-        else:
-            return True
-
-    def match(self, ctxt, s, rexp, flags=''):
-        flags = self._make_string(flags)
-        s = self._make_string(s)
-        rexpc = self._compile(rexp, 'i' in flags)
-        if 'g' in flags:
-            results = rexpc.findall(s)
-            if not results:
-                return ()
-        else:
-            result = rexpc.search(s)
-            if not result:
-                return ()
-            results = [ result.group() ]
-            results.extend( result.groups('') )
-        result_list = []
-        root = Element('matches')
-        join_groups = ''.join
-        for s_match in results:
-            if python.PyTuple_CheckExact(s_match):
-                s_match = join_groups(s_match)
-            elem = SubElement(root, 'match')
-            elem.text = s_match
-            python.PyList_Append(result_list, elem)
-        return result_list
-
-    def replace(self, ctxt, s, rexp, flags, replacement):
-        replacement = self._make_string(replacement)
-        flags = self._make_string(flags)
-        s = self._make_string(s)
-        rexpc = self._compile(rexp, 'i' in flags)
-        if 'g' in flags:
-            count = 0
-        else:
-            count = 1
-        return rexpc.sub(replacement, s, count)
-
-    cdef _register_in_context(self, _XSLTContext context):
-        ns = "http://exslt.org/regular-expressions"
-        context._addLocalExtensionFunction(ns, "test",    self.test)
-        context._addLocalExtensionFunction(ns, "match",   self.match)
-        context._addLocalExtensionFunction(ns, "replace", self.replace)


More information about the lxml-checkins mailing list