Skip to content

flatten_class_extensions

xsdata.codegen.handlers.flatten_class_extensions

FlattenClassExtensions

Bases: RelativeHandlerInterface

Reduce class extensions by copying or creating new attributes.

Source code in xsdata/codegen/handlers/flatten_class_extensions.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
class FlattenClassExtensions(RelativeHandlerInterface):
    """Reduce class extensions by copying or creating new attributes."""

    __slots__ = ()

    def process(self, target: Class):
        """Process a class' extensions.

        Args:
            target: The target class instance
        """
        for extension in list(target.extensions):
            self.process_extension(target, extension)

    def process_extension(self, target: Class, extension: Extension):
        """Process a class extension.

        Slit the process to native xsd extensions and user defined
        types.

        Args:
            target: The target class instance
            extension: The class extension instance
        """
        if extension.type.native:
            self.process_native_extension(target, extension)
        else:
            self.process_dependency_extension(target, extension)

    @classmethod
    def process_native_extension(cls, target: Class, extension: Extension):
        """Native type flatten handler.

        In case of enumerations copy the native data type to all enum
        members, otherwise add a default text attr with the
        extension attributes.

        Args:
            target: The target class instance
            extension: The class extension instance
        """
        if target.is_enumeration:
            cls.replace_attributes_type(target, extension)
        else:
            cls.add_default_attribute(target, extension)

    def process_dependency_extension(self, target: Class, extension: Extension):
        """Process user defined extension types.

        Case:
            - Extension source is missing
            - Target class is an enumeration
            - Extension source is a simple type or an enumeration
            - Extension source is a complex type

        Args:
            target: The target class instance
            extension: The class extension instance
        """
        source = self.find_dependency(extension.type)
        if not source:
            logger.warning("Missing extension type: %s", extension.type.name)
            target.extensions.remove(extension)
        elif target.is_enumeration:
            self.process_enum_extension(source, target, extension)
        elif not source.is_complex_type or source.is_enumeration:
            self.process_simple_extension(source, target, extension)
        else:
            self.process_complex_extension(source, target, extension)

    def process_enum_extension(
        self,
        source: Class,
        target: Class,
        extension: Optional[Extension],
    ):
        """Process an enumeration class extension.

        Cases:
            1. Source is an enumeration: merge them
            2. Source is a simple type: copy all source attr types
            3. Source is a complex type
                3.1 Target has a single member: Restrict default value
                3.2 Target has multiple members: unsupported reset enumeration

        Args:
            source: The source class instance
            target: The target class instance
            extension: The class extension instance
        """
        if source.is_enumeration:
            self.merge_enumerations(source, target)
        elif not source.is_complex_type:
            self.merge_enumeration_types(source, target)
        elif len(target.attrs) == 1:
            self.set_default_value(source, target)
        else:
            # We can't subclass and override the value field
            # the target enumeration, mypy doesn't play nicely.
            target.attrs.clear()

        if extension:
            if target.is_enumeration:
                target.extensions.remove(extension)
            else:
                extension.type.reference = source.ref

    @classmethod
    def merge_enumerations(cls, source: Class, target: Class):
        """Merge enumeration members from source to target class.

        Args:
            source: The source class instance
            target: The target class instance
        """
        source_attrs = {attr.name: attr for attr in source.attrs}
        target.attrs = [
            source_attrs[attr.name].clone() if attr.name in source_attrs else attr
            for attr in target.attrs
        ]

    def merge_enumeration_types(self, source: Class, target: Class):
        """Merge the enumeration attr types and restrictions.

        Args:
            source: The source class instance
            target: The target class instance
        """
        source_attr = source.attrs[0]
        for tp in source_attr.types:
            if tp.native:
                for target_attr in target.attrs:
                    target_attr.types.append(tp.clone())
                    target_attr.restrictions.merge(source_attr.restrictions)
            else:
                base = self.find_dependency(tp)
                # It's impossible to have a missing reference now, the
                # source class has passed through AttributeTypeHandler
                # and any missing types have been reset.
                assert base is not None
                self.process_enum_extension(base, target, None)

    @classmethod
    def set_default_value(cls, source: Class, target: Class):
        """Set the default value from the source single enumeration.

        When a simple type is a restriction of an enumeration with
        only one member, we can safely set its default value
        to that member value as fixed.

        Args:
            source: The source class instance
            target: The target class instance
        """
        new_attr = ClassUtils.find_value_attr(source).clone()
        new_attr.types = target.attrs[0].types
        new_attr.default = target.attrs[0].default
        new_attr.fixed = True
        target.attrs = [new_attr]

    @classmethod
    def process_simple_extension(cls, source: Class, target: Class, ext: Extension):
        """Process simple type extensions.

        Cases:
            1. If target is source: drop the extension.
            2. If source is enumeration and target isn't create default value attribute.
            3. If both source and target are enumerations copy all attributes.
            4. If target is enumeration: drop the extension.

        Args:
            source: The source class instance
            target: The target class instance
            ext: The extension class instance
        """
        if source is target:
            target.extensions.remove(ext)
        elif source.is_enumeration and not target.is_enumeration:
            cls.add_default_attribute(target, ext)
        elif source.is_enumeration == target.is_enumeration:
            ClassUtils.copy_attributes(source, target, ext)
        else:  # this is an enumeration
            target.extensions.remove(ext)

    @classmethod
    def process_complex_extension(cls, source: Class, target: Class, ext: Extension):
        """Process complex type extensions.

        Compare source and target classes and either remove the
        extension completely, copy all source attributes to the target
        class or leave the extension alone.

        Args:
            source: The source class instance
            target: The target class instance
            ext: The extension class instance
        """
        if cls.should_remove_extension(source, target, ext):
            target.extensions.remove(ext)
        elif cls.should_flatten_extension(source, target):
            ClassUtils.copy_attributes(source, target, ext)
        else:
            ext.type.reference = id(source)

    def find_dependency(self, attr_type: AttrType) -> Optional[Class]:
        """Find dependency for the given extension type with priority.

        Search priority: xs:SimpleType >  xs:ComplexType

        Args:
            attr_type: The attr type instance

        Returns:
            The class instance or None if it's undefined.
        """
        conditions = (
            lambda x: x.tag == Tag.SIMPLE_TYPE,
            lambda x: x.tag == Tag.COMPLEX_TYPE,
        )

        for condition in conditions:
            result = self.container.find(attr_type.qname, condition=condition)
            if result:
                return result

        return None

    @classmethod
    def should_remove_extension(
        cls,
        source: Class,
        target: Class,
        extension: Extension,
    ) -> bool:
        """Return whether the extension should be removed.

        Violations:
            - Circular Reference
            - Forward Reference
            - Unordered sequences
            - MRO Violation A(B), C(B) and extensions includes A, B, C

        Args:
            source: The source class instance
            target: The target class instance
            extension: The extension class instance
        """
        if (
            source is target
            or target in source.inner
            or cls.have_unordered_sequences(source, target, extension)
        ):
            return True

        # MRO Violation
        collision = {ext.type.qname for ext in target.extensions}
        return any(ext.type.qname in collision for ext in source.extensions)

    @classmethod
    def should_flatten_extension(cls, source: Class, target: Class) -> bool:
        """Return whether the extension should be flattened.

        Rules:
            1. Source doesn't have a parent class
            2. Source class is a simple type
            3. Source class has a suffix attr and target has its own attrs
            4. Target class has a suffix attr

        Args:
            source: The source class instance
            target: The target class instance
        """
        if not source.extensions and (
            not source.is_complex_type
            or target.has_suffix_attr
            or (source.has_suffix_attr and target.attrs)
        ):
            return True

        return False

    @classmethod
    def have_unordered_sequences(
        cls,
        source: Class,
        target: Class,
        extension: Extension,
    ) -> bool:
        """Validate overriding sequence attrs are in order.

        Dataclasses fields ordering follows the python mro pattern, the
        parent fields are always first, and they are updated if the
        subclass is overriding any of them but the overall ordering
        doesn't change!

        @todo This needs a complete rewrite and most likely it needs to
        @todo move way down in the process chain.

        Args:
            source: The source class instance
            target: The target class instance
            extension: The extension class instance
        """
        if extension.tag == Tag.EXTENSION or source.extensions:
            return False

        sequence = [
            attr.name
            for attr in target.attrs
            if attr.restrictions.sequence is not None and not attr.is_prohibited
        ]
        if len(sequence) > 1:
            compare = [attr.name for attr in source.attrs if attr.name in sequence]
            if compare and compare != sequence:
                return True

        return False

    @classmethod
    def replace_attributes_type(cls, target: Class, extension: Extension):
        """Replace all attrs types with the extension's type.

        The extension is a native xsd datatype.

        Args:
            target: The target class instance
            extension: The extension class instance
        """
        target.extensions.remove(extension)
        for attr in target.attrs:
            attr.types.clear()
            attr.types.append(extension.type.clone())

    @classmethod
    def add_default_attribute(cls, target: Class, extension: Extension):
        """Convert extension to a value text attr.

        If the extension type is xs:anyType convert the
        attr into a wildcard attr to match everything.

        Args:
            target: The target class instance
            extension: The extension class instance
        """
        if extension.type.datatype != DataType.ANY_TYPE:
            tag = Tag.EXTENSION
            name = DEFAULT_ATTR_NAME
            namespace = None
        else:
            tag = Tag.ANY
            name = "@any_element"
            namespace = NamespaceType.ANY_NS

        attr = cls.get_or_create_attribute(target, name, tag)
        attr.types.append(extension.type.clone())
        attr.restrictions.merge(extension.restrictions)
        attr.namespace = namespace
        target.extensions.remove(extension)

    @classmethod
    def get_or_create_attribute(cls, target: Class, name: str, tag: str) -> Attr:
        """Find or create an attr with the given name and tag.

        If the attr doesn't exist, create a new required
        attr and prepend it in the attrs list.

        Args:
            target: The target class instance
            name: The attr name
            tag: The attr tag name
        """
        attr = ClassUtils.find_attr(target, name)
        if attr is None:
            attr = Attr(name=name, tag=tag)
            attr.restrictions.min_occurs = 1
            attr.restrictions.max_occurs = 1
            target.attrs.insert(0, attr)

        return attr

process(target)

Process a class' extensions.

Parameters:

Name Type Description Default
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
16
17
18
19
20
21
22
23
def process(self, target: Class):
    """Process a class' extensions.

    Args:
        target: The target class instance
    """
    for extension in list(target.extensions):
        self.process_extension(target, extension)

process_extension(target, extension)

Process a class extension.

Slit the process to native xsd extensions and user defined types.

Parameters:

Name Type Description Default
target Class

The target class instance

required
extension Extension

The class extension instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def process_extension(self, target: Class, extension: Extension):
    """Process a class extension.

    Slit the process to native xsd extensions and user defined
    types.

    Args:
        target: The target class instance
        extension: The class extension instance
    """
    if extension.type.native:
        self.process_native_extension(target, extension)
    else:
        self.process_dependency_extension(target, extension)

process_native_extension(target, extension) classmethod

Native type flatten handler.

In case of enumerations copy the native data type to all enum members, otherwise add a default text attr with the extension attributes.

Parameters:

Name Type Description Default
target Class

The target class instance

required
extension Extension

The class extension instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@classmethod
def process_native_extension(cls, target: Class, extension: Extension):
    """Native type flatten handler.

    In case of enumerations copy the native data type to all enum
    members, otherwise add a default text attr with the
    extension attributes.

    Args:
        target: The target class instance
        extension: The class extension instance
    """
    if target.is_enumeration:
        cls.replace_attributes_type(target, extension)
    else:
        cls.add_default_attribute(target, extension)

process_dependency_extension(target, extension)

Process user defined extension types.

Case
  • Extension source is missing
  • Target class is an enumeration
  • Extension source is a simple type or an enumeration
  • Extension source is a complex type

Parameters:

Name Type Description Default
target Class

The target class instance

required
extension Extension

The class extension instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def process_dependency_extension(self, target: Class, extension: Extension):
    """Process user defined extension types.

    Case:
        - Extension source is missing
        - Target class is an enumeration
        - Extension source is a simple type or an enumeration
        - Extension source is a complex type

    Args:
        target: The target class instance
        extension: The class extension instance
    """
    source = self.find_dependency(extension.type)
    if not source:
        logger.warning("Missing extension type: %s", extension.type.name)
        target.extensions.remove(extension)
    elif target.is_enumeration:
        self.process_enum_extension(source, target, extension)
    elif not source.is_complex_type or source.is_enumeration:
        self.process_simple_extension(source, target, extension)
    else:
        self.process_complex_extension(source, target, extension)

process_enum_extension(source, target, extension)

Process an enumeration class extension.

Cases
  1. Source is an enumeration: merge them
  2. Source is a simple type: copy all source attr types
  3. Source is a complex type 3.1 Target has a single member: Restrict default value 3.2 Target has multiple members: unsupported reset enumeration

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
extension Optional[Extension]

The class extension instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
 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
def process_enum_extension(
    self,
    source: Class,
    target: Class,
    extension: Optional[Extension],
):
    """Process an enumeration class extension.

    Cases:
        1. Source is an enumeration: merge them
        2. Source is a simple type: copy all source attr types
        3. Source is a complex type
            3.1 Target has a single member: Restrict default value
            3.2 Target has multiple members: unsupported reset enumeration

    Args:
        source: The source class instance
        target: The target class instance
        extension: The class extension instance
    """
    if source.is_enumeration:
        self.merge_enumerations(source, target)
    elif not source.is_complex_type:
        self.merge_enumeration_types(source, target)
    elif len(target.attrs) == 1:
        self.set_default_value(source, target)
    else:
        # We can't subclass and override the value field
        # the target enumeration, mypy doesn't play nicely.
        target.attrs.clear()

    if extension:
        if target.is_enumeration:
            target.extensions.remove(extension)
        else:
            extension.type.reference = source.ref

merge_enumerations(source, target) classmethod

Merge enumeration members from source to target class.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
118
119
120
121
122
123
124
125
126
127
128
129
130
@classmethod
def merge_enumerations(cls, source: Class, target: Class):
    """Merge enumeration members from source to target class.

    Args:
        source: The source class instance
        target: The target class instance
    """
    source_attrs = {attr.name: attr for attr in source.attrs}
    target.attrs = [
        source_attrs[attr.name].clone() if attr.name in source_attrs else attr
        for attr in target.attrs
    ]

merge_enumeration_types(source, target)

Merge the enumeration attr types and restrictions.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def merge_enumeration_types(self, source: Class, target: Class):
    """Merge the enumeration attr types and restrictions.

    Args:
        source: The source class instance
        target: The target class instance
    """
    source_attr = source.attrs[0]
    for tp in source_attr.types:
        if tp.native:
            for target_attr in target.attrs:
                target_attr.types.append(tp.clone())
                target_attr.restrictions.merge(source_attr.restrictions)
        else:
            base = self.find_dependency(tp)
            # It's impossible to have a missing reference now, the
            # source class has passed through AttributeTypeHandler
            # and any missing types have been reset.
            assert base is not None
            self.process_enum_extension(base, target, None)

set_default_value(source, target) classmethod

Set the default value from the source single enumeration.

When a simple type is a restriction of an enumeration with only one member, we can safely set its default value to that member value as fixed.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@classmethod
def set_default_value(cls, source: Class, target: Class):
    """Set the default value from the source single enumeration.

    When a simple type is a restriction of an enumeration with
    only one member, we can safely set its default value
    to that member value as fixed.

    Args:
        source: The source class instance
        target: The target class instance
    """
    new_attr = ClassUtils.find_value_attr(source).clone()
    new_attr.types = target.attrs[0].types
    new_attr.default = target.attrs[0].default
    new_attr.fixed = True
    target.attrs = [new_attr]

process_simple_extension(source, target, ext) classmethod

Process simple type extensions.

Cases
  1. If target is source: drop the extension.
  2. If source is enumeration and target isn't create default value attribute.
  3. If both source and target are enumerations copy all attributes.
  4. If target is enumeration: drop the extension.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
ext Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
@classmethod
def process_simple_extension(cls, source: Class, target: Class, ext: Extension):
    """Process simple type extensions.

    Cases:
        1. If target is source: drop the extension.
        2. If source is enumeration and target isn't create default value attribute.
        3. If both source and target are enumerations copy all attributes.
        4. If target is enumeration: drop the extension.

    Args:
        source: The source class instance
        target: The target class instance
        ext: The extension class instance
    """
    if source is target:
        target.extensions.remove(ext)
    elif source.is_enumeration and not target.is_enumeration:
        cls.add_default_attribute(target, ext)
    elif source.is_enumeration == target.is_enumeration:
        ClassUtils.copy_attributes(source, target, ext)
    else:  # this is an enumeration
        target.extensions.remove(ext)

process_complex_extension(source, target, ext) classmethod

Process complex type extensions.

Compare source and target classes and either remove the extension completely, copy all source attributes to the target class or leave the extension alone.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
ext Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
@classmethod
def process_complex_extension(cls, source: Class, target: Class, ext: Extension):
    """Process complex type extensions.

    Compare source and target classes and either remove the
    extension completely, copy all source attributes to the target
    class or leave the extension alone.

    Args:
        source: The source class instance
        target: The target class instance
        ext: The extension class instance
    """
    if cls.should_remove_extension(source, target, ext):
        target.extensions.remove(ext)
    elif cls.should_flatten_extension(source, target):
        ClassUtils.copy_attributes(source, target, ext)
    else:
        ext.type.reference = id(source)

find_dependency(attr_type)

Find dependency for the given extension type with priority.

Search priority: xs:SimpleType > xs:ComplexType

Parameters:

Name Type Description Default
attr_type AttrType

The attr type instance

required

Returns:

Type Description
Optional[Class]

The class instance or None if it's undefined.

Source code in xsdata/codegen/handlers/flatten_class_extensions.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def find_dependency(self, attr_type: AttrType) -> Optional[Class]:
    """Find dependency for the given extension type with priority.

    Search priority: xs:SimpleType >  xs:ComplexType

    Args:
        attr_type: The attr type instance

    Returns:
        The class instance or None if it's undefined.
    """
    conditions = (
        lambda x: x.tag == Tag.SIMPLE_TYPE,
        lambda x: x.tag == Tag.COMPLEX_TYPE,
    )

    for condition in conditions:
        result = self.container.find(attr_type.qname, condition=condition)
        if result:
            return result

    return None

should_remove_extension(source, target, extension) classmethod

Return whether the extension should be removed.

Violations
  • Circular Reference
  • Forward Reference
  • Unordered sequences
  • MRO Violation A(B), C(B) and extensions includes A, B, C

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
extension Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
@classmethod
def should_remove_extension(
    cls,
    source: Class,
    target: Class,
    extension: Extension,
) -> bool:
    """Return whether the extension should be removed.

    Violations:
        - Circular Reference
        - Forward Reference
        - Unordered sequences
        - MRO Violation A(B), C(B) and extensions includes A, B, C

    Args:
        source: The source class instance
        target: The target class instance
        extension: The extension class instance
    """
    if (
        source is target
        or target in source.inner
        or cls.have_unordered_sequences(source, target, extension)
    ):
        return True

    # MRO Violation
    collision = {ext.type.qname for ext in target.extensions}
    return any(ext.type.qname in collision for ext in source.extensions)

should_flatten_extension(source, target) classmethod

Return whether the extension should be flattened.

Rules
  1. Source doesn't have a parent class
  2. Source class is a simple type
  3. Source class has a suffix attr and target has its own attrs
  4. Target class has a suffix attr

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
@classmethod
def should_flatten_extension(cls, source: Class, target: Class) -> bool:
    """Return whether the extension should be flattened.

    Rules:
        1. Source doesn't have a parent class
        2. Source class is a simple type
        3. Source class has a suffix attr and target has its own attrs
        4. Target class has a suffix attr

    Args:
        source: The source class instance
        target: The target class instance
    """
    if not source.extensions and (
        not source.is_complex_type
        or target.has_suffix_attr
        or (source.has_suffix_attr and target.attrs)
    ):
        return True

    return False

have_unordered_sequences(source, target, extension) classmethod

Validate overriding sequence attrs are in order.

Dataclasses fields ordering follows the python mro pattern, the parent fields are always first, and they are updated if the subclass is overriding any of them but the overall ordering doesn't change!

@todo This needs a complete rewrite and most likely it needs to @todo move way down in the process chain.

Parameters:

Name Type Description Default
source Class

The source class instance

required
target Class

The target class instance

required
extension Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
@classmethod
def have_unordered_sequences(
    cls,
    source: Class,
    target: Class,
    extension: Extension,
) -> bool:
    """Validate overriding sequence attrs are in order.

    Dataclasses fields ordering follows the python mro pattern, the
    parent fields are always first, and they are updated if the
    subclass is overriding any of them but the overall ordering
    doesn't change!

    @todo This needs a complete rewrite and most likely it needs to
    @todo move way down in the process chain.

    Args:
        source: The source class instance
        target: The target class instance
        extension: The extension class instance
    """
    if extension.tag == Tag.EXTENSION or source.extensions:
        return False

    sequence = [
        attr.name
        for attr in target.attrs
        if attr.restrictions.sequence is not None and not attr.is_prohibited
    ]
    if len(sequence) > 1:
        compare = [attr.name for attr in source.attrs if attr.name in sequence]
        if compare and compare != sequence:
            return True

    return False

replace_attributes_type(target, extension) classmethod

Replace all attrs types with the extension's type.

The extension is a native xsd datatype.

Parameters:

Name Type Description Default
target Class

The target class instance

required
extension Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
329
330
331
332
333
334
335
336
337
338
339
340
341
342
@classmethod
def replace_attributes_type(cls, target: Class, extension: Extension):
    """Replace all attrs types with the extension's type.

    The extension is a native xsd datatype.

    Args:
        target: The target class instance
        extension: The extension class instance
    """
    target.extensions.remove(extension)
    for attr in target.attrs:
        attr.types.clear()
        attr.types.append(extension.type.clone())

add_default_attribute(target, extension) classmethod

Convert extension to a value text attr.

If the extension type is xs:anyType convert the attr into a wildcard attr to match everything.

Parameters:

Name Type Description Default
target Class

The target class instance

required
extension Extension

The extension class instance

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
@classmethod
def add_default_attribute(cls, target: Class, extension: Extension):
    """Convert extension to a value text attr.

    If the extension type is xs:anyType convert the
    attr into a wildcard attr to match everything.

    Args:
        target: The target class instance
        extension: The extension class instance
    """
    if extension.type.datatype != DataType.ANY_TYPE:
        tag = Tag.EXTENSION
        name = DEFAULT_ATTR_NAME
        namespace = None
    else:
        tag = Tag.ANY
        name = "@any_element"
        namespace = NamespaceType.ANY_NS

    attr = cls.get_or_create_attribute(target, name, tag)
    attr.types.append(extension.type.clone())
    attr.restrictions.merge(extension.restrictions)
    attr.namespace = namespace
    target.extensions.remove(extension)

get_or_create_attribute(target, name, tag) classmethod

Find or create an attr with the given name and tag.

If the attr doesn't exist, create a new required attr and prepend it in the attrs list.

Parameters:

Name Type Description Default
target Class

The target class instance

required
name str

The attr name

required
tag str

The attr tag name

required
Source code in xsdata/codegen/handlers/flatten_class_extensions.py
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
@classmethod
def get_or_create_attribute(cls, target: Class, name: str, tag: str) -> Attr:
    """Find or create an attr with the given name and tag.

    If the attr doesn't exist, create a new required
    attr and prepend it in the attrs list.

    Args:
        target: The target class instance
        name: The attr name
        tag: The attr tag name
    """
    attr = ClassUtils.find_attr(target, name)
    if attr is None:
        attr = Attr(name=name, tag=tag)
        attr.restrictions.min_occurs = 1
        attr.restrictions.max_occurs = 1
        target.attrs.insert(0, attr)

    return attr