Skip to content

vacuum_inner_classes

xsdata.codegen.handlers.vacuum_inner_classes

VacuumInnerClasses

Bases: HandlerInterface

Cleanup nested classes.

Search and vacuum inner classes with no attributes or a single extension or rename inner classes that have the same name as the outer/parent class.

Cases
  1. Filter duplicate inner classes
  2. Removing identical overriding fields can sometimes leave a class bare with just an extension. For inner classes we can safely replace the forward reference with the inner extension reference.
  3. Empty nested complexContent with no restrictions or extensions, we can replace these references with xs:anySimpleType
Source code in xsdata/codegen/handlers/vacuum_inner_classes.py
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
class VacuumInnerClasses(HandlerInterface):
    """Cleanup nested classes.

    Search and vacuum inner classes with no attributes or a single extension or
    rename inner classes that have the same name as the outer/parent class.

    Cases:
        1. Filter duplicate inner classes
        2. Removing identical overriding fields can sometimes leave a class
           bare with just an extension. For inner classes we can safely
           replace the forward reference with the inner extension reference.
        3. Empty nested complexContent with no restrictions or extensions,
           we can replace these references with xs:anySimpleType
    """

    __slots__ = ()

    def process(self, target: Class):
        """Process entrypoint for classes.

        Args:
            target: The target class instance
        """
        target.inner = collections.unique_sequence(target.inner, key="qname")
        for inner in list(target.inner):
            if not inner.attrs and len(inner.extensions) < 2:
                self.remove_inner(target, inner)
            elif inner.qname == target.qname:
                self.rename_inner(target, inner)

    @classmethod
    def remove_inner(cls, target: Class, inner: Class):
        """Remove inner class and update the target class attrs.

        Args:
            target: The target class instance
            inner: The nested class instance
        """
        target.inner.remove(inner)

        for attr_type in cls.find_attr_types(target, inner.qname):
            attr_type.circular = False
            attr_type.forward = False

            if inner.extensions:
                ext = inner.extensions[0]
                attr_type.reference = ext.type.reference
                attr_type.qname = ext.type.qname
                attr_type.native = False
            else:
                attr_type.native = True
                attr_type.qname = str(DataType.ANY_SIMPLE_TYPE)
                attr_type.reference = 0

    @classmethod
    def rename_inner(cls, target: Class, inner: Class):
        """Rename the inner class and update the target class attrs.

        The inner class will get the `Inner suffix`.

        Args:
            target: The target class instance
            inner: The nested class instance
        """
        namespace = inner.target_namespace
        old_qname = inner.qname
        inner.qname = build_qname(namespace, f"{inner.name}_Inner")

        for attr_type in cls.find_attr_types(target, old_qname):
            attr_type.qname = inner.qname

    @classmethod
    def find_attr_types(cls, target: Class, qname: str) -> Iterator[AttrType]:
        """Find attr and choice types by the qualified name.

        Args:
            target: The target class instance
            qname: The qualified name
        """
        for attr in target.attrs:
            for attr_type in attr.types:
                if attr_type.forward and attr_type.qname == qname:
                    yield attr_type

            for choice in attr.choices:
                for choice_type in choice.types:
                    if choice_type.forward and choice_type.qname == qname:
                        yield choice_type

process(target)

Process entrypoint for classes.

Parameters:

Name Type Description Default
target Class

The target class instance

required
Source code in xsdata/codegen/handlers/vacuum_inner_classes.py
27
28
29
30
31
32
33
34
35
36
37
38
def process(self, target: Class):
    """Process entrypoint for classes.

    Args:
        target: The target class instance
    """
    target.inner = collections.unique_sequence(target.inner, key="qname")
    for inner in list(target.inner):
        if not inner.attrs and len(inner.extensions) < 2:
            self.remove_inner(target, inner)
        elif inner.qname == target.qname:
            self.rename_inner(target, inner)

remove_inner(target, inner) classmethod

Remove inner class and update the target class attrs.

Parameters:

Name Type Description Default
target Class

The target class instance

required
inner Class

The nested class instance

required
Source code in xsdata/codegen/handlers/vacuum_inner_classes.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@classmethod
def remove_inner(cls, target: Class, inner: Class):
    """Remove inner class and update the target class attrs.

    Args:
        target: The target class instance
        inner: The nested class instance
    """
    target.inner.remove(inner)

    for attr_type in cls.find_attr_types(target, inner.qname):
        attr_type.circular = False
        attr_type.forward = False

        if inner.extensions:
            ext = inner.extensions[0]
            attr_type.reference = ext.type.reference
            attr_type.qname = ext.type.qname
            attr_type.native = False
        else:
            attr_type.native = True
            attr_type.qname = str(DataType.ANY_SIMPLE_TYPE)
            attr_type.reference = 0

rename_inner(target, inner) classmethod

Rename the inner class and update the target class attrs.

The inner class will get the Inner suffix.

Parameters:

Name Type Description Default
target Class

The target class instance

required
inner Class

The nested class instance

required
Source code in xsdata/codegen/handlers/vacuum_inner_classes.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@classmethod
def rename_inner(cls, target: Class, inner: Class):
    """Rename the inner class and update the target class attrs.

    The inner class will get the `Inner suffix`.

    Args:
        target: The target class instance
        inner: The nested class instance
    """
    namespace = inner.target_namespace
    old_qname = inner.qname
    inner.qname = build_qname(namespace, f"{inner.name}_Inner")

    for attr_type in cls.find_attr_types(target, old_qname):
        attr_type.qname = inner.qname

find_attr_types(target, qname) classmethod

Find attr and choice types by the qualified name.

Parameters:

Name Type Description Default
target Class

The target class instance

required
qname str

The qualified name

required
Source code in xsdata/codegen/handlers/vacuum_inner_classes.py
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
@classmethod
def find_attr_types(cls, target: Class, qname: str) -> Iterator[AttrType]:
    """Find attr and choice types by the qualified name.

    Args:
        target: The target class instance
        qname: The qualified name
    """
    for attr in target.attrs:
        for attr_type in attr.types:
            if attr_type.forward and attr_type.qname == qname:
                yield attr_type

        for choice in attr.choices:
            for choice_type in choice.types:
                if choice_type.forward and choice_type.qname == qname:
                    yield choice_type