diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..f078c55
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,4 @@
+[run]
+branch = True
+source = xmlschema/
+omit = xmlschema/tests/*
diff --git a/.gitignore b/.gitignore
index 6201f9e..3c1ce44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
*.json
.idea/
.tox/
+.coverage
.ipynb_checkpoints/
doc/_*/
dist/
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 2bc9058..926cb6b 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,7 @@
# Requirements for setup a development environment for the xmlschema package.
setuptools
tox
+coverage
elementpath~=1.1.7
lxml
memory_profiler
diff --git a/tox.ini b/tox.ini
index c271fe3..75e7a79 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,14 +4,20 @@
# and then run "tox" from this directory.
[tox]
-envlist = py27, py34, py35, py36, py37
+envlist = py27, py35, py36, py37, py38, docs, flake8, coverage
+skip_missing_interpreters = true
toxworkdir = {homedir}/.tox/xmlschema
[testenv]
deps =
lxml
elementpath~=1.1.7
+ docs: Sphinx
+ docs: sphinx_rtd_theme
+ flake8: flake8
+ coverage: coverage
commands = python xmlschema/tests/test_all.py {posargs}
+whitelist_externals = make
[testenv:py27]
deps =
@@ -19,3 +25,35 @@ deps =
elementpath~=1.1.7
pathlib2
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
diff --git a/xmlschema/__init__.py b/xmlschema/__init__.py
index 999b25c..e9e8363 100644
--- a/xmlschema/__init__.py
+++ b/xmlschema/__init__.py
@@ -37,7 +37,7 @@ __status__ = "Production/Stable"
# API deprecation warnings
def XMLSchema_v1_0(*args, **kwargs):
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)
return XMLSchema10(*args, **kwargs)
diff --git a/xmlschema/codepoints.py b/xmlschema/codepoints.py
index 3f803d7..84dc04e 100644
--- a/xmlschema/codepoints.py
+++ b/xmlschema/codepoints.py
@@ -165,18 +165,18 @@ def iterparse_character_group(s, expand_ranges=False):
k = next(string_iter)
end_char = s[k]
if end_char == '\\' and (k < length - 1):
- if s[k+1] in r'-|.^?*+{}()[]':
+ if s[k + 1] in r'-|.^?*+{}()[]':
k = next(string_iter)
end_char = s[k]
- elif s[k+1] in r'sSdDiIcCwWpP':
- msg = "bad character range '%s-\\%s' at position %d: %r" % (char, s[k+1], k-2, s)
+ elif s[k + 1] in r'sSdDiIcCwWpP':
+ msg = "bad character range '%s-\\%s' at position %d: %r" % (char, s[k + 1], k - 2, s)
raise XMLSchemaRegexError(msg)
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)
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)
elif expand_ranges:
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))
escaped = on_range = False
char = s[k]
- if k >= length-1 or s[k+1] != '-':
+ if k >= length - 1 or s[k + 1] != '-':
yield ord(char)
elif s[k] == '\\':
if escaped:
@@ -209,7 +209,7 @@ def iterparse_character_group(s, expand_ranges=False):
yield ord('\\')
on_range = False
char = s[k]
- if k >= length-1 or s[k+1] != '-':
+ if k >= length - 1 or s[k + 1] != '-':
yield ord(char)
if escaped:
yield ord('\\')
diff --git a/xmlschema/converters.py b/xmlschema/converters.py
index 6e1ce14..1a4f404 100644
--- a/xmlschema/converters.py
+++ b/xmlschema/converters.py
@@ -145,7 +145,7 @@ class XMLSchemaConverter(NamespaceMapper):
"""
if not content:
return
-
+
map_qname = self.map_qname
for name, value, xsd_child in content:
try:
@@ -274,7 +274,7 @@ class XMLSchemaConverter(NamespaceMapper):
elif name == ns_prefix:
self[''] = value
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):
name = name[len(attr_prefix):]
attributes[unmap_attribute_qname(name)] = value
@@ -314,6 +314,7 @@ class ParkerConverter(XMLSchemaConverter):
XML Schema based converter class for 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 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:
return ElementData(xsd_element.name, None, [], attributes)
- elif data_len == content_index + 1 and (xsd_element.type.is_simple()
- or xsd_element.type.has_simple_content()):
+ elif data_len == content_index + 1 and \
+ (xsd_element.type.is_simple() or xsd_element.type.has_simple_content()):
return ElementData(xsd_element.name, obj[content_index], [], attributes)
else:
cdata_num = iter(range(1, data_len))
diff --git a/xmlschema/exceptions.py b/xmlschema/exceptions.py
index f3d78ae..964bca9 100644
--- a/xmlschema/exceptions.py
+++ b/xmlschema/exceptions.py
@@ -56,4 +56,3 @@ class XMLSchemaRegexError(XMLSchemaException, ValueError):
class XMLSchemaWarning(Warning):
"""Base warning class for the XMLSchema package."""
-
diff --git a/xmlschema/regex.py b/xmlschema/regex.py
index e28b806..b7d59ed 100644
--- a/xmlschema/regex.py
+++ b/xmlschema/regex.py
@@ -51,8 +51,8 @@ I_SHORTCUT_SET = UnicodeSubset(I_SHORTCUT_REPLACE)
C_SHORTCUT_SET = UnicodeSubset(C_SHORTCUT_REPLACE)
W_SHORTCUT_SET = UnicodeSubset()
W_SHORTCUT_SET._code_points = sorted(
- UNICODE_CATEGORIES['P'].code_points + UNICODE_CATEGORIES['Z'].code_points +
- UNICODE_CATEGORIES['C'].code_points, key=lambda x: x if isinstance(x, int) else x[0]
+ UNICODE_CATEGORIES['P'].code_points + UNICODE_CATEGORIES['Z'].code_points + UNICODE_CATEGORIES['C'].code_points,
+ key=lambda x: x if isinstance(x, int) else x[0]
)
# Single and Multi character escapes
@@ -312,9 +312,9 @@ def get_python_regex(xml_regex):
elif ch in ('?', '+', '*'):
if pos == 0:
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(
- "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)
elif ch == '\\':
diff --git a/xmlschema/tests/__init__.py b/xmlschema/tests/__init__.py
index de59196..158459b 100644
--- a/xmlschema/tests/__init__.py
+++ b/xmlschema/tests/__init__.py
@@ -64,7 +64,7 @@ class XMLSchemaTestCase(unittest.TestCase):
etree_register_namespace(prefix='', uri=XSD_NAMESPACE)
etree_register_namespace(prefix='ns', uri="ns")
SCHEMA_TEMPLATE = """
-
{1}
"""
@@ -131,7 +131,7 @@ class XMLSchemaTestCase(unittest.TestCase):
root = etree_element('schema', attrib={
'xmlns:ns': "ns",
'xmlns': "http://www.w3.org/2001/XMLSchema",
- 'targetNamespace': "ns",
+ 'targetNamespace': "ns",
'elementFormDefault': "qualified",
'version': self.schema_class.XSD_VERSION,
})
diff --git a/xmlschema/tests/test_cases/issues/issue_026/issue_026.xsd b/xmlschema/tests/test_cases/issues/issue_026/issue_026.xsd
index a9eef8e..7a85be4 100644
--- a/xmlschema/tests/test_cases/issues/issue_026/issue_026.xsd
+++ b/xmlschema/tests/test_cases/issues/issue_026/issue_026.xsd
@@ -15,6 +15,6 @@
-
+
\ No newline at end of file
diff --git a/xmlschema/tests/test_models.py b/xmlschema/tests/test_models.py
index 8aded08..901ea3a 100644
--- a/xmlschema/tests/test_models.py
+++ b/xmlschema/tests/test_models.py
@@ -270,7 +270,7 @@ class TestModelValidation(XMLSchemaTestCase):
self.assertEqual(model.element, group[2])
self.check_advance_false(model) # don't match
self.assertEqual(model.element, group[3])
- self.check_advance_false(model, [(group, 0, group[0][0][:]+group[1:])]) # don't match
+ self.check_advance_false(model, [(group, 0, group[0][0][:] + group[1:])]) # don't match
model.restart()
self.assertEqual(model.element, group[0][0][0])
diff --git a/xmlschema/tests/test_package.py b/xmlschema/tests/test_package.py
index f3a2c47..619164a 100644
--- a/xmlschema/tests/test_package.py
+++ b/xmlschema/tests/test_package.py
@@ -121,11 +121,8 @@ class TestPackaging(unittest.TestCase):
message = "\nFound a debug missing statement at line %d or file %r: %r"
filename = None
file_excluded = []
- files = (
- glob.glob(os.path.join(self.source_dir, '*.py')) +
+ files = glob.glob(os.path.join(self.source_dir, '*.py')) + \
glob.glob(os.path.join(self.source_dir, 'validators/*.py'))
- )
-
for line in fileinput.input(files):
if fileinput.isfirstline():
filename = fileinput.filename()
diff --git a/xmlschema/tests/test_schemas.py b/xmlschema/tests/test_schemas.py
index ac4ab13..2f86602 100644
--- a/xmlschema/tests/test_schemas.py
+++ b/xmlschema/tests/test_schemas.py
@@ -55,11 +55,11 @@ class TestXMLSchema10(XMLSchemaTestCase):
source = """
{0}
-
+
<{1}Content>
-
- {2}
+
+ {2}
{1}Content>
@@ -95,7 +95,6 @@ class TestXMLSchema10(XMLSchemaTestCase):
-
@@ -156,7 +155,6 @@ class TestXMLSchema10(XMLSchemaTestCase):
-
@@ -360,11 +358,9 @@ class TestXMLSchema10(XMLSchemaTestCase):
-
-
@@ -421,7 +417,7 @@ class TestXMLSchema10(XMLSchemaTestCase):
def test_base_schemas(self):
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):
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'})))
def test_open_content(self):
- schema = self.check_schema("""
+ self.check_schema("""
@@ -583,6 +579,7 @@ class TestXMLSchema11(TestXMLSchema10):
""")
+
def make_schema_test_class(test_file, test_args, test_num, schema_class, check_with_lxml):
"""
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
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)
TestSchema.__name__ = TestSchema.__qualname__ = str('TestSchema{0:03}'.format(test_num))
diff --git a/xmlschema/tests/test_validators.py b/xmlschema/tests/test_validators.py
index 170bc7e..472221a 100644
--- a/xmlschema/tests/test_validators.py
+++ b/xmlschema/tests/test_validators.py
@@ -24,7 +24,7 @@ from elementpath import datatypes
import xmlschema
from xmlschema import (
- XMLSchemaEncodeError, XMLSchemaValidationError, XMLSchema, ParkerConverter,
+ XMLSchemaEncodeError, XMLSchemaValidationError, ParkerConverter,
BadgerFishConverter, AbderaConverter, JsonMLConverter
)
from xmlschema.compat import unicode_type, ordered_dict_class
@@ -56,11 +56,11 @@ _VEHICLES_DICT_ALT = [
{'vh:cars': [
{'vh:car': None, '@make': 'Porsche', '@model': '911'},
{'vh:car': None, '@make': 'Porsche', '@model': '911'}
- ]},
+ ]},
{'vh:bikes': [
{'vh:bike': None, '@make': 'Harley-Davidson', '@model': 'WL'},
{'vh:bike': None, '@make': 'Yamaha', '@model': 'XS650'}
- ]},
+ ]},
{'@xsi:schemaLocation': 'http://example.com/vehicles vehicles.xsd'}
]
@@ -95,7 +95,7 @@ _COLLECTION_DICT = {
'position': 2,
'title': None,
'year': '1925'
- }]
+ }]
}
_COLLECTION_PARKER = {
@@ -165,7 +165,7 @@ _COLLECTION_BADGERFISH = {
'position': {'$': 2},
'title': {},
'year': {'$': '1925'}
- }]
+ }]
}
}
@@ -624,7 +624,7 @@ class TestValidation11(TestValidation):
" beta"
""))
self.assertFalse(xs.is_valid(""
- " alpha" # Misses required attribute
+ " alpha" # Misses required attribute
" beta"
""))
@@ -912,6 +912,47 @@ class TestDecoding(XMLSchemaTestCase):
self.check_decode(decimal_or_nan, '95.0', Decimal('95.0'))
self.check_decode(decimal_or_nan, 'NaN', u'NaN')
+ def test_default_values(self):
+ # From issue #108
+ xsd_text = """
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+ schema = self.schema_class(xsd_text)
+ self.assertEqual(schema.to_dict("text"),
+ {'@attrWithDefault': 'default_value',
+ '@attrWithFixed': 'fixed_value',
+ '$': 'text'})
+ self.assertEqual(schema.to_dict(""),
+ {'@attrWithDefault': 'default_value',
+ '@attrWithFixed': 'fixed_value',
+ '$': 'default_value'})
+ self.assertEqual(schema.to_dict("""text"""),
+ {'$': 'text',
+ '@attr': 'attr_value',
+ '@attrWithDefault': 'default_value',
+ '@attrWithFixed': 'fixed_value'})
+
+ self.assertEqual(schema.to_dict("text", use_defaults=False),
+ {'@attrWithFixed': 'fixed_value', '$': 'text'})
+ self.assertEqual(schema.to_dict("""text""", use_defaults=False),
+ {'$': 'text', '@attr': 'attr_value', '@attrWithFixed': 'fixed_value'})
+ self.assertEqual(schema.to_dict("", use_defaults=False), {'@attrWithFixed': 'fixed_value'})
+
+ self.assertEqual(schema.to_dict(""), 'default_value')
+ self.assertIsNone(schema.to_dict("", use_defaults=False))
+
class TestDecoding11(TestDecoding):
schema_class = XMLSchema11
@@ -1112,7 +1153,7 @@ class TestEncoding(XMLSchemaTestCase):
-
+
@@ -1142,7 +1183,7 @@ class TestEncoding(XMLSchemaTestCase):
-
+
""")
diff --git a/xmlschema/validators/attributes.py b/xmlschema/validators/attributes.py
index 41a21eb..f148e0a 100644
--- a/xmlschema/validators/attributes.py
+++ b/xmlschema/validators/attributes.py
@@ -230,7 +230,7 @@ class XsdAttribute(XsdComponent, ValidationMixin):
yield obj
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
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)
@@ -297,7 +297,7 @@ class Xsd11Attribute(XsdAttribute):
class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
"""
Class for XSD 'attributeGroup' definitions.
-
+
- Content: (annotation?, (simpleContent | complexContent |
+ Content: (annotation?, (simpleContent | complexContent |
((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
"""
@@ -627,7 +627,7 @@ class Xsd11ComplexType(XsdComplexType):
name = NCName
defaultAttributesApply = boolean : true
{any attributes with non-schema namespace . . .}>
- Content: (annotation?, (simpleContent | complexContent | (openContent?,
+ Content: (annotation?, (simpleContent | complexContent | (openContent?,
(group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), assert*)))
"""
diff --git a/xmlschema/validators/elements.py b/xmlschema/validators/elements.py
index 98f0bad..50fd0f7 100644
--- a/xmlschema/validators/elements.py
+++ b/xmlschema/validators/elements.py
@@ -40,7 +40,7 @@ XSD_ATTRIBUTE_GROUP_ELEMENT = etree_element(XSD_ATTRIBUTE_GROUP)
class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin):
"""
Class for XSD 1.0 'element' declarations.
-
+
#
"""
-This module contains functions and classes for managing namespaces's
-XSD declarations/definitions.
+This module contains functions and classes for namespaces XSD declarations/definitions.
"""
from __future__ import unicode_literals
import re
diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py
index 287fa24..2242409 100644
--- a/xmlschema/validators/groups.py
+++ b/xmlschema/validators/groups.py
@@ -503,7 +503,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
if depth <= MAX_MODEL_DEPTH:
for item in self:
if isinstance(item, XsdGroup):
- for e in item.iter_elements(depth+1):
+ for e in item.iter_elements(depth + 1):
yield e
else:
yield item
diff --git a/xmlschema/validators/identities.py b/xmlschema/validators/identities.py
index e026c43..8c1cd7a 100644
--- a/xmlschema/validators/identities.py
+++ b/xmlschema/validators/identities.py
@@ -204,8 +204,8 @@ class XsdIdentity(XsdComponent):
values[v] += 1
for value, count in values.items():
- if count > 1:
- yield XMLSchemaValidationError(self, elem, reason="duplicated value %r." % value)
+ if value and count > 1:
+ yield XMLSchemaValidationError(self, elem, reason="duplicated value {!r}.".format(value))
class XsdUnique(XsdIdentity):
@@ -300,6 +300,6 @@ class XsdKeyref(XsdIdentity):
continue
if v not in refer_values:
- 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))
+ reason = "Key {!r} with value {!r} not found for identity constraint of element {!r}." \
+ .format(self.prefixed_name, v, qname_to_prefixed(elem.tag, self.namespaces))
yield XMLSchemaValidationError(validator=self, obj=elem, reason=reason)
diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py
index e9381a8..40dec63 100644
--- a/xmlschema/validators/models.py
+++ b/xmlschema/validators/models.py
@@ -155,7 +155,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
elif not item.is_pointless(parent=self):
yield item
else:
- for obj in item.iter_model(depth+1):
+ for obj in item.iter_model(depth + 1):
yield obj
def iter_elements(self, depth=0):
@@ -169,7 +169,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
raise XMLSchemaModelDepthError(self)
for item in self:
if isinstance(item, ModelGroup):
- for e in item.iter_elements(depth+1):
+ for e in item.iter_elements(depth + 1):
yield e
else:
yield item
@@ -178,7 +178,7 @@ class ModelGroup(MutableSequence, ParticleMixin):
if depth <= MAX_MODEL_DEPTH:
for item in self:
if isinstance(item, ModelGroup):
- for e in item.iter_subelements(depth+1):
+ for e in item.iter_subelements(depth + 1):
yield e
else:
yield item
@@ -269,8 +269,8 @@ def distinguishable_paths(path1, path2):
if path1[depth].model != 'sequence':
return before1 and before2 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))
+ (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))
elif path1[depth].max_occurs == 1:
return before2 or (before1 or univocal1) and (e1.is_univocal() or after1)
else:
diff --git a/xmlschema/validators/simple_types.py b/xmlschema/validators/simple_types.py
index 2b0d59c..3f5dc97 100644
--- a/xmlschema/validators/simple_types.py
+++ b/xmlschema/validators/simple_types.py
@@ -374,8 +374,8 @@ class XsdSimpleType(XsdType, ValidationMixin):
# simpleType's derived classes:
class XsdAtomic(XsdSimpleType):
"""
- Class for atomic simpleType definitions. An atomic definition has
- a base_type attribute that refers to primitive or derived atomic
+ Class for atomic simpleType definitions. An atomic definition has
+ a base_type attribute that refers to primitive or derived atomic
built-in type or another derived simpleType.
"""
_special_types = {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE, XSD_ANY_ATOMIC_TYPE}
@@ -599,9 +599,9 @@ class XsdAtomicBuiltin(XsdAtomic):
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.
-
+
- Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
- maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
+ Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
+ maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
enumeration | whiteSpace | pattern)*))
"""
@@ -1269,9 +1269,9 @@ class Xsd11AtomicRestriction(XsdAtomicRestriction):
base = QName
id = ID
{any attributes with non-schema namespace . . .}>
- Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
- maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
- enumeration | whiteSpace | pattern | assertion | explicitTimezone |
+ Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
+ maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
+ enumeration | whiteSpace | pattern | assertion | explicitTimezone |
{any with namespace: ##other})*))
"""
diff --git a/xmlschema/validators/wildcards.py b/xmlschema/validators/wildcards.py
index 584ead1..bc221f8 100644
--- a/xmlschema/validators/wildcards.py
+++ b/xmlschema/validators/wildcards.py
@@ -25,6 +25,9 @@ from .xsdbase import ValidationMixin, XsdComponent, ParticleMixin
class XsdWildcard(XsdComponent, ValidationMixin):
names = {}
+ namespace = '##any'
+ not_namespace = ()
+ not_qname = ()
def __init__(self, elem, schema, parent):
if parent is None:
@@ -40,15 +43,15 @@ class XsdWildcard(XsdComponent, ValidationMixin):
super(XsdWildcard, self)._parse()
# Parse namespace and processContents
- namespace = self.elem.get('namespace', '##any')
- items = namespace.strip().split()
- if len(items) == 1 and items[0] in ('##any', '##other', '##local', '##targetNamespace'):
- self.namespace = namespace.strip()
- elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in items):
+ namespace = self.elem.get('namespace', '##any').strip()
+ if namespace == '##any':
+ pass
+ elif namespace in {'##other', '##local', '##targetNamespace'}:
+ 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.namespace = '##any'
else:
- self.namespace = namespace.strip()
+ self.namespace = namespace
self.process_contents = self.elem.get('processContents', '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)
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
elif self.namespace == '##other':
if namespace:
@@ -261,7 +272,7 @@ class XsdAnyElement(XsdWildcard, ParticleMixin, ElementPathMixin):
class XsdAnyAttribute(XsdWildcard):
"""
Class for XSD 1.0 'anyAttribute' wildcards.
-
+
"""
- 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):
@@ -432,7 +420,32 @@ class Xsd11AnyAttribute(XsdAnyAttribute):
Content: (annotation?)
"""
- 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):
@@ -467,6 +480,10 @@ class XsdOpenContent(XsdComponent):
if child is not None and child.tag == XSD_ANY:
self.any_element = Xsd11AnyElement(child, self.schema, self)
+ @property
+ def built(self):
+ return True
+
class XsdDefaultOpenContent(XsdOpenContent):
"""