gabledata/recap

`to_dict` breaks when a type is aliased and alias=True

Closed this issue · 2 comments

Adding this param to the test_to_dict test"

(
    StructType(
        fields=[
            UnionType(
                types=[
                    NullType(),
                    StringType(bytes_=50),
                    IntType(bits=32),
                ],
                default=None,
                alias="recap.build.TestUnion",
            ),
        ],
    ),
    {
        "type": "struct",
        "fields": [
            {
                "type": "union",
                "alias": "recap.build.TestUnion",
                "types": [
                    {"type": "string", "bytes": 50},
                    "int32",
                ],
                "optional": True,
            }
        ],
    },
    True,
    True,
),

Fails with:

tests/unit/test_types.py:855: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
recap/types.py:498: in to_dict
    type_dict["fields"] = [
recap/types.py:499: in <listcomp>
    to_dict(
recap/types.py:531: in to_dict
    type_dict = alias_dict(type_dict) if alias else type_dict
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

type_dict = {'alias': 'recap.build.TestUnion', 'default': None, 'doc': None, 'logical': None, ...}
registry = <recap.types.RecapTypeRegistry object at 0x1265a60b0>

    def alias_dict(
        type_dict: dict[str, Any],
        registry: RecapTypeRegistry | None = None,
    ) -> dict[str, Any]:
        """
        Replaces concrete type definitions with aliases when possible.
    
        ```
        {
            "type": "int",
            "bits": 32,
            "signed": true,
        }
        ```
    
        Would become `{"type": "int32"}`.
    
        :param type_dict: A type dictionary.
        :param registry: A RecapTypeRegistry containing aliases.
        :return: A type dictionary with aliases.
        """
    
        registry = registry or RecapTypeRegistry()
    
        # If there's a matching alias, replace the type_dict with it
>       for alias, recap_type in registry._type_registry.items():
E       RuntimeError: dictionary changed size during iteration

I'm pretty sure it's because the UnionType is being added to the RecapTypeRegistry in from_dict (via alias_dict) while iteration is occurring.

Another test that breaks for test_to_dict:

(
    StructType(
        fields=[
            StringType(
                name="primary_phone",
                alias="build.recap.Phone",
                bytes_=10,
            ),
            UnionType(
                name="secondary_phone",
                types=[
                    NullType(),
                    ProxyType(
                        alias="build.recap.Phone",
                        registry=RecapTypeRegistry(),
                    ),
                ],
                default=None,
            ),
        ],
    ),
    {
        "type": "struct",
        "fields": [
            {
                "type": "string",
                "bytes": 10,
                "name": "primary_phone",
                "alias": "build.recap.Phone",
            },
            {
                "type": "build.recap.Phone",
                "name": "secondary_phone",
                "optional": True,
            },
        ],
    },
    True,
    True,
),

Closed by #340