Skip to content

Commit

Permalink
Update to new release 1.0.12
Browse files Browse the repository at this point in the history
  - Added default_namespace property to XPath parser classes
  - XPath1Parser ignores the default namespace setting
  - Fixed the '(name)' token methods to use the default namespace
  • Loading branch information
brunato committed Sep 1, 2018
1 parent f8d8333 commit 8c094eb
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 134 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
CHANGELOG
*********

`v1.0.12`_ (TDB)
================
* Fixed the default namespace use for names without prefix.

`v1.0.11`_ (2018-07-25)
=======================
* Added two recursive protected methods to context class
Expand Down Expand Up @@ -63,4 +67,4 @@ handling of static context evaluation
.. _v1.0.8: https://github.com/brunato/elementpath/compare/v1.0.7...v1.0.8
.. _v1.0.10: https://github.com/brunato/elementpath/compare/v1.0.8...v1.0.10
.. _v1.0.11: https://github.com/brunato/elementpath/compare/v1.0.10...v1.0.11

.. _v1.0.12: https://github.com/brunato/elementpath/compare/v1.0.11...v1.0.12
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ data structures, both for the standard ElementTree library and for the
For `lxml.etree <http://lxml.de>`_ this package can be useful for providing XPath 2.0 selectors,
because `lxml.etree <http://lxml.de>`_ already has it's own implementation of XPath 1.0.

The XPath 2.0 functions implementation not completed yet, due to wide number of functions that this
The XPath 2.0 functions implementation is not completed yet, due to wide number of functions that this
language provides. If you want you can contribute to add an unimplemented function see the section below.


Expand Down
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = '1.0.11'
release = '1.0.12'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions doc/xpath_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ XPath parsers
.. autoclass:: elementpath.XPath1Parser

.. autoattribute:: DEFAULT_NAMESPACES
.. autoattribute:: version
.. autoattribute:: default_namespace

.. autoclass:: elementpath.XPath2Parser

Expand Down
4 changes: 2 additions & 2 deletions elementpath/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#
# @author Davide Brunato <[email protected]>
#
__version__ = '1.0.11'
__version__ = '1.0.12'
__author__ = "Davide Brunato"
__contact__ = "[email protected]"
__copyright__ = "Copyright 2018, SISSA"
Expand All @@ -18,7 +18,7 @@

from .exceptions import *
from .tdop_parser import Token, Parser
from .xpath_helpers import AttributeNode, NamespaceNode, UntypedAtomic
from .xpath_helpers import UntypedAtomic, AttributeNode, NamespaceNode
from .xpath_token import XPathToken
from .xpath_context import XPathContext
from .xpath1_parser import XPath1Parser
Expand Down
29 changes: 23 additions & 6 deletions elementpath/xpath1_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class XPath1Parser(Parser):

DEFAULT_NAMESPACES = XPATH_1_DEFAULT_NAMESPACES
"""
The default namespaces to use on the XPath instance. Those namespaces are updated in the
instance with the ones passed with the *namespaces* argument.
The default prefix-to-namespace associations of the XPath class. Those namespaces are updated
in the instance with the ones passed with the *namespaces* argument.
"""

def __init__(self, namespaces=None, variables=None, strict=True, *args, **kwargs):
Expand All @@ -99,8 +99,17 @@ def build_tokenizer(cls, name_pattern=XML_NCNAME_PATTERN):

@property
def version(self):
"""The XPath version string."""
return '1.0'

@property
def default_namespace(self):
"""
The default namespace. For XPath 1.0 this value is always `None` because the default
namespace is ignored (see https://www.w3.org/TR/1999/REC-xpath-19991116/#node-tests).
"""
return

@classmethod
def axis(cls, symbol, bp=0):
"""Register a token for a symbol that represents an XPath *axis*."""
Expand Down Expand Up @@ -216,16 +225,24 @@ def next_is_path_step_token(self):

@method('(name)')
def evaluate(self, context=None):
if context is None:
return None
elif is_element_node(context.item, self.value) or is_attribute_node(context.item, self.value):
return context.item
if context is not None:
value = self.value
if value[0] != '{' and self.parser.default_namespace:
value = u'{%s}%s' % (self.parser.default_namespace, value)

if is_element_node(context.item, value):
return context.item
elif is_attribute_node(context.item, value):
return context.item


@method('(name)')
def select(self, context=None):
if context is not None:
value = self.value
if value[0] != '{' and self.parser.default_namespace:
value = u'{%s}%s' % (self.parser.default_namespace, value)

for item in context.iter_children_or_self():
if is_attribute_node(item, value):
yield item[1]
Expand Down
4 changes: 4 additions & 0 deletions elementpath/xpath2_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ def __init__(self, namespaces=None, variables=None, strict=True, default_namespa
def version(self):
return '2.0'

@property
def default_namespace(self):
return self.namespaces.get('')

def advance(self, *symbols):
super(XPath2Parser, self).advance(*symbols)
if self.next_token.symbol == '(:':
Expand Down
225 changes: 113 additions & 112 deletions elementpath/xpath_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,126 @@
"""
import operator
from collections import namedtuple

from .compat import PY3
from .exceptions import ElementPathTypeError
from .namespaces import (
XML_BASE_QNAME, XML_ID_QNAME, XSI_TYPE_QNAME, XSI_NIL_QNAME, XSD_UNTYPED, XSD_UNTYPED_ATOMIC, prefixed_to_qname
)


class UntypedAtomic(object):
"""
Class for xs:untypedAtomic data. Provides special methods for comparing
and converting to basic data types.
:param value: The untyped value, usually a string.
"""
def __init__(self, value):
self.value = value

def __repr__(self):
return '%s(value=%r)' % (self.__class__.__name__, self.value)

def _get_operands(self, other, force_float=True):
"""
Returns a couple of operands, applying a cast to the instance value based on
the type of the *other* argument.
:param other: The other operand, that determines the cast for the untyped instance.
:param force_float: Force a conversion to float if *other* is an UntypedAtomic instance.
:return: A couple of values.
"""
if isinstance(other, UntypedAtomic):
if force_float:
return float(self.value), float(other.value)
else:
return self.value, other.value
elif isinstance(other, int):
return float(self.value), other
else:
return type(other)(self.value), other

def __eq__(self, other):
return operator.eq(*self._get_operands(other, force_float=False))

def __ne__(self, other):
return not operator.eq(*self._get_operands(other, force_float=False))

def __lt__(self, other):
return operator.lt(*self._get_operands(other))

def __le__(self, other):
return operator.le(*self._get_operands(other))

def __gt__(self, other):
return operator.gt(*self._get_operands(other))

def __ge__(self, other):
return operator.ge(*self._get_operands(other))

def __add__(self, other):
return operator.add(*self._get_operands(other))
__radd__ = __add__

def __sub__(self, other):
return operator.sub(*self._get_operands(other))

def __rsub__(self, other):
return operator.sub(*reversed(self._get_operands(other)))

def __mul__(self, other):
return operator.mul(*self._get_operands(other))
__rmul__ = __mul__

def __truediv__(self, other):
return operator.truediv(*self._get_operands(other))

def __rtruediv__(self, other):
return operator.truediv(*reversed(self._get_operands(other)))

def __int__(self):
return int(self.value)

def __float__(self):
return float(self.value)

def __bool__(self):
return bool(self.value)

def __abs__(self):
return abs(self.value)

if PY3:
def __str__(self):
return str(self.value)

def __bytes__(self):
return bytes(self.value, encoding='utf-8')

else:
def __unicode__(self):
return unicode(self.value)

def __str__(self):
try:
return str(self.value)
except UnicodeEncodeError:
return self.value.encode('utf-8')

def __bytes__(self):
return self.value.encode('utf-8')

def __div__(self, other):
return operator.truediv(*self._get_operands(other))

def __rdiv__(self, other):
return operator.truediv(*reversed(self._get_operands(other)))

def __long__(self):
return int(self.value)


###
# Utility functions for ElementTree's Element instances
def is_etree_element(obj):
Expand Down Expand Up @@ -288,115 +401,3 @@ def number_value(obj):
return float(node_string_value(obj) if is_xpath_node(obj) else obj)
except (TypeError, ValueError):
return float('nan')


class UntypedAtomic(object):
"""
Class for xs:untypedAtomic data. Provides special methods for comparing
and converting to basic data types.
:param value: The untyped value, usually a string.
"""
def __init__(self, value):
self.value = value

def __repr__(self):
return '%s(value=%r)' % (self.__class__.__name__, self.value)

def _get_operands(self, other, force_float=True):
"""
Returns a couple of operands, applying a cast to the instance value based on
the type of the *other* argument.
:param other: The other operand, that determines the cast for the untyped instance.
:param force_float: Force a conversion to float if *other* is an UntypedAtomic instance.
:return: A couple of values.
"""
if isinstance(other, UntypedAtomic):
if force_float:
return float(self.value), float(other.value)
else:
return self.value, other.value
elif isinstance(other, int):
return float(self.value), other
else:
return type(other)(self.value), other

def __eq__(self, other):
return operator.eq(*self._get_operands(other, force_float=False))

def __ne__(self, other):
return not operator.eq(*self._get_operands(other, force_float=False))

def __lt__(self, other):
return operator.lt(*self._get_operands(other))

def __le__(self, other):
return operator.le(*self._get_operands(other))

def __gt__(self, other):
return operator.gt(*self._get_operands(other))

def __ge__(self, other):
return operator.ge(*self._get_operands(other))

def __add__(self, other):
return operator.add(*self._get_operands(other))
__radd__ = __add__

def __sub__(self, other):
return operator.sub(*self._get_operands(other))

def __rsub__(self, other):
return operator.sub(*reversed(self._get_operands(other)))

def __mul__(self, other):
return operator.mul(*self._get_operands(other))
__rmul__ = __mul__

def __truediv__(self, other):
return operator.truediv(*self._get_operands(other))

def __rtruediv__(self, other):
return operator.truediv(*reversed(self._get_operands(other)))

def __int__(self):
return int(self.value)

def __float__(self):
return float(self.value)

def __bool__(self):
return bool(self.value)

def __abs__(self):
return abs(self.value)

if PY3:
def __str__(self):
return str(self.value)

def __bytes__(self):
return bytes(self.value, encoding='utf-8')

else:
def __unicode__(self):
return unicode(self.value)

def __str__(self):
try:
return str(self.value)
except UnicodeEncodeError:
return self.value.encode('utf-8')

def __bytes__(self):
return self.value.encode('utf-8')

def __div__(self, other):
return operator.truediv(*self._get_operands(other))

def __rdiv__(self, other):
return operator.truediv(*reversed(self._get_operands(other)))

def __long__(self):
return int(self.value)
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@

setup(
name='elementpath',
version='1.0.11',
version='1.0.12',
packages=['elementpath'],
author='Davide Brunato',
author_email='[email protected]',
url='https://github.com/brunato/elementpath',
license='MIT',
license='MIT',
description='XPath 1.0/2.0 parsers and selectors for ElementTree.',
long_description=long_description,
classifiers=[
Expand Down
Loading

0 comments on commit 8c094eb

Please sign in to comment.