Fix default values decoding as reported by issue #108
- Default and fixed values are inserted during the decode or encode process - Update tox.ini to include coverage and flake8 to environments and an optional environment to build source and wheel packages
This commit is contained in:
parent
0e9a3cb4c0
commit
d21ac11dde
|
@ -0,0 +1,4 @@
|
||||||
|
[run]
|
||||||
|
branch = True
|
||||||
|
source = xmlschema/
|
||||||
|
omit = xmlschema/tests/*
|
|
@ -6,6 +6,7 @@
|
||||||
*.json
|
*.json
|
||||||
.idea/
|
.idea/
|
||||||
.tox/
|
.tox/
|
||||||
|
.coverage
|
||||||
.ipynb_checkpoints/
|
.ipynb_checkpoints/
|
||||||
doc/_*/
|
doc/_*/
|
||||||
dist/
|
dist/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Requirements for setup a development environment for the xmlschema package.
|
# Requirements for setup a development environment for the xmlschema package.
|
||||||
setuptools
|
setuptools
|
||||||
tox
|
tox
|
||||||
|
coverage
|
||||||
elementpath~=1.1.7
|
elementpath~=1.1.7
|
||||||
lxml
|
lxml
|
||||||
memory_profiler
|
memory_profiler
|
||||||
|
|
40
tox.ini
40
tox.ini
|
@ -4,14 +4,20 @@
|
||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py27, py34, py35, py36, py37
|
envlist = py27, py35, py36, py37, py38, docs, flake8, coverage
|
||||||
|
skip_missing_interpreters = true
|
||||||
toxworkdir = {homedir}/.tox/xmlschema
|
toxworkdir = {homedir}/.tox/xmlschema
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
lxml
|
lxml
|
||||||
elementpath~=1.1.7
|
elementpath~=1.1.7
|
||||||
|
docs: Sphinx
|
||||||
|
docs: sphinx_rtd_theme
|
||||||
|
flake8: flake8
|
||||||
|
coverage: coverage
|
||||||
commands = python xmlschema/tests/test_all.py {posargs}
|
commands = python xmlschema/tests/test_all.py {posargs}
|
||||||
|
whitelist_externals = make
|
||||||
|
|
||||||
[testenv:py27]
|
[testenv:py27]
|
||||||
deps =
|
deps =
|
||||||
|
@ -19,3 +25,35 @@ deps =
|
||||||
elementpath~=1.1.7
|
elementpath~=1.1.7
|
||||||
pathlib2
|
pathlib2
|
||||||
commands = python xmlschema/tests/test_all.py {posargs}
|
commands = python xmlschema/tests/test_all.py {posargs}
|
||||||
|
|
||||||
|
[testenv:py38]
|
||||||
|
deps = elementpath~=1.1.7
|
||||||
|
commands = python xmlschema/tests/test_all.py {posargs}
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
commands =
|
||||||
|
make -C doc html
|
||||||
|
make -C doc latexpdf
|
||||||
|
make -C doc doctest
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 119
|
||||||
|
|
||||||
|
[testenv:flake8]
|
||||||
|
commands =
|
||||||
|
flake8 --ignore=F401,F403,F405,F811,F821 xmlschema
|
||||||
|
|
||||||
|
[testenv:coverage]
|
||||||
|
commands =
|
||||||
|
coverage run -p -m unittest
|
||||||
|
coverage combine
|
||||||
|
coverage report -m
|
||||||
|
|
||||||
|
[testenv:build]
|
||||||
|
deps =
|
||||||
|
setuptools
|
||||||
|
wheel
|
||||||
|
commands =
|
||||||
|
python setup.py clean --all
|
||||||
|
python setup.py sdist --dist-dir {toxinidir}/dist
|
||||||
|
python setup.py bdist_wheel --universal --dist-dir {toxinidir}/dist
|
||||||
|
|
|
@ -37,7 +37,7 @@ __status__ = "Production/Stable"
|
||||||
# API deprecation warnings
|
# API deprecation warnings
|
||||||
def XMLSchema_v1_0(*args, **kwargs):
|
def XMLSchema_v1_0(*args, **kwargs):
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn("XMLSchema_v1_0 class name has been replaced by XMLSchema10 "
|
warnings.warn("XMLSchema_v1_0 class name has been replaced by XMLSchema10 "
|
||||||
"and will be removed in 1.1 version", DeprecationWarning, stacklevel=2)
|
"and will be removed in 1.1 version", DeprecationWarning, stacklevel=2)
|
||||||
return XMLSchema10(*args, **kwargs)
|
return XMLSchema10(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -165,18 +165,18 @@ def iterparse_character_group(s, expand_ranges=False):
|
||||||
k = next(string_iter)
|
k = next(string_iter)
|
||||||
end_char = s[k]
|
end_char = s[k]
|
||||||
if end_char == '\\' and (k < length - 1):
|
if end_char == '\\' and (k < length - 1):
|
||||||
if s[k+1] in r'-|.^?*+{}()[]':
|
if s[k + 1] in r'-|.^?*+{}()[]':
|
||||||
k = next(string_iter)
|
k = next(string_iter)
|
||||||
end_char = s[k]
|
end_char = s[k]
|
||||||
elif s[k+1] in r'sSdDiIcCwWpP':
|
elif s[k + 1] in r'sSdDiIcCwWpP':
|
||||||
msg = "bad character range '%s-\\%s' at position %d: %r" % (char, s[k+1], k-2, s)
|
msg = "bad character range '%s-\\%s' at position %d: %r" % (char, s[k + 1], k - 2, s)
|
||||||
raise XMLSchemaRegexError(msg)
|
raise XMLSchemaRegexError(msg)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
msg = "bad character range '%s-%s' at position %d: %r" % (char, s[-1], k-2, s)
|
msg = "bad character range '%s-%s' at position %d: %r" % (char, s[-1], k - 2, s)
|
||||||
raise XMLSchemaRegexError(msg)
|
raise XMLSchemaRegexError(msg)
|
||||||
|
|
||||||
if ord(char) > ord(end_char):
|
if ord(char) > ord(end_char):
|
||||||
msg = "bad character range '%s-%s' at position %d: %r" % (char, end_char, k-2, s)
|
msg = "bad character range '%s-%s' at position %d: %r" % (char, end_char, k - 2, s)
|
||||||
raise XMLSchemaRegexError(msg)
|
raise XMLSchemaRegexError(msg)
|
||||||
elif expand_ranges:
|
elif expand_ranges:
|
||||||
for cp in range(ord(char) + 1, ord(end_char) + 1):
|
for cp in range(ord(char) + 1, ord(end_char) + 1):
|
||||||
|
@ -194,7 +194,7 @@ def iterparse_character_group(s, expand_ranges=False):
|
||||||
raise XMLSchemaRegexError("bad character %r at position %d" % (s[k], k))
|
raise XMLSchemaRegexError("bad character %r at position %d" % (s[k], k))
|
||||||
escaped = on_range = False
|
escaped = on_range = False
|
||||||
char = s[k]
|
char = s[k]
|
||||||
if k >= length-1 or s[k+1] != '-':
|
if k >= length - 1 or s[k + 1] != '-':
|
||||||
yield ord(char)
|
yield ord(char)
|
||||||
elif s[k] == '\\':
|
elif s[k] == '\\':
|
||||||
if escaped:
|
if escaped:
|
||||||
|
@ -209,7 +209,7 @@ def iterparse_character_group(s, expand_ranges=False):
|
||||||
yield ord('\\')
|
yield ord('\\')
|
||||||
on_range = False
|
on_range = False
|
||||||
char = s[k]
|
char = s[k]
|
||||||
if k >= length-1 or s[k+1] != '-':
|
if k >= length - 1 or s[k + 1] != '-':
|
||||||
yield ord(char)
|
yield ord(char)
|
||||||
if escaped:
|
if escaped:
|
||||||
yield ord('\\')
|
yield ord('\\')
|
||||||
|
|
|
@ -145,7 +145,7 @@ class XMLSchemaConverter(NamespaceMapper):
|
||||||
"""
|
"""
|
||||||
if not content:
|
if not content:
|
||||||
return
|
return
|
||||||
|
|
||||||
map_qname = self.map_qname
|
map_qname = self.map_qname
|
||||||
for name, value, xsd_child in content:
|
for name, value, xsd_child in content:
|
||||||
try:
|
try:
|
||||||
|
@ -274,7 +274,7 @@ class XMLSchemaConverter(NamespaceMapper):
|
||||||
elif name == ns_prefix:
|
elif name == ns_prefix:
|
||||||
self[''] = value
|
self[''] = value
|
||||||
elif name.startswith('%s:' % ns_prefix):
|
elif name.startswith('%s:' % ns_prefix):
|
||||||
self[name[len(ns_prefix)+1:]] = value
|
self[name[len(ns_prefix) + 1:]] = value
|
||||||
elif attr_prefix and name.startswith(attr_prefix):
|
elif attr_prefix and name.startswith(attr_prefix):
|
||||||
name = name[len(attr_prefix):]
|
name = name[len(attr_prefix):]
|
||||||
attributes[unmap_attribute_qname(name)] = value
|
attributes[unmap_attribute_qname(name)] = value
|
||||||
|
@ -314,6 +314,7 @@ class ParkerConverter(XMLSchemaConverter):
|
||||||
XML Schema based converter class for Parker convention.
|
XML Schema based converter class for Parker convention.
|
||||||
|
|
||||||
ref: http://wiki.open311.org/JSON_and_XML_Conversion/#the-parker-convention
|
ref: http://wiki.open311.org/JSON_and_XML_Conversion/#the-parker-convention
|
||||||
|
ref: https://developer.mozilla.org/en-US/docs/Archive/JXON#The_Parker_Convention
|
||||||
|
|
||||||
:param namespaces: Map from namespace prefixes to URI.
|
:param namespaces: Map from namespace prefixes to URI.
|
||||||
:param dict_class: Dictionary class to use for decoded data. Default is `dict` for \
|
:param dict_class: Dictionary class to use for decoded data. Default is `dict` for \
|
||||||
|
@ -780,8 +781,8 @@ class JsonMLConverter(XMLSchemaConverter):
|
||||||
|
|
||||||
if data_len <= content_index:
|
if data_len <= content_index:
|
||||||
return ElementData(xsd_element.name, None, [], attributes)
|
return ElementData(xsd_element.name, None, [], attributes)
|
||||||
elif data_len == content_index + 1 and (xsd_element.type.is_simple()
|
elif data_len == content_index + 1 and \
|
||||||
or xsd_element.type.has_simple_content()):
|
(xsd_element.type.is_simple() or xsd_element.type.has_simple_content()):
|
||||||
return ElementData(xsd_element.name, obj[content_index], [], attributes)
|
return ElementData(xsd_element.name, obj[content_index], [], attributes)
|
||||||
else:
|
else:
|
||||||
cdata_num = iter(range(1, data_len))
|
cdata_num = iter(range(1, data_len))
|
||||||
|
|
|
@ -56,4 +56,3 @@ class XMLSchemaRegexError(XMLSchemaException, ValueError):
|
||||||
|
|
||||||
class XMLSchemaWarning(Warning):
|
class XMLSchemaWarning(Warning):
|
||||||
"""Base warning class for the XMLSchema package."""
|
"""Base warning class for the XMLSchema package."""
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,8 @@ I_SHORTCUT_SET = UnicodeSubset(I_SHORTCUT_REPLACE)
|
||||||
C_SHORTCUT_SET = UnicodeSubset(C_SHORTCUT_REPLACE)
|
C_SHORTCUT_SET = UnicodeSubset(C_SHORTCUT_REPLACE)
|
||||||
W_SHORTCUT_SET = UnicodeSubset()
|
W_SHORTCUT_SET = UnicodeSubset()
|
||||||
W_SHORTCUT_SET._code_points = sorted(
|
W_SHORTCUT_SET._code_points = sorted(
|
||||||
UNICODE_CATEGORIES['P'].code_points + UNICODE_CATEGORIES['Z'].code_points +
|
UNICODE_CATEGORIES['P'].code_points + UNICODE_CATEGORIES['Z'].code_points + UNICODE_CATEGORIES['C'].code_points,
|
||||||
UNICODE_CATEGORIES['C'].code_points, key=lambda x: x if isinstance(x, int) else x[0]
|
key=lambda x: x if isinstance(x, int) else x[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Single and Multi character escapes
|
# Single and Multi character escapes
|
||||||
|
@ -312,9 +312,9 @@ def get_python_regex(xml_regex):
|
||||||
elif ch in ('?', '+', '*'):
|
elif ch in ('?', '+', '*'):
|
||||||
if pos == 0:
|
if pos == 0:
|
||||||
raise XMLSchemaRegexError("unexpected quantifier %r at position %d: %r" % (ch, pos, xml_regex))
|
raise XMLSchemaRegexError("unexpected quantifier %r at position %d: %r" % (ch, pos, xml_regex))
|
||||||
elif pos < xml_regex_len - 1 and xml_regex[pos+1] in ('?', '+', '*', '{'):
|
elif pos < xml_regex_len - 1 and xml_regex[pos + 1] in ('?', '+', '*', '{'):
|
||||||
raise XMLSchemaRegexError(
|
raise XMLSchemaRegexError(
|
||||||
"unexpected meta character %r at position %d: %r" % (xml_regex[pos+1], pos+1, xml_regex)
|
"unexpected meta character %r at position %d: %r" % (xml_regex[pos + 1], pos + 1, xml_regex)
|
||||||
)
|
)
|
||||||
regex.append(ch)
|
regex.append(ch)
|
||||||
elif ch == '\\':
|
elif ch == '\\':
|
||||||
|
|
|
@ -64,7 +64,7 @@ class XMLSchemaTestCase(unittest.TestCase):
|
||||||
etree_register_namespace(prefix='', uri=XSD_NAMESPACE)
|
etree_register_namespace(prefix='', uri=XSD_NAMESPACE)
|
||||||
etree_register_namespace(prefix='ns', uri="ns")
|
etree_register_namespace(prefix='ns', uri="ns")
|
||||||
SCHEMA_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
|
SCHEMA_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<schema xmlns:ns="ns" xmlns="http://www.w3.org/2001/XMLSchema"
|
<schema xmlns:ns="ns" xmlns="http://www.w3.org/2001/XMLSchema"
|
||||||
targetNamespace="ns" elementFormDefault="unqualified" version="{0}">
|
targetNamespace="ns" elementFormDefault="unqualified" version="{0}">
|
||||||
{1}
|
{1}
|
||||||
</schema>"""
|
</schema>"""
|
||||||
|
@ -131,7 +131,7 @@ class XMLSchemaTestCase(unittest.TestCase):
|
||||||
root = etree_element('schema', attrib={
|
root = etree_element('schema', attrib={
|
||||||
'xmlns:ns': "ns",
|
'xmlns:ns': "ns",
|
||||||
'xmlns': "http://www.w3.org/2001/XMLSchema",
|
'xmlns': "http://www.w3.org/2001/XMLSchema",
|
||||||
'targetNamespace': "ns",
|
'targetNamespace': "ns",
|
||||||
'elementFormDefault': "qualified",
|
'elementFormDefault': "qualified",
|
||||||
'version': self.schema_class.XSD_VERSION,
|
'version': self.schema_class.XSD_VERSION,
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||||
<xs:element name="subject_name" type="xs:string" />
|
<xs:element name="subject_name" type="xs:string" />
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="name" fixed="BAR" use="required"/>
|
<xs:attribute name="name" fixed="BAR" use="required" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:schema>
|
</xs:schema>
|
|
@ -270,7 +270,7 @@ class TestModelValidation(XMLSchemaTestCase):
|
||||||
self.assertEqual(model.element, group[2])
|
self.assertEqual(model.element, group[2])
|
||||||
self.check_advance_false(model) # <attribute> don't match
|
self.check_advance_false(model) # <attribute> don't match
|
||||||
self.assertEqual(model.element, group[3])
|
self.assertEqual(model.element, group[3])
|
||||||
self.check_advance_false(model, [(group, 0, group[0][0][:]+group[1:])]) # <notation> don't match
|
self.check_advance_false(model, [(group, 0, group[0][0][:] + group[1:])]) # <notation> don't match
|
||||||
|
|
||||||
model.restart()
|
model.restart()
|
||||||
self.assertEqual(model.element, group[0][0][0])
|
self.assertEqual(model.element, group[0][0][0])
|
||||||
|
|
|
@ -121,11 +121,8 @@ class TestPackaging(unittest.TestCase):
|
||||||
message = "\nFound a debug missing statement at line %d or file %r: %r"
|
message = "\nFound a debug missing statement at line %d or file %r: %r"
|
||||||
filename = None
|
filename = None
|
||||||
file_excluded = []
|
file_excluded = []
|
||||||
files = (
|
files = glob.glob(os.path.join(self.source_dir, '*.py')) + \
|
||||||
glob.glob(os.path.join(self.source_dir, '*.py')) +
|
|
||||||
glob.glob(os.path.join(self.source_dir, 'validators/*.py'))
|
glob.glob(os.path.join(self.source_dir, 'validators/*.py'))
|
||||||
)
|
|
||||||
|
|
||||||
for line in fileinput.input(files):
|
for line in fileinput.input(files):
|
||||||
if fileinput.isfirstline():
|
if fileinput.isfirstline():
|
||||||
filename = fileinput.filename()
|
filename = fileinput.filename()
|
||||||
|
|
|
@ -55,11 +55,11 @@ class TestXMLSchema10(XMLSchemaTestCase):
|
||||||
source = """
|
source = """
|
||||||
<complexType name="targetType">
|
<complexType name="targetType">
|
||||||
{0}
|
{0}
|
||||||
</complexType>
|
</complexType>
|
||||||
<complexType name="restrictedType">
|
<complexType name="restrictedType">
|
||||||
<{1}Content>
|
<{1}Content>
|
||||||
<restriction base="ns:targetType">
|
<restriction base="ns:targetType">
|
||||||
{2}
|
{2}
|
||||||
</restriction>
|
</restriction>
|
||||||
</{1}Content>
|
</{1}Content>
|
||||||
</complexType>
|
</complexType>
|
||||||
|
@ -95,7 +95,6 @@ class TestXMLSchema10(XMLSchemaTestCase):
|
||||||
<annotation/>
|
<annotation/>
|
||||||
<list itemType="string"/>
|
<list itemType="string"/>
|
||||||
</simpleType>
|
</simpleType>
|
||||||
|
|
||||||
<simpleType name="test_union">
|
<simpleType name="test_union">
|
||||||
<annotation/>
|
<annotation/>
|
||||||
<union memberTypes="string integer boolean"/>
|
<union memberTypes="string integer boolean"/>
|
||||||
|
@ -156,7 +155,6 @@ class TestXMLSchema10(XMLSchemaTestCase):
|
||||||
<totalDigits value="20" />
|
<totalDigits value="20" />
|
||||||
</restriction>
|
</restriction>
|
||||||
</simpleType>
|
</simpleType>
|
||||||
|
|
||||||
<simpleType name="ntype">
|
<simpleType name="ntype">
|
||||||
<restriction base="ns:dtype">
|
<restriction base="ns:dtype">
|
||||||
<totalDigits value="3" />
|
<totalDigits value="3" />
|
||||||
|
@ -360,11 +358,9 @@ class TestXMLSchema10(XMLSchemaTestCase):
|
||||||
<maxInclusive value="100"/>
|
<maxInclusive value="100"/>
|
||||||
</restriction>
|
</restriction>
|
||||||
</simpleType>
|
</simpleType>
|
||||||
|
|
||||||
<simpleType name="Integer">
|
<simpleType name="Integer">
|
||||||
<union memberTypes="int ns:IntegerString"/>
|
<union memberTypes="int ns:IntegerString"/>
|
||||||
</simpleType>
|
</simpleType>
|
||||||
|
|
||||||
<simpleType name="IntegerString">
|
<simpleType name="IntegerString">
|
||||||
<restriction base="string">
|
<restriction base="string">
|
||||||
<pattern value="-?[0-9]+(\.[0-9]+)?%"/>
|
<pattern value="-?[0-9]+(\.[0-9]+)?%"/>
|
||||||
|
@ -421,7 +417,7 @@ class TestXMLSchema10(XMLSchemaTestCase):
|
||||||
|
|
||||||
def test_base_schemas(self):
|
def test_base_schemas(self):
|
||||||
from xmlschema.validators.schema import XML_SCHEMA_FILE
|
from xmlschema.validators.schema import XML_SCHEMA_FILE
|
||||||
schema = self.schema_class(XML_SCHEMA_FILE)
|
self.schema_class(XML_SCHEMA_FILE)
|
||||||
|
|
||||||
def test_recursive_complex_type(self):
|
def test_recursive_complex_type(self):
|
||||||
schema = self.schema_class("""
|
schema = self.schema_class("""
|
||||||
|
@ -567,7 +563,7 @@ class TestXMLSchema11(TestXMLSchema10):
|
||||||
self.assertTrue(xsd_type.is_valid(etree_element('a', attrib={'min': '25', 'max': '100'})))
|
self.assertTrue(xsd_type.is_valid(etree_element('a', attrib={'min': '25', 'max': '100'})))
|
||||||
|
|
||||||
def test_open_content(self):
|
def test_open_content(self):
|
||||||
schema = self.check_schema("""
|
self.check_schema("""
|
||||||
<element name="Book">
|
<element name="Book">
|
||||||
<complexType>
|
<complexType>
|
||||||
<openContent mode="interleave">
|
<openContent mode="interleave">
|
||||||
|
@ -583,6 +579,7 @@ class TestXMLSchema11(TestXMLSchema10):
|
||||||
</complexType>
|
</complexType>
|
||||||
</element>""")
|
</element>""")
|
||||||
|
|
||||||
|
|
||||||
def make_schema_test_class(test_file, test_args, test_num, schema_class, check_with_lxml):
|
def make_schema_test_class(test_file, test_args, test_num, schema_class, check_with_lxml):
|
||||||
"""
|
"""
|
||||||
Creates a schema test class.
|
Creates a schema test class.
|
||||||
|
@ -695,7 +692,7 @@ def make_schema_test_class(test_file, test_args, test_num, schema_class, check_w
|
||||||
|
|
||||||
# Check with lxml.etree.XMLSchema class
|
# Check with lxml.etree.XMLSchema class
|
||||||
if check_with_lxml and lxml_etree is not None:
|
if check_with_lxml and lxml_etree is not None:
|
||||||
self.check_lxml_schema(xmlschema_time=time.time()-start_time)
|
self.check_lxml_schema(xmlschema_time=time.time() - start_time)
|
||||||
self.check_errors(xsd_file, expected_errors)
|
self.check_errors(xsd_file, expected_errors)
|
||||||
|
|
||||||
TestSchema.__name__ = TestSchema.__qualname__ = str('TestSchema{0:03}'.format(test_num))
|
TestSchema.__name__ = TestSchema.__qualname__ = str('TestSchema{0:03}'.format(test_num))
|
||||||
|
|
|
@ -24,7 +24,7 @@ from elementpath import datatypes
|
||||||
|
|
||||||
import xmlschema
|
import xmlschema
|
||||||
from xmlschema import (
|
from xmlschema import (
|
||||||
XMLSchemaEncodeError, XMLSchemaValidationError, XMLSchema, ParkerConverter,
|
XMLSchemaEncodeError, XMLSchemaValidationError, ParkerConverter,
|
||||||
BadgerFishConverter, AbderaConverter, JsonMLConverter
|
BadgerFishConverter, AbderaConverter, JsonMLConverter
|
||||||
)
|
)
|
||||||
from xmlschema.compat import unicode_type, ordered_dict_class
|
from xmlschema.compat import unicode_type, ordered_dict_class
|
||||||
|
@ -56,11 +56,11 @@ _VEHICLES_DICT_ALT = [
|
||||||
{'vh:cars': [
|
{'vh:cars': [
|
||||||
{'vh:car': None, '@make': 'Porsche', '@model': '911'},
|
{'vh:car': None, '@make': 'Porsche', '@model': '911'},
|
||||||
{'vh:car': None, '@make': 'Porsche', '@model': '911'}
|
{'vh:car': None, '@make': 'Porsche', '@model': '911'}
|
||||||
]},
|
]},
|
||||||
{'vh:bikes': [
|
{'vh:bikes': [
|
||||||
{'vh:bike': None, '@make': 'Harley-Davidson', '@model': 'WL'},
|
{'vh:bike': None, '@make': 'Harley-Davidson', '@model': 'WL'},
|
||||||
{'vh:bike': None, '@make': 'Yamaha', '@model': 'XS650'}
|
{'vh:bike': None, '@make': 'Yamaha', '@model': 'XS650'}
|
||||||
]},
|
]},
|
||||||
{'@xsi:schemaLocation': 'http://example.com/vehicles vehicles.xsd'}
|
{'@xsi:schemaLocation': 'http://example.com/vehicles vehicles.xsd'}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ _COLLECTION_DICT = {
|
||||||
'position': 2,
|
'position': 2,
|
||||||
'title': None,
|
'title': None,
|
||||||
'year': '1925'
|
'year': '1925'
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
_COLLECTION_PARKER = {
|
_COLLECTION_PARKER = {
|
||||||
|
@ -165,7 +165,7 @@ _COLLECTION_BADGERFISH = {
|
||||||
'position': {'$': 2},
|
'position': {'$': 2},
|
||||||
'title': {},
|
'title': {},
|
||||||
'year': {'$': '1925'}
|
'year': {'$': '1925'}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +624,7 @@ class TestValidation11(TestValidation):
|
||||||
" <node node-id='2' colour='red'>beta</node>"
|
" <node node-id='2' colour='red'>beta</node>"
|
||||||
"</tree>"))
|
"</tree>"))
|
||||||
self.assertFalse(xs.is_valid("<tree xmlns='ns'>"
|
self.assertFalse(xs.is_valid("<tree xmlns='ns'>"
|
||||||
" <node>alpha</node>" # Misses required attribute
|
" <node>alpha</node>" # Misses required attribute
|
||||||
" <node node-id='2' colour='red'>beta</node>"
|
" <node node-id='2' colour='red'>beta</node>"
|
||||||
"</tree>"))
|
"</tree>"))
|
||||||
|
|
||||||
|
@ -912,6 +912,47 @@ class TestDecoding(XMLSchemaTestCase):
|
||||||
self.check_decode(decimal_or_nan, '95.0', Decimal('95.0'))
|
self.check_decode(decimal_or_nan, '95.0', Decimal('95.0'))
|
||||||
self.check_decode(decimal_or_nan, 'NaN', u'NaN')
|
self.check_decode(decimal_or_nan, 'NaN', u'NaN')
|
||||||
|
|
||||||
|
def test_default_values(self):
|
||||||
|
# From issue #108
|
||||||
|
xsd_text = """<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="root" type="root" default="default_value"/>
|
||||||
|
<xs:complexType name="root">
|
||||||
|
<xs:simpleContent>
|
||||||
|
<xs:extension base="xs:string">
|
||||||
|
<xs:attribute name="attr" type="xs:string"/>
|
||||||
|
<xs:attribute name="attrWithDefault" type="xs:string" default="default_value"/>
|
||||||
|
<xs:attribute name="attrWithFixed" type="xs:string" fixed="fixed_value"/>
|
||||||
|
</xs:extension>
|
||||||
|
</xs:simpleContent>
|
||||||
|
</xs:complexType>
|
||||||
|
<xs:element name="simple_root" type="xs:string" default="default_value"/>
|
||||||
|
</xs:schema>"""
|
||||||
|
|
||||||
|
schema = self.schema_class(xsd_text)
|
||||||
|
self.assertEqual(schema.to_dict("<root>text</root>"),
|
||||||
|
{'@attrWithDefault': 'default_value',
|
||||||
|
'@attrWithFixed': 'fixed_value',
|
||||||
|
'$': 'text'})
|
||||||
|
self.assertEqual(schema.to_dict("<root/>"),
|
||||||
|
{'@attrWithDefault': 'default_value',
|
||||||
|
'@attrWithFixed': 'fixed_value',
|
||||||
|
'$': 'default_value'})
|
||||||
|
self.assertEqual(schema.to_dict("""<root attr="attr_value">text</root>"""),
|
||||||
|
{'$': 'text',
|
||||||
|
'@attr': 'attr_value',
|
||||||
|
'@attrWithDefault': 'default_value',
|
||||||
|
'@attrWithFixed': 'fixed_value'})
|
||||||
|
|
||||||
|
self.assertEqual(schema.to_dict("<root>text</root>", use_defaults=False),
|
||||||
|
{'@attrWithFixed': 'fixed_value', '$': 'text'})
|
||||||
|
self.assertEqual(schema.to_dict("""<root attr="attr_value">text</root>""", use_defaults=False),
|
||||||
|
{'$': 'text', '@attr': 'attr_value', '@attrWithFixed': 'fixed_value'})
|
||||||
|
self.assertEqual(schema.to_dict("<root/>", use_defaults=False), {'@attrWithFixed': 'fixed_value'})
|
||||||
|
|
||||||
|
self.assertEqual(schema.to_dict("<simple_root/>"), 'default_value')
|
||||||
|
self.assertIsNone(schema.to_dict("<simple_root/>", use_defaults=False))
|
||||||
|
|
||||||
|
|
||||||
class TestDecoding11(TestDecoding):
|
class TestDecoding11(TestDecoding):
|
||||||
schema_class = XMLSchema11
|
schema_class = XMLSchema11
|
||||||
|
@ -1112,7 +1153,7 @@ class TestEncoding(XMLSchemaTestCase):
|
||||||
<complexType name="A_type" mixed="true">
|
<complexType name="A_type" mixed="true">
|
||||||
<simpleContent>
|
<simpleContent>
|
||||||
<extension base="string">
|
<extension base="string">
|
||||||
<attribute name="a1" type="short" use="required"/>
|
<attribute name="a1" type="short" use="required"/>
|
||||||
<attribute name="a2" type="negativeInteger"/>
|
<attribute name="a2" type="negativeInteger"/>
|
||||||
</extension>
|
</extension>
|
||||||
</simpleContent>
|
</simpleContent>
|
||||||
|
@ -1142,7 +1183,7 @@ class TestEncoding(XMLSchemaTestCase):
|
||||||
<sequence>
|
<sequence>
|
||||||
<element name="B1" type="string"/>
|
<element name="B1" type="string"/>
|
||||||
<element name="B2" type="integer"/>
|
<element name="B2" type="integer"/>
|
||||||
<element name="B3" type="boolean"/>
|
<element name="B3" type="boolean"/>
|
||||||
</sequence>
|
</sequence>
|
||||||
</complexType>
|
</complexType>
|
||||||
""")
|
""")
|
||||||
|
|
|
@ -230,7 +230,7 @@ class XsdAttribute(XsdComponent, ValidationMixin):
|
||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
def iter_decode(self, text, validation='lax', **kwargs):
|
def iter_decode(self, text, validation='lax', **kwargs):
|
||||||
if not text and kwargs.get('use_defaults', True) and self.default is not None:
|
if not text and self.default is not None:
|
||||||
text = self.default
|
text = self.default
|
||||||
if self.fixed is not None and text != self.fixed and validation != 'skip':
|
if self.fixed is not None and text != self.fixed and validation != 'skip':
|
||||||
yield self.validation_error(validation, "value differs from fixed value", text, **kwargs)
|
yield self.validation_error(validation, "value differs from fixed value", text, **kwargs)
|
||||||
|
@ -297,7 +297,7 @@ class Xsd11Attribute(XsdAttribute):
|
||||||
class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
"""
|
"""
|
||||||
Class for XSD 'attributeGroup' definitions.
|
Class for XSD 'attributeGroup' definitions.
|
||||||
|
|
||||||
<attributeGroup
|
<attributeGroup
|
||||||
id = ID
|
id = ID
|
||||||
name = NCName
|
name = NCName
|
||||||
|
@ -548,6 +548,20 @@ class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
if k is not None and v.use == 'required':
|
if k is not None and v.use == 'required':
|
||||||
yield k
|
yield k
|
||||||
|
|
||||||
|
def iter_predefined(self, use_defaults=True):
|
||||||
|
if use_defaults:
|
||||||
|
for k, v in self._attribute_group.items():
|
||||||
|
if k is None:
|
||||||
|
continue
|
||||||
|
elif v.fixed is not None:
|
||||||
|
yield k, v.fixed
|
||||||
|
elif v.default is not None:
|
||||||
|
yield k, v.default
|
||||||
|
else:
|
||||||
|
for k, v in self._attribute_group.items():
|
||||||
|
if k is not None and v.fixed is not None:
|
||||||
|
yield k, v.fixed
|
||||||
|
|
||||||
def iter_components(self, xsd_classes=None):
|
def iter_components(self, xsd_classes=None):
|
||||||
if xsd_classes is None or isinstance(self, xsd_classes):
|
if xsd_classes is None or isinstance(self, xsd_classes):
|
||||||
yield self
|
yield self
|
||||||
|
@ -561,8 +575,18 @@ class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
if not attrs and not self:
|
if not attrs and not self:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if validation != 'skip' and any(k not in attrs for k in self.iter_required()):
|
||||||
|
missing_attrs = {k for k in self.iter_required() if k not in attrs}
|
||||||
|
reason = "missing required attributes: %r" % missing_attrs
|
||||||
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
||||||
|
|
||||||
|
use_defaults = kwargs.get('use_defaults', True)
|
||||||
|
additional_attrs = {k: v for k, v in self.iter_predefined(use_defaults) if k not in attrs}
|
||||||
|
if additional_attrs:
|
||||||
|
attrs = {k: v for k, v in attrs.items()}
|
||||||
|
attrs.update(additional_attrs)
|
||||||
|
|
||||||
result_list = []
|
result_list = []
|
||||||
required_attributes = {a for a in self.iter_required()}
|
|
||||||
for name, value in attrs.items():
|
for name, value in attrs.items():
|
||||||
try:
|
try:
|
||||||
xsd_attribute = self[name]
|
xsd_attribute = self[name]
|
||||||
|
@ -584,8 +608,6 @@ class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
reason = "%r attribute not allowed for element." % name
|
reason = "%r attribute not allowed for element." % name
|
||||||
yield self.validation_error(validation, reason, attrs, **kwargs)
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
required_attributes.discard(name)
|
|
||||||
|
|
||||||
for result in xsd_attribute.iter_decode(value, validation, **kwargs):
|
for result in xsd_attribute.iter_decode(value, validation, **kwargs):
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
if isinstance(result, XMLSchemaValidationError):
|
||||||
|
@ -594,21 +616,22 @@ class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
result_list.append((name, result))
|
result_list.append((name, result))
|
||||||
break
|
break
|
||||||
|
|
||||||
if required_attributes and validation != 'skip':
|
|
||||||
reason = "missing required attributes: %r" % required_attributes
|
|
||||||
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
||||||
|
|
||||||
yield result_list
|
yield result_list
|
||||||
|
|
||||||
def iter_encode(self, attrs, validation='lax', **kwargs):
|
def iter_encode(self, attrs, validation='lax', **kwargs):
|
||||||
result_list = []
|
if validation != 'skip' and any(k not in attrs for k in self.iter_required()):
|
||||||
required_attributes = {a for a in self.iter_required()}
|
missing_attrs = {k for k in self.iter_required() if k not in attrs}
|
||||||
try:
|
reason = "missing required attributes: %r" % missing_attrs
|
||||||
attrs = attrs.items()
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for name, value in attrs:
|
use_defaults = kwargs.get('use_defaults', True)
|
||||||
|
additional_attrs = {k: v for k, v in self.iter_predefined(use_defaults) if k not in attrs}
|
||||||
|
if additional_attrs:
|
||||||
|
attrs = {k: v for k, v in attrs.items()}
|
||||||
|
attrs.update(additional_attrs)
|
||||||
|
|
||||||
|
result_list = []
|
||||||
|
for name, value in attrs.items():
|
||||||
try:
|
try:
|
||||||
xsd_attribute = self[name]
|
xsd_attribute = self[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -630,17 +653,13 @@ class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
||||||
reason = "%r attribute not allowed for element." % name
|
reason = "%r attribute not allowed for element." % name
|
||||||
yield self.validation_error(validation, reason, attrs, **kwargs)
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
required_attributes.discard(name)
|
|
||||||
|
|
||||||
for result in xsd_attribute.iter_encode(value, validation, **kwargs):
|
for result in xsd_attribute.iter_encode(value, validation, **kwargs):
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
if isinstance(result, XMLSchemaValidationError):
|
||||||
yield result
|
yield result
|
||||||
else:
|
else:
|
||||||
result_list.append((name, result))
|
if result is not None:
|
||||||
|
result_list.append((name, result))
|
||||||
break
|
break
|
||||||
|
|
||||||
if required_attributes and validation != 'skip':
|
|
||||||
reason = "missing required attributes %r" % required_attributes
|
|
||||||
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
||||||
yield result_list
|
yield result_list
|
||||||
|
|
|
@ -33,7 +33,7 @@ SEQUENCE_ELEMENT = etree_element(XSD_SEQUENCE)
|
||||||
class XsdComplexType(XsdType, ValidationMixin):
|
class XsdComplexType(XsdType, ValidationMixin):
|
||||||
"""
|
"""
|
||||||
Class for XSD 1.0 'complexType' definitions.
|
Class for XSD 1.0 'complexType' definitions.
|
||||||
|
|
||||||
<complexType
|
<complexType
|
||||||
abstract = boolean : false
|
abstract = boolean : false
|
||||||
block = (#all | List of (extension | restriction))
|
block = (#all | List of (extension | restriction))
|
||||||
|
@ -42,7 +42,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
mixed = boolean : false
|
mixed = boolean : false
|
||||||
name = NCName
|
name = NCName
|
||||||
{any attributes with non-schema namespace . . .}>
|
{any attributes with non-schema namespace . . .}>
|
||||||
Content: (annotation?, (simpleContent | complexContent |
|
Content: (annotation?, (simpleContent | complexContent |
|
||||||
((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
|
((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
|
||||||
</complexType>
|
</complexType>
|
||||||
"""
|
"""
|
||||||
|
@ -627,7 +627,7 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
name = NCName
|
name = NCName
|
||||||
defaultAttributesApply = boolean : true
|
defaultAttributesApply = boolean : true
|
||||||
{any attributes with non-schema namespace . . .}>
|
{any attributes with non-schema namespace . . .}>
|
||||||
Content: (annotation?, (simpleContent | complexContent | (openContent?,
|
Content: (annotation?, (simpleContent | complexContent | (openContent?,
|
||||||
(group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), assert*)))
|
(group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), assert*)))
|
||||||
</complexType>
|
</complexType>
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -40,7 +40,7 @@ XSD_ATTRIBUTE_GROUP_ELEMENT = etree_element(XSD_ATTRIBUTE_GROUP)
|
||||||
class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin):
|
class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin):
|
||||||
"""
|
"""
|
||||||
Class for XSD 1.0 'element' declarations.
|
Class for XSD 1.0 'element' declarations.
|
||||||
|
|
||||||
<element
|
<element
|
||||||
abstract = boolean : false
|
abstract = boolean : false
|
||||||
block = (#all | List of (extension | restriction | substitution))
|
block = (#all | List of (extension | restriction | substitution))
|
||||||
|
@ -486,9 +486,15 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
||||||
reason = "xsi:nil attribute must has a boolean value."
|
reason = "xsi:nil attribute must has a boolean value."
|
||||||
yield self.validation_error(validation, reason, elem, **kwargs)
|
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||||
|
|
||||||
if xsd_type.is_simple():
|
if not xsd_type.has_simple_content():
|
||||||
|
for result in xsd_type.content_type.iter_decode(elem, validation, converter, level=level + 1, **kwargs):
|
||||||
|
if isinstance(result, XMLSchemaValidationError):
|
||||||
|
yield self.validation_error(validation, result, elem, **kwargs)
|
||||||
|
else:
|
||||||
|
content = result
|
||||||
|
else:
|
||||||
if len(elem) and validation != 'skip':
|
if len(elem) and validation != 'skip':
|
||||||
reason = "a simpleType element can't has child elements."
|
reason = "a simple content element can't has child elements."
|
||||||
yield self.validation_error(validation, reason, elem, **kwargs)
|
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||||
|
|
||||||
text = elem.text
|
text = elem.text
|
||||||
|
@ -501,6 +507,9 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
||||||
elif not text and use_defaults and self.default is not None:
|
elif not text and use_defaults and self.default is not None:
|
||||||
text = self.default
|
text = self.default
|
||||||
|
|
||||||
|
if not xsd_type.is_simple():
|
||||||
|
xsd_type = xsd_type.content_type
|
||||||
|
|
||||||
if text is None:
|
if text is None:
|
||||||
for result in xsd_type.iter_decode('', validation, **kwargs):
|
for result in xsd_type.iter_decode('', validation, **kwargs):
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
if isinstance(result, XMLSchemaValidationError):
|
||||||
|
@ -512,25 +521,6 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
||||||
else:
|
else:
|
||||||
value = result
|
value = result
|
||||||
|
|
||||||
elif xsd_type.has_simple_content():
|
|
||||||
if len(elem) and validation != 'skip':
|
|
||||||
reason = "a simple content element can't has child elements."
|
|
||||||
yield self.validation_error(validation, reason, elem, **kwargs)
|
|
||||||
|
|
||||||
if elem.text is not None:
|
|
||||||
text = elem.text or self.default if use_defaults else elem.text
|
|
||||||
for result in xsd_type.content_type.iter_decode(text, validation, **kwargs):
|
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
|
||||||
yield self.validation_error(validation, result, elem, **kwargs)
|
|
||||||
else:
|
|
||||||
value = result
|
|
||||||
else:
|
|
||||||
for result in xsd_type.content_type.iter_decode(elem, validation, converter, level=level + 1, **kwargs):
|
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
|
||||||
yield self.validation_error(validation, result, elem, **kwargs)
|
|
||||||
else:
|
|
||||||
content = result
|
|
||||||
|
|
||||||
if isinstance(value, Decimal):
|
if isinstance(value, Decimal):
|
||||||
try:
|
try:
|
||||||
value = kwargs['decimal_type'](value)
|
value = kwargs['decimal_type'](value)
|
||||||
|
@ -629,7 +619,7 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
||||||
text = result
|
text = result
|
||||||
else:
|
else:
|
||||||
for result in xsd_type.content_type.iter_encode(
|
for result in xsd_type.content_type.iter_encode(
|
||||||
element_data, validation, converter, level=level+1, **kwargs):
|
element_data, validation, converter, level=level + 1, **kwargs):
|
||||||
if isinstance(result, XMLSchemaValidationError):
|
if isinstance(result, XMLSchemaValidationError):
|
||||||
errors.append(result)
|
errors.append(result)
|
||||||
elif result:
|
elif result:
|
||||||
|
|
|
@ -13,7 +13,7 @@ This module contains declarations and classes for XML Schema constraint facets.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import re
|
import re
|
||||||
from elementpath import XPath2Parser, ElementPathError, datatypes
|
from elementpath import XPath2Parser, ElementPathError, datatypes
|
||||||
|
|
||||||
from ..compat import unicode_type, MutableSequence
|
from ..compat import unicode_type, MutableSequence
|
||||||
from ..qnames import XSD_LENGTH, XSD_MIN_LENGTH, XSD_MAX_LENGTH, XSD_ENUMERATION, XSD_WHITE_SPACE, \
|
from ..qnames import XSD_LENGTH, XSD_MIN_LENGTH, XSD_MAX_LENGTH, XSD_ENUMERATION, XSD_WHITE_SPACE, \
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
# @author Davide Brunato <brunato@sissa.it>
|
# @author Davide Brunato <brunato@sissa.it>
|
||||||
#
|
#
|
||||||
"""
|
"""
|
||||||
This module contains functions and classes for managing namespaces's
|
This module contains functions and classes for namespaces XSD declarations/definitions.
|
||||||
XSD declarations/definitions.
|
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import re
|
import re
|
||||||
|
|
|
@ -503,7 +503,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
||||||
if depth <= MAX_MODEL_DEPTH:
|
if depth <= MAX_MODEL_DEPTH:
|
||||||
for item in self:
|
for item in self:
|
||||||
if isinstance(item, XsdGroup):
|
if isinstance(item, XsdGroup):
|
||||||
for e in item.iter_elements(depth+1):
|
for e in item.iter_elements(depth + 1):
|
||||||
yield e
|
yield e
|
||||||
else:
|
else:
|
||||||
yield item
|
yield item
|
||||||
|
|
|
@ -204,8 +204,8 @@ class XsdIdentity(XsdComponent):
|
||||||
values[v] += 1
|
values[v] += 1
|
||||||
|
|
||||||
for value, count in values.items():
|
for value, count in values.items():
|
||||||
if count > 1:
|
if value and count > 1:
|
||||||
yield XMLSchemaValidationError(self, elem, reason="duplicated value %r." % value)
|
yield XMLSchemaValidationError(self, elem, reason="duplicated value {!r}.".format(value))
|
||||||
|
|
||||||
|
|
||||||
class XsdUnique(XsdIdentity):
|
class XsdUnique(XsdIdentity):
|
||||||
|
@ -300,6 +300,6 @@ class XsdKeyref(XsdIdentity):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if v not in refer_values:
|
if v not in refer_values:
|
||||||
reason = "Key %r with value %r not found for identity constraint of element %r." \
|
reason = "Key {!r} with value {!r} not found for identity constraint of element {!r}." \
|
||||||
% (self.prefixed_name, v, qname_to_prefixed(elem.tag, self.namespaces))
|
.format(self.prefixed_name, v, qname_to_prefixed(elem.tag, self.namespaces))
|
||||||
yield XMLSchemaValidationError(validator=self, obj=elem, reason=reason)
|
yield XMLSchemaValidationError(validator=self, obj=elem, reason=reason)
|
||||||
|
|
|
@ -155,7 +155,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
|
||||||
elif not item.is_pointless(parent=self):
|
elif not item.is_pointless(parent=self):
|
||||||
yield item
|
yield item
|
||||||
else:
|
else:
|
||||||
for obj in item.iter_model(depth+1):
|
for obj in item.iter_model(depth + 1):
|
||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
def iter_elements(self, depth=0):
|
def iter_elements(self, depth=0):
|
||||||
|
@ -169,7 +169,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
|
||||||
raise XMLSchemaModelDepthError(self)
|
raise XMLSchemaModelDepthError(self)
|
||||||
for item in self:
|
for item in self:
|
||||||
if isinstance(item, ModelGroup):
|
if isinstance(item, ModelGroup):
|
||||||
for e in item.iter_elements(depth+1):
|
for e in item.iter_elements(depth + 1):
|
||||||
yield e
|
yield e
|
||||||
else:
|
else:
|
||||||
yield item
|
yield item
|
||||||
|
@ -178,7 +178,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
|
||||||
if depth <= MAX_MODEL_DEPTH:
|
if depth <= MAX_MODEL_DEPTH:
|
||||||
for item in self:
|
for item in self:
|
||||||
if isinstance(item, ModelGroup):
|
if isinstance(item, ModelGroup):
|
||||||
for e in item.iter_subelements(depth+1):
|
for e in item.iter_subelements(depth + 1):
|
||||||
yield e
|
yield e
|
||||||
else:
|
else:
|
||||||
yield item
|
yield item
|
||||||
|
@ -269,8 +269,8 @@ def distinguishable_paths(path1, path2):
|
||||||
|
|
||||||
if path1[depth].model != 'sequence':
|
if path1[depth].model != 'sequence':
|
||||||
return before1 and before2 or \
|
return before1 and before2 or \
|
||||||
(before1 and (univocal1 and e1.is_univocal() or after1 or path1[depth].max_occurs == 1)) or \
|
(before1 and (univocal1 and e1.is_univocal() or after1 or path1[depth].max_occurs == 1)) or \
|
||||||
(before2 and (univocal2 and e2.is_univocal() or after2 or path2[depth].max_occurs == 1))
|
(before2 and (univocal2 and e2.is_univocal() or after2 or path2[depth].max_occurs == 1))
|
||||||
elif path1[depth].max_occurs == 1:
|
elif path1[depth].max_occurs == 1:
|
||||||
return before2 or (before1 or univocal1) and (e1.is_univocal() or after1)
|
return before2 or (before1 or univocal1) and (e1.is_univocal() or after1)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -374,8 +374,8 @@ class XsdSimpleType(XsdType, ValidationMixin):
|
||||||
# simpleType's derived classes:
|
# simpleType's derived classes:
|
||||||
class XsdAtomic(XsdSimpleType):
|
class XsdAtomic(XsdSimpleType):
|
||||||
"""
|
"""
|
||||||
Class for atomic simpleType definitions. An atomic definition has
|
Class for atomic simpleType definitions. An atomic definition has
|
||||||
a base_type attribute that refers to primitive or derived atomic
|
a base_type attribute that refers to primitive or derived atomic
|
||||||
built-in type or another derived simpleType.
|
built-in type or another derived simpleType.
|
||||||
"""
|
"""
|
||||||
_special_types = {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE, XSD_ANY_ATOMIC_TYPE}
|
_special_types = {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE, XSD_ANY_ATOMIC_TYPE}
|
||||||
|
@ -599,9 +599,9 @@ class XsdAtomicBuiltin(XsdAtomic):
|
||||||
|
|
||||||
class XsdList(XsdSimpleType):
|
class XsdList(XsdSimpleType):
|
||||||
"""
|
"""
|
||||||
Class for 'list' definitions. A list definition has an item_type attribute
|
Class for 'list' definitions. A list definition has an item_type attribute
|
||||||
that refers to an atomic or union simpleType definition.
|
that refers to an atomic or union simpleType definition.
|
||||||
|
|
||||||
<list
|
<list
|
||||||
id = ID
|
id = ID
|
||||||
itemType = QName
|
itemType = QName
|
||||||
|
@ -776,7 +776,7 @@ class XsdList(XsdSimpleType):
|
||||||
|
|
||||||
class XsdUnion(XsdSimpleType):
|
class XsdUnion(XsdSimpleType):
|
||||||
"""
|
"""
|
||||||
Class for 'union' definitions. A union definition has a member_types
|
Class for 'union' definitions. A union definition has a member_types
|
||||||
attribute that refers to a 'simpleType' definition.
|
attribute that refers to a 'simpleType' definition.
|
||||||
|
|
||||||
<union
|
<union
|
||||||
|
@ -1021,8 +1021,8 @@ class XsdAtomicRestriction(XsdAtomic):
|
||||||
base = QName
|
base = QName
|
||||||
id = ID
|
id = ID
|
||||||
{any attributes with non-schema namespace . . .}>
|
{any attributes with non-schema namespace . . .}>
|
||||||
Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
|
Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
|
||||||
maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
|
maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
|
||||||
enumeration | whiteSpace | pattern)*))
|
enumeration | whiteSpace | pattern)*))
|
||||||
</restriction>
|
</restriction>
|
||||||
"""
|
"""
|
||||||
|
@ -1269,9 +1269,9 @@ class Xsd11AtomicRestriction(XsdAtomicRestriction):
|
||||||
base = QName
|
base = QName
|
||||||
id = ID
|
id = ID
|
||||||
{any attributes with non-schema namespace . . .}>
|
{any attributes with non-schema namespace . . .}>
|
||||||
Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
|
Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
|
||||||
maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
|
maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
|
||||||
enumeration | whiteSpace | pattern | assertion | explicitTimezone |
|
enumeration | whiteSpace | pattern | assertion | explicitTimezone |
|
||||||
{any with namespace: ##other})*))
|
{any with namespace: ##other})*))
|
||||||
</restriction>
|
</restriction>
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -25,6 +25,9 @@ from .xsdbase import ValidationMixin, XsdComponent, ParticleMixin
|
||||||
|
|
||||||
class XsdWildcard(XsdComponent, ValidationMixin):
|
class XsdWildcard(XsdComponent, ValidationMixin):
|
||||||
names = {}
|
names = {}
|
||||||
|
namespace = '##any'
|
||||||
|
not_namespace = ()
|
||||||
|
not_qname = ()
|
||||||
|
|
||||||
def __init__(self, elem, schema, parent):
|
def __init__(self, elem, schema, parent):
|
||||||
if parent is None:
|
if parent is None:
|
||||||
|
@ -40,15 +43,15 @@ class XsdWildcard(XsdComponent, ValidationMixin):
|
||||||
super(XsdWildcard, self)._parse()
|
super(XsdWildcard, self)._parse()
|
||||||
|
|
||||||
# Parse namespace and processContents
|
# Parse namespace and processContents
|
||||||
namespace = self.elem.get('namespace', '##any')
|
namespace = self.elem.get('namespace', '##any').strip()
|
||||||
items = namespace.strip().split()
|
if namespace == '##any':
|
||||||
if len(items) == 1 and items[0] in ('##any', '##other', '##local', '##targetNamespace'):
|
pass
|
||||||
self.namespace = namespace.strip()
|
elif namespace in {'##other', '##local', '##targetNamespace'}:
|
||||||
elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in items):
|
self.namespace = namespace
|
||||||
|
elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in namespace.split()):
|
||||||
self.parse_error("wrong value %r for 'namespace' attribute." % namespace)
|
self.parse_error("wrong value %r for 'namespace' attribute." % namespace)
|
||||||
self.namespace = '##any'
|
|
||||||
else:
|
else:
|
||||||
self.namespace = namespace.strip()
|
self.namespace = namespace
|
||||||
|
|
||||||
self.process_contents = self.elem.get('processContents', 'strict')
|
self.process_contents = self.elem.get('processContents', 'strict')
|
||||||
if self.process_contents not in {'lax', 'skip', 'strict'}:
|
if self.process_contents not in {'lax', 'skip', 'strict'}:
|
||||||
|
@ -99,7 +102,15 @@ class XsdWildcard(XsdComponent, ValidationMixin):
|
||||||
return self.is_namespace_allowed(default_namespace)
|
return self.is_namespace_allowed(default_namespace)
|
||||||
|
|
||||||
def is_namespace_allowed(self, namespace):
|
def is_namespace_allowed(self, namespace):
|
||||||
if self.namespace == '##any' or namespace == XSI_NAMESPACE:
|
if self.not_namespace:
|
||||||
|
if '##local' in self.not_namespace and namespace == '':
|
||||||
|
return False
|
||||||
|
elif '##targetNamespace' in self.not_namespace and namespace == self.target_namespace:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return namespace not in self.not_namespace
|
||||||
|
|
||||||
|
elif self.namespace == '##any' or namespace == XSI_NAMESPACE:
|
||||||
return True
|
return True
|
||||||
elif self.namespace == '##other':
|
elif self.namespace == '##other':
|
||||||
if namespace:
|
if namespace:
|
||||||
|
@ -261,7 +272,7 @@ class XsdAnyElement(XsdWildcard, ParticleMixin, ElementPathMixin):
|
||||||
class XsdAnyAttribute(XsdWildcard):
|
class XsdAnyAttribute(XsdWildcard):
|
||||||
"""
|
"""
|
||||||
Class for XSD 1.0 'anyAttribute' wildcards.
|
Class for XSD 1.0 'anyAttribute' wildcards.
|
||||||
|
|
||||||
<anyAttribute
|
<anyAttribute
|
||||||
id = ID
|
id = ID
|
||||||
namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) )
|
namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) )
|
||||||
|
@ -351,54 +362,6 @@ class XsdAnyAttribute(XsdWildcard):
|
||||||
yield self.validation_error(validation, reason, attribute, **kwargs)
|
yield self.validation_error(validation, reason, attribute, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Xsd11Wildcard(XsdWildcard):
|
|
||||||
|
|
||||||
def _parse(self):
|
|
||||||
super(Xsd11Wildcard, self)._parse()
|
|
||||||
|
|
||||||
# Parse notNamespace attribute
|
|
||||||
try:
|
|
||||||
not_namespace = self.elem.attrib['notNamespace'].strip()
|
|
||||||
except KeyError:
|
|
||||||
self.not_namespace = None
|
|
||||||
else:
|
|
||||||
if 'namespace' in self.elem.attrib:
|
|
||||||
self.not_namespace = None
|
|
||||||
self.parse_error("'namespace' and 'notNamespace' attributes are mutually exclusive.")
|
|
||||||
elif not_namespace in ('##local', '##targetNamespace'):
|
|
||||||
self.not_namespace = not_namespace
|
|
||||||
else:
|
|
||||||
self.not_namespace = not_namespace.split()
|
|
||||||
|
|
||||||
# Parse notQName attribute
|
|
||||||
try:
|
|
||||||
not_qname = self.elem.attrib['notQName'].strip()
|
|
||||||
except KeyError:
|
|
||||||
self.not_qname = None
|
|
||||||
else:
|
|
||||||
if not_qname in ('##defined', '##definedSibling'):
|
|
||||||
self.not_qname = not_qname
|
|
||||||
else:
|
|
||||||
self.not_qname = not_qname.split()
|
|
||||||
|
|
||||||
def is_namespace_allowed(self, namespace):
|
|
||||||
if self.namespace == '##any' or namespace == XSI_NAMESPACE:
|
|
||||||
return True
|
|
||||||
elif self.namespace == '##other':
|
|
||||||
if namespace:
|
|
||||||
return namespace != self.target_namespace
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
any_namespaces = self.namespace.split()
|
|
||||||
if '##local' in any_namespaces and namespace == '':
|
|
||||||
return True
|
|
||||||
elif '##targetNamespace' in any_namespaces and namespace == self.target_namespace:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return namespace in any_namespaces
|
|
||||||
|
|
||||||
|
|
||||||
class Xsd11AnyElement(XsdAnyElement):
|
class Xsd11AnyElement(XsdAnyElement):
|
||||||
"""
|
"""
|
||||||
Class for XSD 1.1 'any' declarations.
|
Class for XSD 1.1 'any' declarations.
|
||||||
|
@ -415,7 +378,32 @@ class Xsd11AnyElement(XsdAnyElement):
|
||||||
Content: (annotation?)
|
Content: (annotation?)
|
||||||
</any>
|
</any>
|
||||||
"""
|
"""
|
||||||
pass
|
def _parse(self):
|
||||||
|
super(Xsd11AnyElement, self)._parse()
|
||||||
|
|
||||||
|
# Parse notNamespace attribute
|
||||||
|
try:
|
||||||
|
not_namespace = self.elem.attrib['notNamespace'].strip().split()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if 'namespace' in self.elem.attrib:
|
||||||
|
self.parse_error("'namespace' and 'notNamespace' attributes are mutually exclusive.")
|
||||||
|
elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in not_namespace):
|
||||||
|
self.parse_error("wrong value %r for 'notNamespace' attribute." % self.elem.attrib['notNamespace'])
|
||||||
|
else:
|
||||||
|
self.not_namespace = not_namespace
|
||||||
|
|
||||||
|
# Parse notQName attribute
|
||||||
|
try:
|
||||||
|
not_qname = self.elem.attrib['notQName'].strip().split()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if not all(not s.startswith('##') or s in {'##defined', '##definedSibling'} for s in not_qname):
|
||||||
|
self.parse_error("wrong value %r for 'notQName' attribute." % self.elem.attrib['notQName'])
|
||||||
|
else:
|
||||||
|
self.not_qname = not_qname
|
||||||
|
|
||||||
|
|
||||||
class Xsd11AnyAttribute(XsdAnyAttribute):
|
class Xsd11AnyAttribute(XsdAnyAttribute):
|
||||||
|
@ -432,7 +420,32 @@ class Xsd11AnyAttribute(XsdAnyAttribute):
|
||||||
Content: (annotation?)
|
Content: (annotation?)
|
||||||
</anyAttribute>
|
</anyAttribute>
|
||||||
"""
|
"""
|
||||||
pass
|
def _parse(self):
|
||||||
|
super(Xsd11AnyAttribute, self)._parse()
|
||||||
|
|
||||||
|
# Parse notNamespace attribute
|
||||||
|
try:
|
||||||
|
not_namespace = self.elem.attrib['notNamespace'].strip().split()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if 'namespace' in self.elem.attrib:
|
||||||
|
self.parse_error("'namespace' and 'notNamespace' attributes are mutually exclusive.")
|
||||||
|
elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in not_namespace):
|
||||||
|
self.parse_error("wrong value %r for 'notNamespace' attribute." % self.elem.attrib['notNamespace'])
|
||||||
|
else:
|
||||||
|
self.not_namespace = not_namespace
|
||||||
|
|
||||||
|
# Parse notQName attribute
|
||||||
|
try:
|
||||||
|
not_qname = self.elem.attrib['notQName'].strip().split()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if not all(not s.startswith('##') or s == '##defined' for s in not_qname):
|
||||||
|
self.parse_error("wrong value %r for 'notQName' attribute." % self.elem.attrib['notQName'])
|
||||||
|
else:
|
||||||
|
self.not_qname = not_qname
|
||||||
|
|
||||||
|
|
||||||
class XsdOpenContent(XsdComponent):
|
class XsdOpenContent(XsdComponent):
|
||||||
|
@ -467,6 +480,10 @@ class XsdOpenContent(XsdComponent):
|
||||||
if child is not None and child.tag == XSD_ANY:
|
if child is not None and child.tag == XSD_ANY:
|
||||||
self.any_element = Xsd11AnyElement(child, self.schema, self)
|
self.any_element = Xsd11AnyElement(child, self.schema, self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def built(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class XsdDefaultOpenContent(XsdOpenContent):
|
class XsdDefaultOpenContent(XsdOpenContent):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue