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