Skip to content

# calculate_attribute_paths

## `xsdata.codegen.handlers.calculate_attribute_paths`

### `CalculateAttributePaths`

Bases: `HandlerInterface`

Calculate min/max occurs and sequence/choice/group from the schema path.

Source code in `xsdata/codegen/handlers/calculate_attribute_paths.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

    class CalculateAttributePaths(HandlerInterface):
        """Calculate min/max occurs and sequence/choice/group from the schema path."""

        __slots__ = ()

        @classmethod
        def process(cls, target: Class):
            """Calculating the class attrs restrictions by their schema path.

            For each attr calculate the min/max occurs and set the
            sequence/choice/group reference id. Ignore attrs derived
            from xs:attribute and xs:enumeration as these are not affected
            by the parent element.

            Args:
                target: The target class instance
            """
            for attr in target.attrs:
                if (
                    attr.restrictions.path
                    and not attr.is_attribute
                    and not attr.is_enumeration
                ):
                    cls.process_attr_path(attr)

        @classmethod
        def process_attr_path(cls, attr: Attr):
            """Entrypoint for processing a class attr.

            Example path:
                ("s", 1, 1, 1), ("s", 2, 1, 2), ("c", 3, 0, 10)  ->
                sequence:1 with min=1 and max_occurs=1
                sequence:2 with min=1 and max_occurs=2
                choice:3 with min=0 and max_occurs=10

            Steps:
                - Every attr starts with a min/max occurs equal to one.
                - For every parent container multiply the min/max occurs
                - Set the sequence/choice/group reference ids as you go along
                - Elements within a choice with minOccurs <= 1 are effectively
                  optional since only one branch of the choice is selected

            Args:
                attr: The attr of the class to check and process
            """
            min_occurs = 1
            max_occurs = 1
            choice_min_occurs = None
            for path in attr.restrictions.path:
                name, index, mi, ma = path

                if name == SEQUENCE:
                    if not attr.restrictions.sequence:
                        attr.restrictions.sequence = index
                elif name == CHOICE:
                    if not attr.restrictions.choice:
                        attr.restrictions.choice = index
                    # Track the minimum minOccurs of any choice in the path
                    if choice_min_occurs is None or mi < choice_min_occurs:
                        choice_min_occurs = mi
                elif name == GROUP:
                    attr.restrictions.group = index
                else:
                    pass

                min_occurs *= mi
                max_occurs *= ma

            assert attr.restrictions.min_occurs is not None
            assert attr.restrictions.max_occurs is not None

            attr.restrictions.min_occurs *= min_occurs
            attr.restrictions.max_occurs *= max_occurs

            # Elements within a choice with minOccurs <= 1 are effectively optional
            # because only one branch of the choice is ever selected at runtime
            if choice_min_occurs is not None and choice_min_occurs <= 1:
                attr.restrictions.min_occurs = 0

#### `process(target)` `classmethod`

Calculating the class attrs restrictions by their schema path.

For each attr calculate the min/max occurs and set the sequence/choice/group reference id. Ignore attrs derived from xs:attribute and xs:enumeration as these are not affected by the parent element.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`target` | `Class` | The target class instance | required

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

    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    @classmethod
    def process(cls, target: Class):
        """Calculating the class attrs restrictions by their schema path.

        For each attr calculate the min/max occurs and set the
        sequence/choice/group reference id. Ignore attrs derived
        from xs:attribute and xs:enumeration as these are not affected
        by the parent element.

        Args:
            target: The target class instance
        """
        for attr in target.attrs:
            if (
                attr.restrictions.path
                and not attr.is_attribute
                and not attr.is_enumeration
            ):
                cls.process_attr_path(attr)

#### `process_attr_path(attr)` `classmethod`

Entrypoint for processing a class attr.

Example path

("s", 1, 1, 1), ("s", 2, 1, 2), ("c", 3, 0, 10) -> sequence:1 with min=1 and max_occurs=1 sequence:2 with min=1 and max_occurs=2 choice:3 with min=0 and max_occurs=10

Steps

- Every attr starts with a min/max occurs equal to one.
- For every parent container multiply the min/max occurs
- Set the sequence/choice/group reference ids as you go along
- Elements within a choice with minOccurs <= 1 are effectively optional since only one branch of the choice is selected

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`attr` | `Attr` | The attr of the class to check and process | required

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

    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

    @classmethod
    def process_attr_path(cls, attr: Attr):
        """Entrypoint for processing a class attr.

        Example path:
            ("s", 1, 1, 1), ("s", 2, 1, 2), ("c", 3, 0, 10)  ->
            sequence:1 with min=1 and max_occurs=1
            sequence:2 with min=1 and max_occurs=2
            choice:3 with min=0 and max_occurs=10

        Steps:
            - Every attr starts with a min/max occurs equal to one.
            - For every parent container multiply the min/max occurs
            - Set the sequence/choice/group reference ids as you go along
            - Elements within a choice with minOccurs <= 1 are effectively
              optional since only one branch of the choice is selected

        Args:
            attr: The attr of the class to check and process
        """
        min_occurs = 1
        max_occurs = 1
        choice_min_occurs = None
        for path in attr.restrictions.path:
            name, index, mi, ma = path

            if name == SEQUENCE:
                if not attr.restrictions.sequence:
                    attr.restrictions.sequence = index
            elif name == CHOICE:
                if not attr.restrictions.choice:
                    attr.restrictions.choice = index
                # Track the minimum minOccurs of any choice in the path
                if choice_min_occurs is None or mi < choice_min_occurs:
                    choice_min_occurs = mi
            elif name == GROUP:
                attr.restrictions.group = index
            else:
                pass

            min_occurs *= mi
            max_occurs *= ma

        assert attr.restrictions.min_occurs is not None
        assert attr.restrictions.max_occurs is not None

        attr.restrictions.min_occurs *= min_occurs
        attr.restrictions.max_occurs *= max_occurs

        # Elements within a choice with minOccurs <= 1 are effectively optional
        # because only one branch of the choice is ever selected at runtime
        if choice_min_occurs is not None and choice_min_occurs <= 1:
            attr.restrictions.min_occurs = 0