Skip to content

Commit

Permalink
Fix complex content extension with mixed content base (issue #334)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunato committed Feb 8, 2023
1 parent 581058c commit f4e36bd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 23 deletions.
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
# The short X.Y version.
version = '2.2'
# The full version, including alpha/beta/rc tags.
release = '2.2.0'
release = '2.2.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

setup(
name='xmlschema',
version='2.2.0',
version='2.2.1',
packages=find_packages(include=['xmlschema*']),
package_data={
'xmlschema': ['py.typed', 'locale/**/*.mo', 'locale/**/*.po', 'schemas/*/*.xsd'],
Expand Down
27 changes: 27 additions & 0 deletions tests/validators/test_complex_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,33 @@ def test_restriction_with_empty_particle__issue_323(self):
self.assertIsNone(schema.build())
self.assertTrue(schema.built)

def test_mixed_content_extension__issue_334(self):
schema = self.schema_class("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:complexType name="mixedContentType" mixed="true">
<xs:sequence>
<xs:element name="elem1"/>
</xs:sequence>
</xs:complexType>

<xs:element name="foo">
<xs:complexType>
<xs:complexContent>
<xs:extension base="mixedContentType">
<xs:attribute name="bar" type="xs:string" use="required" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>

</xs:schema>
""")

self.assertTrue(schema.types['mixedContentType'].mixed)
self.assertTrue(schema.elements['foo'].type.mixed)
self.assertTrue(schema.elements['foo'].type.content.mixed)


class TestXsd11ComplexType(TestXsdComplexType):

Expand Down
2 changes: 1 addition & 1 deletion xmlschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
XsdComponent, XsdType, XsdElement, XsdAttribute
)

__version__ = '2.2.0'
__version__ = '2.2.1'
__author__ = "Davide Brunato"
__contact__ = "brunato@sissa.it"
__copyright__ = "Copyright 2016-2023, SISSA"
Expand Down
46 changes: 26 additions & 20 deletions xmlschema/validators/complex_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ def _parse(self) -> None:
except ValueError as err:
self.parse_error(err)

if 'mixed' in self.elem.attrib:
if 'mixed' not in self.elem.attrib:
has_mixed_attribute = False
else:
has_mixed_attribute = True
if self.elem.attrib['mixed'].strip() in {'true', '1'}:
self.mixed = True

Expand Down Expand Up @@ -181,6 +184,7 @@ def _parse(self) -> None:
#
# complexType with complexContent restriction/extension
if 'mixed' in content_elem.attrib:
has_mixed_attribute = True
mixed = content_elem.attrib['mixed'] in ('true', '1')
if mixed is not self.mixed:
self.mixed = mixed
Expand All @@ -201,7 +205,7 @@ def _parse(self) -> None:
if derivation_elem.tag == XSD_RESTRICTION:
self._parse_complex_content_restriction(derivation_elem, self.base_type)
else:
self._parse_complex_content_extension(derivation_elem, self.base_type)
self._parse_complex_content_extension(derivation_elem, self.base_type, has_mixed_attribute)

if content_elem is not self.elem[-1]:
k = 2 if content_elem is not self.elem[0] else 1
Expand Down Expand Up @@ -407,7 +411,8 @@ def _parse_complex_content_restriction(self, elem: ElementType, base_type: Any)
self._parse_content_tail(elem, derivation='restriction',
base_attributes=base_type.attributes)

def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) -> None:
def _parse_complex_content_extension(self, elem: ElementType, base_type: Any,
has_mixed_attribute: bool) -> None:
if 'extension' in base_type.final:
msg = _("the base type is not derivable by extension")
self.parse_error(msg)
Expand Down Expand Up @@ -477,7 +482,7 @@ def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) ->
if base_type.content.model == 'all' and base_type.content and group:
msg = _("XSD 1.0 does not allow extension of a not empty 'all' model group")
self.parse_error(msg)
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
if base_type.mixed is not self.mixed and base_type.name != XSD_ANY_TYPE:
msg = _("base has a different content type (mixed=%r) "
"and the extension group is not empty")
self.parse_error(msg % base_type.mixed, elem)
Expand All @@ -488,12 +493,15 @@ def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) ->
elif base_type.has_simple_content():
self.content = base_type.content
else:
if base_type.mixed is not self.mixed and base_type.name != XSD_ANY_TYPE:
if has_mixed_attribute:
msg = _("extended type has a mixed content but the base is element-only")
self.parse_error(msg, elem)
self.mixed = base_type.mixed

self.content = self.schema.create_empty_content_group(self)
self.content.append(base_type.content)
self.content.elem.append(base_type.content.elem)
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
msg = _("extended type has a mixed content but the base is element-only")
self.parse_error(msg, elem)

self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)

Expand Down Expand Up @@ -875,7 +883,8 @@ def _parse(self) -> None:

self.attributes.update((k, v) for k, v in self.default_attributes.items())

def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) -> None:
def _parse_complex_content_extension(self, elem: ElementType, base_type: Any,
has_mixed_attribute: bool) -> None:
# Complex content extension with simple base is forbidden XSD 1.1.
# For the detailed rule refer to XSD 1.1 documentation:
# https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#sec-cos-ct-extends
Expand Down Expand Up @@ -911,19 +920,13 @@ def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) ->
self.content = self.schema.xsd_group_class(
group_elem, self.schema, self
)
elif base_type.content.max_occurs is None:
self.content = self.schema.create_empty_content_group(
parent=self,
model=base_type.content.model,
minOccurs=str(base_type.content.min_occurs),
maxOccurs='unbounded',
)
else:
max_occurs = base_type.content.max_occurs
self.content = self.schema.create_empty_content_group(
parent=self,
model=base_type.content.model,
minOccurs=str(base_type.content.min_occurs),
maxOccurs=str(base_type.content.max_occurs),
maxOccurs='unbounded' if max_occurs is None else str(max_occurs),
)

elif base_type.mixed:
Expand Down Expand Up @@ -985,7 +988,7 @@ def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) ->
content.extend(group)
content.elem.extend(group.elem)

if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
if base_type.mixed is not self.mixed and base_type.name != XSD_ANY_TYPE:
msg = _("base has a different content type (mixed=%r) "
"and the extension group is not empty.")
self.parse_error(msg % base_type.mixed, elem)
Expand All @@ -997,12 +1000,15 @@ def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) ->
elif base_type.has_simple_content():
self.content = base_type.content
else:
if base_type.mixed is not self.mixed and base_type.name != XSD_ANY_TYPE:
if has_mixed_attribute:
msg = _("extended type has a mixed content but the base is element-only")
self.parse_error(msg, elem)
self.mixed = base_type.mixed

self.content = self.schema.create_empty_content_group(self)
self.content.append(base_type.content)
self.content.elem.append(base_type.content.elem)
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
msg = _("extended type has a mixed content but the base is element-only")
self.parse_error(msg, elem)

if self.open_content is None:
default_open_content = self.default_open_content
Expand Down

0 comments on commit f4e36bd

Please sign in to comment.