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`

      6
      7
      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

    class CreateWrapperFields(RelativeHandlerInterface):
        """Create wrapper fields.

        Args:
            container: The class container instance
        """

        def process(self, target: Class) -> None:
            """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, Attr | None]:
            """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: str | None) -> 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: str | None, b: str | None) -> bool:
                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`

    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

    def process(self, target: Class) -> None:
        """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`

    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58

    @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, Attr | None]` | A tuple of whether the source attr is inner and the source attr.

Source code in `xsdata/codegen/handlers/create_wrapper_fields.py`

    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

    def find_source_attr(self, parent: Class, attr: Attr) -> tuple[bool, Attr | None]:
        """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`

     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108

    @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` | `str | None` | 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`

    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

    @classmethod
    def validate_source(cls, source: Class, namespace: str | None) -> 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: str | None, b: str | None) -> bool:
            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)
        )