Skip to content

Commit

Permalink
Split context in cycle statements and set context with typed nodes
Browse files Browse the repository at this point in the history
  - 'for', 'some' and 'every' statements split context another
    time on calling iter_product() to preserve the original
    context item
  - set context.item with typed node before yield statement
  • Loading branch information
brunato committed Sep 13, 2020
1 parent bbe4bc4 commit e74f371
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 20 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
CHANGELOG
*********

`v2.0.3`_ (2020-09-13)
======================
* Fix context handling in cycle statements
* Change constructor's label to 'constructor function'

`v2.0.2`_ (2020-09-03)
======================
* Add regex translator to package API
Expand Down Expand Up @@ -244,4 +249,4 @@ CHANGELOG
.. _v2.0.0: https://github.com/sissaschool/elementpath/compare/v1.4.6...v2.0.0
.. _v2.0.1: https://github.com/sissaschool/elementpath/compare/v2.0.0...v2.0.1
.. _v2.0.2: https://github.com/sissaschool/elementpath/compare/v2.0.1...v2.0.2

.. _v2.0.3: https://github.com/sissaschool/elementpath/compare/v2.0.2...v2.0.3
6 changes: 4 additions & 2 deletions elementpath/schema_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,12 @@ def iter_atomic_types(self):
@abstractmethod
def get_primitive_type(self, xsd_type):
"""
Returns the primitive type of an XSD type.
Returns the type at base of the definition of an XSD type. For an atomic type
is effectively the primitive type. For a list is the primitive type of the item.
For a union is the base union type. For a complex type is xs:anyType.
:param xsd_type: an XSD type instance.
:return: an XSD builtin primitive type.
:return: an XSD type instance.
"""


Expand Down
19 changes: 13 additions & 6 deletions elementpath/xpath1/xpath1_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,14 @@ def select(self, context=None):
else:
self.xsd_types = self.parser.schema

yield self.get_typed_node(item)
context.item = self.get_typed_node(item)
yield context.item
else:
# XSD typed selection
for item in context.iter_children_or_self():
if match_attribute_node(item, name) or match_element_node(item, tag):
yield self.get_typed_node(item)
context.item = self.get_typed_node(item)
yield context.item


###
Expand Down Expand Up @@ -556,13 +558,16 @@ def select(self, context=None):
self.add_xsd_type(xsd_node)
else:
self.xsd_types = self.parser.schema
yield self.get_typed_node(item)

context.item = self.get_typed_node(item)
yield context.item

else:
# XSD typed selection
for item in context.iter_children_or_self():
if match_attribute_node(item, name) or match_element_node(item, name):
yield self.get_typed_node(item)
context.item = self.get_typed_node(item)
yield context.item


###
Expand Down Expand Up @@ -613,7 +618,8 @@ def select(self, context=None):
# XSD typed selection
for item in context.iter_children_or_self():
if match_attribute_node(item, name) or match_element_node(item, name):
yield self.get_typed_node(item)
context.item = self.get_typed_node(item)
yield context.item


###
Expand Down Expand Up @@ -675,7 +681,8 @@ def select(self, context=None):
if context.item is None:
pass
elif context.is_principal_node_kind():
yield self.get_typed_node(item)
context.item = self.get_typed_node(item)
yield context.item


@method(nullary('.'))
Expand Down
4 changes: 2 additions & 2 deletions elementpath/xpath2/xpath2_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def evaluate(self, context=None):
varnames = [self[k][0].value for k in range(0, len(self) - 1, 2)]
selectors = [self[k].select for k in range(1, len(self) - 1, 2)]

for results in context.iter_product(selectors, varnames):
for results in copy(context).iter_product(selectors, varnames):
context.variables.update(x for x in zip(varnames, results))
if self.boolean_value([x for x in self[-1].select(copy(context))]):
if some:
Expand Down Expand Up @@ -643,7 +643,7 @@ def select(self, context=None):
varnames = [self[k][0].value for k in range(0, len(self) - 1, 2)]
selectors = [self[k].select for k in range(1, len(self) - 1, 2)]

for results in context.iter_product(selectors, varnames):
for results in copy(context).iter_product(selectors, varnames):
context.variables.update(x for x in zip(varnames, results))
yield from self[-1].select(copy(context))

Expand Down
30 changes: 23 additions & 7 deletions elementpath/xpath_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,17 @@ def get_argument(self, context, index=0, required=False, default_to_context=Fals
for k, result in enumerate(selector(copy(context))):
if k == 0:
item = result
elif not self.parser.compatibility_mode:
elif self.parser.compatibility_mode:
break
elif isinstance(context, XPathSchemaContext):
# Multiple schema nodes are ignored but do not raise. The target
# of schema context selection is XSD type association and multiple
# nodes coherency is already checked at schema level.
break
else:
raise self.wrong_context_type(
"a sequence of more than one item is not allowed as argument"
)
else:
break
else:
if item is None:
if not required:
Expand Down Expand Up @@ -285,15 +290,20 @@ def get_atomized_operand(self, context=None):
except StopIteration:
return
else:
item = getattr(context, 'item', None)

try:
next(selector)
except StopIteration:
if isinstance(value, UntypedAtomic):
value = str(value)
if isinstance(context, XPathSchemaContext):
return value
if self.xsd_types and isinstance(value, str):
xsd_type = self.get_xsd_type(context.item)

if not isinstance(context, XPathSchemaContext) and \
item is not None and \
self.xsd_types and \
isinstance(value, str):

xsd_type = self.get_xsd_type(item)
if xsd_type is None or xsd_type.name in XSD_SPECIAL_TYPES:
pass
else:
Expand All @@ -302,6 +312,7 @@ def get_atomized_operand(self, context=None):
except (TypeError, ValueError):
msg = "Type {!r} is not appropriate for the context"
raise self.wrong_context_type(msg.format(type(value)))

return value
else:
msg = "atomized operand is a sequence of length greater than one"
Expand Down Expand Up @@ -668,6 +679,8 @@ def get_xsd_type(self, item):
xsd_type = self.xsd_types.get(item)
elif isinstance(item, AttributeNode):
xsd_type = self.xsd_types.get(item[0])
elif isinstance(item, (TypedAttribute, TypedElement)):
return item.type
else:
xsd_type = self.xsd_types.get(item.tag)

Expand Down Expand Up @@ -702,6 +715,9 @@ def get_typed_node(self, item):
:return: a TypedAttribute or a TypedElement, or the argument \
if it's not matching any associated XSD type.
"""
if isinstance(item, (TypedAttribute, TypedElement)):
return item

xsd_type = self.get_xsd_type(item)
if not xsd_type:
return item
Expand Down
4 changes: 2 additions & 2 deletions publiccode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ publiccodeYmlVersion: '0.2'
name: elementpath
url: 'https://github.com/sissaschool/elementpath'
landingURL: 'https://github.com/sissaschool/elementpath'
releaseDate: '2020-09-03'
softwareVersion: v2.0.2
releaseDate: '2020-09-13'
softwareVersion: v2.0.3
developmentStatus: stable
platforms:
- linux
Expand Down

0 comments on commit e74f371

Please sign in to comment.