Skip to content

add_attribute_substitutions

xsdata.codegen.handlers.add_attribute_substitutions

AddAttributeSubstitutions

Bases: RelativeHandlerInterface

Apply substitution attributes to the given class recursively.

Parameters:

Name Type Description Default
container ContainerInterface

The class container instance

required

Attributes:

Name Type Description
substitutions Optional[Dict[str, List[Attr]]]

Mapping of type names to attr values

Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class AddAttributeSubstitutions(RelativeHandlerInterface):
    """Apply substitution attributes to the given class recursively.

    Args:
        container: The class container instance

    Attributes:
        substitutions: Mapping of type names to attr values
    """

    __slots__ = "substitutions"

    def __init__(self, container: ContainerInterface):
        super().__init__(container)
        self.substitutions: Optional[Dict[str, List[Attr]]] = None

    def process(self, target: Class):
        """Process the given class attrs for substitution groups.

        This method will ignore attrs in the class derived from
        a xs:enumeration, xs:anyType and xs:any. If this is the
        first time we call the method, build the substitution
        map.

        Args:
            target: The target class instance
        """
        if self.substitutions is None:
            self.create_substitutions()

        for attr in list(target.attrs):
            if not (attr.is_enumeration or attr.is_wildcard):
                self.process_attribute(target, attr)

    def process_attribute(self, target: Class, attr: Attr):
        """Add substitution attrs that refer to the attr type.

        If the given attr is referenced in substitution groups
        clone all substitution attrs and place them bellow
        the original attr. Convert all the attrs of the group
        to repeatable choice elements.

        Guard against multiple substitutions in case of xs:groups.

        Args:
            target: The target class instance
            attr: The source attr instance to check and process
        """
        index = target.attrs.index(attr)
        assert self.substitutions is not None

        for attr_type in attr.types:
            if attr_type.substituted:
                continue

            attr_type.substituted = True
            for substitution in self.substitutions.get(attr_type.qname, []):
                self.prepare_substituted(attr)

                clone = ClassUtils.clone_attribute(substitution, attr.restrictions)
                clone.restrictions.min_occurs = 0
                clone.restrictions.max_occurs = attr.restrictions.max_occurs

                attr.substitution = clone.substitution = attr_type.name

                pos = collections.find(target.attrs, clone)
                index = pos + 1 if pos > -1 else index
                target.attrs.insert(index, clone)

                self.process_attribute(target, clone)

    def create_substitutions(self):
        """Build the substitutions mapping of type names to attr values.

        The values are simple reference attrs that we can easily
        clone later on demand.
        """
        self.substitutions = defaultdict(list)
        for obj in self.container:
            for qname in obj.substitutions:
                attr = self.create_substitution(obj)
                self.substitutions[qname].append(attr)

    @classmethod
    def prepare_substituted(cls, attr: Attr):
        """Prepare the original attr for substitutions.

        Effectively place the attr inside a xs:choice container
        with min occurs zero.
        """
        attr.restrictions.min_occurs = 0
        if not attr.restrictions.choice:
            choice = id(attr)
            attr.restrictions.choice = choice
            attr.restrictions.path.append(("c", choice, 1, 1))

    @classmethod
    def create_substitution(cls, source: Class) -> Attr:
        """Create a reference attr to the source class qname.

        Args:
            source: The source class to reference

        Returns:
            The reference to the source class attr.
        """
        return Attr(
            name=source.name,
            types=[AttrType(qname=source.qname)],
            tag=Tag.ELEMENT,
            namespace=source.namespace,
        )

process(target)

Process the given class attrs for substitution groups.

This method will ignore attrs in the class derived from a xs:enumeration, xs:anyType and xs:any. If this is the first time we call the method, build the substitution map.

Parameters:

Name Type Description Default
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def process(self, target: Class):
    """Process the given class attrs for substitution groups.

    This method will ignore attrs in the class derived from
    a xs:enumeration, xs:anyType and xs:any. If this is the
    first time we call the method, build the substitution
    map.

    Args:
        target: The target class instance
    """
    if self.substitutions is None:
        self.create_substitutions()

    for attr in list(target.attrs):
        if not (attr.is_enumeration or attr.is_wildcard):
            self.process_attribute(target, attr)

process_attribute(target, attr)

Add substitution attrs that refer to the attr type.

If the given attr is referenced in substitution groups clone all substitution attrs and place them bellow the original attr. Convert all the attrs of the group to repeatable choice elements.

Guard against multiple substitutions in case of xs:groups.

Parameters:

Name Type Description Default
target Class

The target class instance

required
attr Attr

The source attr instance to check and process

required
Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
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
def process_attribute(self, target: Class, attr: Attr):
    """Add substitution attrs that refer to the attr type.

    If the given attr is referenced in substitution groups
    clone all substitution attrs and place them bellow
    the original attr. Convert all the attrs of the group
    to repeatable choice elements.

    Guard against multiple substitutions in case of xs:groups.

    Args:
        target: The target class instance
        attr: The source attr instance to check and process
    """
    index = target.attrs.index(attr)
    assert self.substitutions is not None

    for attr_type in attr.types:
        if attr_type.substituted:
            continue

        attr_type.substituted = True
        for substitution in self.substitutions.get(attr_type.qname, []):
            self.prepare_substituted(attr)

            clone = ClassUtils.clone_attribute(substitution, attr.restrictions)
            clone.restrictions.min_occurs = 0
            clone.restrictions.max_occurs = attr.restrictions.max_occurs

            attr.substitution = clone.substitution = attr_type.name

            pos = collections.find(target.attrs, clone)
            index = pos + 1 if pos > -1 else index
            target.attrs.insert(index, clone)

            self.process_attribute(target, clone)

create_substitutions()

Build the substitutions mapping of type names to attr values.

The values are simple reference attrs that we can easily clone later on demand.

Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
82
83
84
85
86
87
88
89
90
91
92
def create_substitutions(self):
    """Build the substitutions mapping of type names to attr values.

    The values are simple reference attrs that we can easily
    clone later on demand.
    """
    self.substitutions = defaultdict(list)
    for obj in self.container:
        for qname in obj.substitutions:
            attr = self.create_substitution(obj)
            self.substitutions[qname].append(attr)

prepare_substituted(attr) classmethod

Prepare the original attr for substitutions.

Effectively place the attr inside a xs:choice container with min occurs zero.

Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@classmethod
def prepare_substituted(cls, attr: Attr):
    """Prepare the original attr for substitutions.

    Effectively place the attr inside a xs:choice container
    with min occurs zero.
    """
    attr.restrictions.min_occurs = 0
    if not attr.restrictions.choice:
        choice = id(attr)
        attr.restrictions.choice = choice
        attr.restrictions.path.append(("c", choice, 1, 1))

create_substitution(source) classmethod

Create a reference attr to the source class qname.

Parameters:

Name Type Description Default
source Class

The source class to reference

required

Returns:

Type Description
Attr

The reference to the source class attr.

Source code in xsdata/codegen/handlers/add_attribute_substitutions.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
@classmethod
def create_substitution(cls, source: Class) -> Attr:
    """Create a reference attr to the source class qname.

    Args:
        source: The source class to reference

    Returns:
        The reference to the source class attr.
    """
    return Attr(
        name=source.name,
        types=[AttrType(qname=source.qname)],
        tag=Tag.ELEMENT,
        namespace=source.namespace,
    )