[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