Skip to content

create_wrapper_fields

xsdata.codegen.handlers.create_wrapper_fields

CreateWrapperFields

Bases: RelativeHandlerInterface

Create wrapper fields.

Parameters:

Name Type Description Default
container ContainerInterface

The class container instance

required
Source code in xsdata/codegen/handlers/create_wrapper_fields.py
  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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
class CreateWrapperFields(RelativeHandlerInterface):
    """Create wrapper fields.

    Args:
        container: The class container instance
    """

    def process(self, target: Class):
        """Process the given class attrs and choices.

        Args:
            target: The target class instance
        """
        if not self.container.config.output.wrapper_fields:
            return

        wrapped = False
        wrapped_inner = False
        for attr in target.attrs:
            if not self.validate_attr(attr):
                continue

            inner, source = self.find_source_attr(target, attr)
            if not source:
                continue

            self.wrap_field(source, attr, inner)
            wrapped = True
            wrapped_inner = wrapped_inner or inner

        if wrapped:
            ClassUtils.rename_duplicate_attributes(target)

            if inner:
                ClassUtils.clean_inner_classes(target)

    @classmethod
    def wrap_field(cls, source: Attr, attr: Attr, inner: bool):
        """Create a wrapper field.

        Clone the source attr and update its name, local name and wrapper
        attributes.

        Args:
            source: The source attr instance
            attr: The attr instance to wrap
            inner: Specify if the source is from an inner class
        """
        wrapper = attr.local_name

        attr.swap(source)
        attr.wrapper = wrapper
        attr.types[0].forward = False

    def find_source_attr(
        self, parent: Class, attr: Attr
    ) -> Tuple[bool, Optional[Attr]]:
        """Find the source type for the given attr type instance.

        If it's a forward reference, look up the source in
        the parent class inners.

        Args:
            parent: The parent class instance
            attr: The attr instance to find a valid source attr

        Returns:
            A tuple of whether the source attr is inner and the source attr.
        """
        tp = attr.types[0]
        inner = False
        if tp.forward:
            source = self.container.find_inner(parent, tp.qname)
            inner = True
        else:
            source = self.container.first(tp.qname)

        if self.validate_source(source, attr.namespace):
            return inner, source.attrs[0]

        return inner, None

    @classmethod
    def validate_attr(cls, attr: Attr) -> bool:
        """Validate if the attr can be converted to a wrapper field.

        Rules:
            1. Must be an element
            2. Must have only one user type
            4. The element can't be optional
            5. The element can't be a list element

        Args:
            attr: The attr instance to validate

        Returns:
            Whether the attr can be converted to a wrapper.
        """
        return (
            attr.is_element
            and len(attr.types) == 1
            and not attr.types[0].native
            and not attr.is_list
            and not attr.is_optional
        )

    @classmethod
    def validate_source(cls, source: Class, namespace: Optional[str]) -> bool:
        """Validate if the source class can be converted to a wrapper field.

        Rules:
            1. It must not have any extensions
            2. It must contain exactly one type
            3. It must be derived from a xs:element
            4. It must not be optional
            5. It must not be a forward reference
            6. The source attr namespace must match the namespace

        Args:
            source: The source class instance to validate
            namespace: The processing attr namespace

        Returns:
            Whether the source class can be converted to a wrapper.
        """

        def ns_equal(a: Optional[str], b: Optional[str]):
            return (a or "") == (b or "")

        return (
            not source.extensions
            and len(source.attrs) == 1
            and source.attrs[0].is_element
            and not source.attrs[0].is_optional
            and not source.attrs[0].is_forward_ref
            and ns_equal(source.attrs[0].namespace, namespace)
        )

process(target)

Process the given class attrs and choices.

Parameters:

Name Type Description Default
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/create_wrapper_fields.py
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
def process(self, target: Class):
    """Process the given class attrs and choices.

    Args:
        target: The target class instance
    """
    if not self.container.config.output.wrapper_fields:
        return

    wrapped = False
    wrapped_inner = False
    for attr in target.attrs:
        if not self.validate_attr(attr):
            continue

        inner, source = self.find_source_attr(target, attr)
        if not source:
            continue

        self.wrap_field(source, attr, inner)
        wrapped = True
        wrapped_inner = wrapped_inner or inner

    if wrapped:
        ClassUtils.rename_duplicate_attributes(target)

        if inner:
            ClassUtils.clean_inner_classes(target)

wrap_field(source, attr, inner) classmethod

Create a wrapper field.

Clone the source attr and update its name, local name and wrapper attributes.

Parameters:

Name Type Description Default
source Attr

The source attr instance

required
attr Attr

The attr instance to wrap

required
inner bool

Specify if the source is from an inner class

required
Source code in xsdata/codegen/handlers/create_wrapper_fields.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@classmethod
def wrap_field(cls, source: Attr, attr: Attr, inner: bool):
    """Create a wrapper field.

    Clone the source attr and update its name, local name and wrapper
    attributes.

    Args:
        source: The source attr instance
        attr: The attr instance to wrap
        inner: Specify if the source is from an inner class
    """
    wrapper = attr.local_name

    attr.swap(source)
    attr.wrapper = wrapper
    attr.types[0].forward = False

find_source_attr(parent, attr)

Find the source type for the given attr type instance.

If it's a forward reference, look up the source in the parent class inners.

Parameters:

Name Type Description Default
parent Class

The parent class instance

required
attr Attr

The attr instance to find a valid source attr

required

Returns:

Type Description
Tuple[bool, Optional[Attr]]

A tuple of whether the source attr is inner and the source attr.

Source code in xsdata/codegen/handlers/create_wrapper_fields.py
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
def find_source_attr(
    self, parent: Class, attr: Attr
) -> Tuple[bool, Optional[Attr]]:
    """Find the source type for the given attr type instance.

    If it's a forward reference, look up the source in
    the parent class inners.

    Args:
        parent: The parent class instance
        attr: The attr instance to find a valid source attr

    Returns:
        A tuple of whether the source attr is inner and the source attr.
    """
    tp = attr.types[0]
    inner = False
    if tp.forward:
        source = self.container.find_inner(parent, tp.qname)
        inner = True
    else:
        source = self.container.first(tp.qname)

    if self.validate_source(source, attr.namespace):
        return inner, source.attrs[0]

    return inner, None

validate_attr(attr) classmethod

Validate if the attr can be converted to a wrapper field.

Rules
  1. Must be an element
  2. Must have only one user type
  3. The element can't be optional
  4. The element can't be a list element

Parameters:

Name Type Description Default
attr Attr

The attr instance to validate

required

Returns:

Type Description
bool

Whether the attr can be converted to a wrapper.

Source code in xsdata/codegen/handlers/create_wrapper_fields.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@classmethod
def validate_attr(cls, attr: Attr) -> bool:
    """Validate if the attr can be converted to a wrapper field.

    Rules:
        1. Must be an element
        2. Must have only one user type
        4. The element can't be optional
        5. The element can't be a list element

    Args:
        attr: The attr instance to validate

    Returns:
        Whether the attr can be converted to a wrapper.
    """
    return (
        attr.is_element
        and len(attr.types) == 1
        and not attr.types[0].native
        and not attr.is_list
        and not attr.is_optional
    )

validate_source(source, namespace) classmethod

Validate if the source class can be converted to a wrapper field.

Rules
  1. It must not have any extensions
  2. It must contain exactly one type
  3. It must be derived from a xs:element
  4. It must not be optional
  5. It must not be a forward reference
  6. The source attr namespace must match the namespace

Parameters:

Name Type Description Default
source Class

The source class instance to validate

required
namespace Optional[str]

The processing attr namespace

required

Returns:

Type Description
bool

Whether the source class can be converted to a wrapper.

Source code in xsdata/codegen/handlers/create_wrapper_fields.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@classmethod
def validate_source(cls, source: Class, namespace: Optional[str]) -> bool:
    """Validate if the source class can be converted to a wrapper field.

    Rules:
        1. It must not have any extensions
        2. It must contain exactly one type
        3. It must be derived from a xs:element
        4. It must not be optional
        5. It must not be a forward reference
        6. The source attr namespace must match the namespace

    Args:
        source: The source class instance to validate
        namespace: The processing attr namespace

    Returns:
        Whether the source class can be converted to a wrapper.
    """

    def ns_equal(a: Optional[str], b: Optional[str]):
        return (a or "") == (b or "")

    return (
        not source.extensions
        and len(source.attrs) == 1
        and source.attrs[0].is_element
        and not source.attrs[0].is_optional
        and not source.attrs[0].is_forward_ref
        and ns_equal(source.attrs[0].namespace, namespace)
    )