Skip to content

unnest_inner_classes

xsdata.codegen.handlers.unnest_inner_classes

UnnestInnerClasses

Bases: RelativeHandlerInterface

Promote inner classes to root classes.

Source code in xsdata/codegen/handlers/unnest_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
 98
 99
100
101
102
103
104
class UnnestInnerClasses(RelativeHandlerInterface):
    """Promote inner classes to root classes."""

    __slots__ = ()

    def process(self, target: Class) -> None:
        """Promote all inner classes recursively.

        Args:
            target: The target class instance to process
        """
        inner_classes = {}
        inner_references = defaultdict(list)
        promote_all = self.container.config.output.unnest_classes
        for attr_type, source in self.find_forward_refs(target):
            inner = ClassUtils.find_nested(source, attr_type.qname)

            if not (promote_all or inner.is_enumeration):
                continue

            inner_classes[inner.ref] = inner
            inner_references[inner.ref].append(attr_type)

        for ref, inner in inner_classes.items():
            references = inner_references[ref]

            self.update_inner_class(inner)
            self.update_types(references, inner)
            self.container.add(inner)

        self.remove_orphan_inner_classes(target, promote_all)

    @classmethod
    def remove_orphan_inner_classes(cls, target: Class, promote_all: bool):
        """Remove inner classes with no attr references.

        Args:
            target: The target class instance to process
            promote_all: Whether to remove all inner classes or just the enumerations
        """
        for inner in target.inner.copy():
            if promote_all or inner.is_enumeration:
                target.inner.remove(inner)

    @classmethod
    def find_forward_refs(cls, target: Class) -> Iterator[tuple[AttrType, Class]]:
        """Find all forward references for all inner classes.

        Args:
            target: The target class instance to process

        Yields:
            A tuple of attr type and the parent class instance.
        """
        for attr in target.attrs:
            for tp in attr.types:
                if tp.forward and not tp.native:
                    yield tp, target

        for inner in target.inner:
            yield from cls.find_forward_refs(inner)

    @classmethod
    def update_inner_class(cls, target: Class):
        """Prepare the nested class to be added as root.

        Args:
            target: The target class
        """
        assert target.parent is not None
        name_parts = [target.parent.name, target.name]
        new_qname = build_qname(target.target_namespace, "_".join(name_parts))

        target.qname = new_qname

        assert target.parent is not None

        target.parent.inner.remove(target)
        target.parent = None
        target.local_type = True

    @classmethod
    def update_types(cls, types: list[AttrType], inner: Class):
        """Search and replace forward references.

        Return the number changes.

        Args:
            types: The types to search and replace
            inner: The updated inner class
        """
        for tp in types:
            tp.qname = inner.qname
            tp.forward = False
            tp.reference = inner.ref

process(target)

Promote all inner classes recursively.

Parameters:

Name Type Description Default
target Class

The target class instance to process

required
Source code in xsdata/codegen/handlers/unnest_inner_classes.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
def process(self, target: Class) -> None:
    """Promote all inner classes recursively.

    Args:
        target: The target class instance to process
    """
    inner_classes = {}
    inner_references = defaultdict(list)
    promote_all = self.container.config.output.unnest_classes
    for attr_type, source in self.find_forward_refs(target):
        inner = ClassUtils.find_nested(source, attr_type.qname)

        if not (promote_all or inner.is_enumeration):
            continue

        inner_classes[inner.ref] = inner
        inner_references[inner.ref].append(attr_type)

    for ref, inner in inner_classes.items():
        references = inner_references[ref]

        self.update_inner_class(inner)
        self.update_types(references, inner)
        self.container.add(inner)

    self.remove_orphan_inner_classes(target, promote_all)

remove_orphan_inner_classes(target, promote_all) classmethod

Remove inner classes with no attr references.

Parameters:

Name Type Description Default
target Class

The target class instance to process

required
promote_all bool

Whether to remove all inner classes or just the enumerations

required
Source code in xsdata/codegen/handlers/unnest_inner_classes.py
42
43
44
45
46
47
48
49
50
51
52
@classmethod
def remove_orphan_inner_classes(cls, target: Class, promote_all: bool):
    """Remove inner classes with no attr references.

    Args:
        target: The target class instance to process
        promote_all: Whether to remove all inner classes or just the enumerations
    """
    for inner in target.inner.copy():
        if promote_all or inner.is_enumeration:
            target.inner.remove(inner)

find_forward_refs(target) classmethod

Find all forward references for all inner classes.

Parameters:

Name Type Description Default
target Class

The target class instance to process

required

Yields:

Type Description
tuple[AttrType, Class]

A tuple of attr type and the parent class instance.

Source code in xsdata/codegen/handlers/unnest_inner_classes.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@classmethod
def find_forward_refs(cls, target: Class) -> Iterator[tuple[AttrType, Class]]:
    """Find all forward references for all inner classes.

    Args:
        target: The target class instance to process

    Yields:
        A tuple of attr type and the parent class instance.
    """
    for attr in target.attrs:
        for tp in attr.types:
            if tp.forward and not tp.native:
                yield tp, target

    for inner in target.inner:
        yield from cls.find_forward_refs(inner)

update_inner_class(target) classmethod

Prepare the nested class to be added as root.

Parameters:

Name Type Description Default
target Class

The target class

required
Source code in xsdata/codegen/handlers/unnest_inner_classes.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
def update_inner_class(cls, target: Class):
    """Prepare the nested class to be added as root.

    Args:
        target: The target class
    """
    assert target.parent is not None
    name_parts = [target.parent.name, target.name]
    new_qname = build_qname(target.target_namespace, "_".join(name_parts))

    target.qname = new_qname

    assert target.parent is not None

    target.parent.inner.remove(target)
    target.parent = None
    target.local_type = True

update_types(types, inner) classmethod

Search and replace forward references.

Return the number changes.

Parameters:

Name Type Description Default
types list[AttrType]

The types to search and replace

required
inner Class

The updated inner class

required
Source code in xsdata/codegen/handlers/unnest_inner_classes.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@classmethod
def update_types(cls, types: list[AttrType], inner: Class):
    """Search and replace forward references.

    Return the number changes.

    Args:
        types: The types to search and replace
        inner: The updated inner class
    """
    for tp in types:
        tp.qname = inner.qname
        tp.forward = False
        tp.reference = inner.ref