Pre-parse StanzaPath paths to speed up matching.

The parsing and namespace cleaning isn't terribly expensive, but it does
add up. It was adding an extra 5sec when processing 100,000 basic
message stanzas.
This commit is contained in:
Lance Stout
2012-01-17 22:28:44 -08:00
parent 4274f49ada
commit 2923f56561
2 changed files with 57 additions and 42 deletions

View File

@@ -77,6 +77,49 @@ def register_stanza_plugin(stanza, plugin, iterable=False, overrides=False):
registerStanzaPlugin = register_stanza_plugin
def fix_ns(xpath, split=False, propagate_ns=True, default_ns=''):
"""Apply the stanza's namespace to elements in an XPath expression.
:param string xpath: The XPath expression to fix with namespaces.
:param bool split: Indicates if the fixed XPath should be left as a
list of element names with namespaces. Defaults to
False, which returns a flat string path.
:param bool propagate_ns: Overrides propagating parent element
namespaces to child elements. Useful if
you wish to simply split an XPath that has
non-specified namespaces, and child and
parent namespaces are known not to always
match. Defaults to True.
"""
fixed = []
# Split the XPath into a series of blocks, where a block
# is started by an element with a namespace.
ns_blocks = xpath.split('{')
for ns_block in ns_blocks:
if '}' in ns_block:
# Apply the found namespace to following elements
# that do not have namespaces.
namespace = ns_block.split('}')[0]
elements = ns_block.split('}')[1].split('/')
else:
# Apply the stanza's namespace to the following
# elements since no namespace was provided.
namespace = default_ns
elements = ns_block.split('/')
for element in elements:
if element:
# Skip empty entry artifacts from splitting.
if propagate_ns:
tag = '{%s}%s' % (namespace, element)
else:
tag = element
fixed.append(tag)
if split:
return fixed
return '/'.join(fixed)
class ElementBase(object):
"""
@@ -760,7 +803,7 @@ class ElementBase(object):
may be either a string or a list of element
names with attribute checks.
"""
if isinstance(xpath, str):
if not isinstance(xpath, list):
xpath = self._fix_ns(xpath, split=True, propagate_ns=False)
# Extract the tag name and attribute checks for the first XPath node.
@@ -952,46 +995,9 @@ class ElementBase(object):
return self
def _fix_ns(self, xpath, split=False, propagate_ns=True):
"""Apply the stanza's namespace to elements in an XPath expression.
:param string xpath: The XPath expression to fix with namespaces.
:param bool split: Indicates if the fixed XPath should be left as a
list of element names with namespaces. Defaults to
False, which returns a flat string path.
:param bool propagate_ns: Overrides propagating parent element
namespaces to child elements. Useful if
you wish to simply split an XPath that has
non-specified namespaces, and child and
parent namespaces are known not to always
match. Defaults to True.
"""
fixed = []
# Split the XPath into a series of blocks, where a block
# is started by an element with a namespace.
ns_blocks = xpath.split('{')
for ns_block in ns_blocks:
if '}' in ns_block:
# Apply the found namespace to following elements
# that do not have namespaces.
namespace = ns_block.split('}')[0]
elements = ns_block.split('}')[1].split('/')
else:
# Apply the stanza's namespace to the following
# elements since no namespace was provided.
namespace = self.namespace
elements = ns_block.split('/')
for element in elements:
if element:
# Skip empty entry artifacts from splitting.
if propagate_ns:
tag = '{%s}%s' % (namespace, element)
else:
tag = element
fixed.append(tag)
if split:
return fixed
return '/'.join(fixed)
return fix_ns(xpath, split=split,
propagate_ns=propagate_ns,
default_ns=self.namespace)
def __eq__(self, other):
"""Compare the stanza object with another to test for equality.