diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 213513e..0af63c5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ CHANGELOG ********* +`v1.0.16`_ (2019-10-XX) +======================= +* Improved XMLResource with zip files interface and lazy +* Fix for validation with XSD wildcards and 'lax' process content +* Fix for issue #1... + `v1.0.15`_ (2019-10-13) ======================= * Improved XPath 2.0 bindings diff --git a/xmlschema/tests/test_factory/arguments.py b/xmlschema/tests/test_factory/arguments.py index 95ff4c2..49326cd 100644 --- a/xmlschema/tests/test_factory/arguments.py +++ b/xmlschema/tests/test_factory/arguments.py @@ -21,6 +21,7 @@ import re import argparse TEST_FACTORY_OPTIONS = { + 'narrow': '-n' in sys.argv or '--narrow' in sys.argv, # Skip extra checks (eg. other converters) 'extra_cases': '-x' in sys.argv or '--extra' in sys.argv, # Include extra test cases 'check_with_lxml': '-l' in sys.argv or '--lxml' in sys.argv, # Check with lxml.etree.XMLSchema (for XSD 1.0) } @@ -28,7 +29,8 @@ TEST_FACTORY_OPTIONS = { RUN_W3C_TEST_SUITE = '-w' in sys.argv or '--w3c' in sys.argv -sys.argv = [a for a in sys.argv if a not in {'-x', '--extra', '-l', '--lxml'}] # Clean sys.argv for unittest +sys.argv = [a for a in sys.argv if a not in + {'-x', '--extra', '-l', '--lxml', '-n', '--narrow'}] # Clean sys.argv for unittest def get_test_args(args_line): diff --git a/xmlschema/tests/test_factory/factory.py b/xmlschema/tests/test_factory/factory.py index 53e3700..08d62d4 100644 --- a/xmlschema/tests/test_factory/factory.py +++ b/xmlschema/tests/test_factory/factory.py @@ -38,6 +38,7 @@ def tests_factory(test_class_builder, suffix='xml'): test_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) testfiles = [os.path.join(test_dir, 'test_cases/testfiles')] + narrow = TEST_FACTORY_OPTIONS['narrow'] if TEST_FACTORY_OPTIONS['extra_cases']: package_dir = os.path.dirname(os.path.dirname(test_dir)) testfiles.extend(glob.glob(os.path.join(package_dir, 'test_cases/testfiles'))) @@ -94,7 +95,9 @@ def tests_factory(test_class_builder, suffix='xml'): schema_class = ObservedXMLSchema11 if test_args.inspect else XMLSchema11 check_with_lxml = False - test_class = test_class_builder(test_file, test_args, test_num, schema_class, check_with_lxml) + test_class = test_class_builder( + test_file, test_args, test_num, schema_class, narrow, check_with_lxml + ) test_classes[test_class.__name__] = test_class logger.debug("Add XSD %s test class %r.", test_args.version, test_class.__name__) diff --git a/xmlschema/tests/test_factory/schema_tests.py b/xmlschema/tests/test_factory/schema_tests.py index 6796ef3..5e3511a 100644 --- a/xmlschema/tests/test_factory/schema_tests.py +++ b/xmlschema/tests/test_factory/schema_tests.py @@ -27,7 +27,7 @@ from xmlschema.tests import XsdValidatorTestCase from .observers import SchemaObserver -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, narrow, check_with_lxml): """ Creates a schema test class. @@ -35,6 +35,7 @@ def make_schema_test_class(test_file, test_args, test_num, schema_class, check_w :param test_args: line arguments for test case. :param test_num: a positive integer number associated with the test case. :param schema_class: the schema class to use. + :param narrow: skip extra checks (observed inspections). :param check_with_lxml: if `True` compare with lxml XMLSchema class, reporting anomalies. \ Works only for XSD 1.0 tests. """ @@ -69,7 +70,7 @@ def make_schema_test_class(test_file, test_args, test_num, schema_class, check_w xs = schema_class(xsd_file, locations=locations, defuse=defuse, loglevel=loglevel) self.errors.extend(xs.maps.all_errors) - if inspect: + if narrow and inspect: components_ids = set([id(c) for c in xs.maps.iter_components()]) missing = [c for c in SchemaObserver.components if id(c) not in components_ids] if any(c for c in missing): diff --git a/xmlschema/tests/test_factory/validation_tests.py b/xmlschema/tests/test_factory/validation_tests.py index dfd2d50..651281e 100644 --- a/xmlschema/tests/test_factory/validation_tests.py +++ b/xmlschema/tests/test_factory/validation_tests.py @@ -47,7 +47,7 @@ def iter_nested_items(items, dict_class=dict, list_class=list): yield items -def make_validator_test_class(test_file, test_args, test_num, schema_class, check_with_lxml): +def make_validator_test_class(test_file, test_args, test_num, schema_class, narrow, check_with_lxml): """ Creates a validator test class. @@ -55,6 +55,7 @@ def make_validator_test_class(test_file, test_args, test_num, schema_class, chec :param test_args: line arguments for test case. :param test_num: a positive integer number associated with the test case. :param schema_class: the schema class to use. + :param narrow: skip other converters checks. :param check_with_lxml: if `True` compare with lxml XMLSchema class, reporting anomalies. \ Works only for XSD 1.0 tests. """ @@ -239,19 +240,21 @@ def make_validator_test_class(test_file, test_args, test_num, schema_class, chec options = {'namespaces': namespaces, 'dict_class': ordered_dict_class} self.check_etree_encode(root, cdata_prefix='#', **options) # Default converter - self.check_etree_encode(root, ParkerConverter, validation='lax', **options) - self.check_etree_encode(root, ParkerConverter, validation='skip', **options) - self.check_etree_encode(root, BadgerFishConverter, **options) - self.check_etree_encode(root, AbderaConverter, **options) - self.check_etree_encode(root, JsonMLConverter, **options) + if narrow: + self.check_etree_encode(root, ParkerConverter, validation='lax', **options) + self.check_etree_encode(root, ParkerConverter, validation='skip', **options) + self.check_etree_encode(root, BadgerFishConverter, **options) + self.check_etree_encode(root, AbderaConverter, **options) + self.check_etree_encode(root, JsonMLConverter, **options) options.pop('dict_class') self.check_json_serialization(root, cdata_prefix='#', **options) - self.check_json_serialization(root, ParkerConverter, validation='lax', **options) - self.check_json_serialization(root, ParkerConverter, validation='skip', **options) - self.check_json_serialization(root, BadgerFishConverter, **options) - self.check_json_serialization(root, AbderaConverter, **options) - self.check_json_serialization(root, JsonMLConverter, **options) + if narrow: + self.check_json_serialization(root, ParkerConverter, validation='lax', **options) + self.check_json_serialization(root, ParkerConverter, validation='skip', **options) + self.check_json_serialization(root, BadgerFishConverter, **options) + self.check_json_serialization(root, AbderaConverter, **options) + self.check_json_serialization(root, JsonMLConverter, **options) def check_decoding_and_encoding_with_lxml(self): xml_tree = lxml_etree.parse(xml_file) @@ -280,19 +283,21 @@ def make_validator_test_class(test_file, test_args, test_num, schema_class, chec 'dict_class': ordered_dict_class, } self.check_etree_encode(root, cdata_prefix='#', **options) # Default converter - self.check_etree_encode(root, ParkerConverter, validation='lax', **options) - self.check_etree_encode(root, ParkerConverter, validation='skip', **options) - self.check_etree_encode(root, BadgerFishConverter, **options) - self.check_etree_encode(root, AbderaConverter, **options) - self.check_etree_encode(root, JsonMLConverter, **options) + if narrow: + self.check_etree_encode(root, ParkerConverter, validation='lax', **options) + self.check_etree_encode(root, ParkerConverter, validation='skip', **options) + self.check_etree_encode(root, BadgerFishConverter, **options) + self.check_etree_encode(root, AbderaConverter, **options) + self.check_etree_encode(root, JsonMLConverter, **options) options.pop('dict_class') self.check_json_serialization(root, cdata_prefix='#', **options) - self.check_json_serialization(root, ParkerConverter, validation='lax', **options) - self.check_json_serialization(root, ParkerConverter, validation='skip', **options) - self.check_json_serialization(root, BadgerFishConverter, **options) - self.check_json_serialization(root, AbderaConverter, **options) - self.check_json_serialization(root, JsonMLConverter, **options) + if narrow: + self.check_json_serialization(root, ParkerConverter, validation='lax', **options) + self.check_json_serialization(root, ParkerConverter, validation='skip', **options) + self.check_json_serialization(root, BadgerFishConverter, **options) + self.check_json_serialization(root, AbderaConverter, **options) + self.check_json_serialization(root, JsonMLConverter, **options) def check_validate_and_is_valid_api(self): if expected_errors: diff --git a/xmlschema/tests/test_resources.py b/xmlschema/tests/test_resources.py index f5dbd5d..2519be2 100644 --- a/xmlschema/tests/test_resources.py +++ b/xmlschema/tests/test_resources.py @@ -14,6 +14,7 @@ This module runs tests concerning resources. """ import unittest import os +import platform try: from pathlib import PureWindowsPath, PurePath @@ -22,9 +23,9 @@ except ImportError: from xmlschema import ( fetch_namespaces, fetch_resource, normalize_url, fetch_schema, fetch_schema_locations, - load_xml_resource, XMLResource, XMLSchemaURLError + load_xml_resource, XMLResource, XMLSchemaURLError, XMLSchema ) -from xmlschema.tests import casepath +from xmlschema.tests import SKIP_REMOTE_TESTS, casepath from xmlschema.compat import urlopen, urlsplit, uses_relative, StringIO from xmlschema.etree import ElementTree, PyElementTree, lxml_etree, \ etree_element, py_etree_element @@ -44,6 +45,7 @@ class TestResources(unittest.TestCase): @classmethod def setUpClass(cls): + cls.schema_class = XMLSchema cls.vh_dir = casepath('examples/vehicles') cls.vh_xsd_file = casepath('examples/vehicles/vehicles.xsd') cls.vh_xml_file = casepath('examples/vehicles/vehicles.xml') diff --git a/xmlschema/validators/globals_.py b/xmlschema/validators/globals_.py index c9716d7..c469b63 100644 --- a/xmlschema/validators/globals_.py +++ b/xmlschema/validators/globals_.py @@ -15,7 +15,7 @@ from __future__ import unicode_literals import warnings from collections import Counter -from ..compat import string_base_type +from ..compat import string_base_type, lru_cache from ..exceptions import XMLSchemaKeyError, XMLSchemaTypeError, XMLSchemaValueError, XMLSchemaWarning from ..namespaces import XSD_NAMESPACE, LOCATION_HINTS, NamespaceResourcesMap from ..qnames import XSD_REDEFINE, XSD_OVERRIDE, XSD_NOTATION, XSD_ANY_TYPE, \ @@ -385,6 +385,7 @@ class XsdGlobals(XsdValidator): elif not any(schema.url == obj.url and schema.__class__ == obj.__class__ for obj in ns_schemas): ns_schemas.append(schema) + @lru_cache(maxsize=1000) def load_namespace(self, namespace, build=True): """ Load namespace from available location hints. Returns `True` if the namespace @@ -471,7 +472,7 @@ class XsdGlobals(XsdValidator): self.namespaces = namespaces else: - self.missing_locations.clear() + del self.missing_locations[:] for global_map in self.global_maps: global_map.clear() self.substitution_groups.clear()