File: _observers.py

package info (click to toggle)
python-xmlschema 4.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 5,208 kB
  • sloc: python: 39,174; xml: 1,282; makefile: 36
file content (100 lines) | stat: -rw-r--r-- 3,365 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#
# Copyright (c), 2016-2020, SISSA (International School for Advanced Studies).
# All rights reserved.
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#
# @author Davide Brunato <brunato@sissa.it>
#
# mypy: ignore-errors
"""
Observers for testing XMLSchema classes.
"""
from functools import wraps
from itertools import chain

from xmlschema.names import XSD_NAMESPACE, XSD_ANY_TYPE
from xmlschema.validators import XMLSchema10, XMLSchema11, XsdGroup, \
    XsdAttributeGroup, XsdComplexType, XsdComponent, XsdBuilders


class SchemaObserver:
    """
    Observer that registers created components. Run the 'clear' method after each usage.
    """
    components = []
    dummy_components = []

    @classmethod
    def observed_builder(cls, builder):
        if isinstance(builder, type):
            class BuilderProxy(builder):
                def __init__(self, *args, **kwargs):
                    super().__init__(*args, **kwargs)
                    assert isinstance(self, XsdComponent)

                    if not cls.is_dummy_component(self):
                        cls.components.append(self)
                    else:
                        cls.dummy_components.append(self)

            BuilderProxy.__name__ = builder.__name__
            return BuilderProxy

        elif callable(builder):
            @wraps(builder)
            def builder_proxy(*args, **kwargs):
                obj = builder(*args, **kwargs)
                assert isinstance(obj, XsdComponent)

                if not cls.is_dummy_component(obj):
                    cls.components.append(obj)
                else:
                    cls.dummy_components.append(obj)
                return obj

            return builder_proxy

    @classmethod
    def clear(cls) -> None:
        del cls.components[:]
        del cls.dummy_components[:]

    @classmethod
    def is_dummy_component(cls, component) -> bool:
        # Dummy components are empty attribute groups and xs:anyType
        # definitions not related to XSD namespace.
        if component.parent in cls.dummy_components:
            return True
        elif isinstance(component, XsdAttributeGroup):
            return not component
        elif isinstance(component, XsdComplexType):
            return component.name == XSD_ANY_TYPE and \
                component.target_namespace != XSD_NAMESPACE
        elif isinstance(component, XsdGroup) and component.parent is not None:
            return component.parent.name == XSD_ANY_TYPE and \
                component.target_namespace != XSD_NAMESPACE
        return False


class ObservedBuilders(XsdBuilders):

    def __set_name__(self, cls, name):
        super().__set_name__(cls, name)

        for attr in chain(self.__dict__, self.__slots__):
            value = getattr(self, attr)
            if isinstance(value, dict):
                for k, v in value.items():
                    value[k] = SchemaObserver.observed_builder(v)
            elif isinstance(value, type):
                object.__setattr__(self, attr, SchemaObserver.observed_builder(value))


class ObservedXMLSchema10(XMLSchema10):
    builders = ObservedBuilders()


class ObservedXMLSchema11(XMLSchema11):
    xsd_builders = ObservedBuilders()