Skip to content

KeyError when using configure_tagged_union with existing field #483

Open
@bvitaz-ck

Description

@bvitaz-ck
  • cattrs version: 23.2.3
  • Python version: 3.11.5
  • Operating System: Windows 10

Description

I am trying to create a union type which uses an existing key to determine the type to use.

Looking at an example object, the "type" key word is used by the application to set up the correct type of device, and I want to use it to enable additional configuration settings:

    {
        "device": {
            "type": "special",
            "hostname": "hostname",
            "port": 1234
        }
    }

When I use configure_tagged_union(..., tag_name="type" ...), the "type" key seems to be consumed and is no longer available for the Device data class. This is causing a key error during the structure process.

What do I need to do to keep the "type" data available and use it for the tagged union?

What I Did

Example code:

import attrs
from cattrs import Converter
from cattrs.strategies import configure_tagged_union


@attrs.define
class SpecialDevice:
    hostname: str
    port: int
    type: str


@attrs.define
class StandardDevice:
    hostname: str
    type: str


Device = SpecialDevice | StandardDevice


@attrs.define
class Settings:
    device: Device


def tag_generator(t) -> str:
    return {
        SpecialDevice: "special",
        StandardDevice: "standard"
    }[t]


c = Converter()
configure_tagged_union(Device, c, tag_name="type", tag_generator=tag_generator, default=StandardDevice)
settings = c.structure(
    {
        "device": {
            "type": "special",
            "hostname": "hostname",
            "port": 1234
        }
    }, Settings)
print(settings)

Traceback:

  + Exception Group Traceback (most recent call last):
  |   File "AppData\Roaming\JetBrains\PyCharm2023.3\scratches\scratch.py", line 36, in <module>
  |     settings = c.structure(
  |                ^^^^^^^^^^^^
  |   File ".virtualenvs\gantry-control-1-NYP4DbyD\Lib\site-packages\cattrs\converters.py", line 332, in structure
  |     return self._structure_func.dispatch(cl)(obj, cl)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "<cattrs generated structure __main__.Settings>", line 9, in structure_Settings
  | cattrs.errors.ClassValidationError: While structuring Settings (1 sub-exception)
  +-+---------------- 1 ----------------
    | Exception Group Traceback (most recent call last):
    |   File "<cattrs generated structure __main__.Settings>", line 5, in structure_Settings
    |   File ".virtualenvs\gantry-control-1-NYP4DbyD\Lib\site-packages\cattrs\converters.py", line 632, in _structure_union
    |     return handler(obj, union)
    |            ^^^^^^^^^^^^^^^^^^^
    |   File ".virtualenvs\gantry-control-1-NYP4DbyD\Lib\site-packages\cattrs\strategies\_unions.py", line 106, in structure_tagged_union
    |     return _tag_to_hook[val.pop(_tag_name)](val)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File ".virtualenvs\gantry-control-1-NYP4DbyD\Lib\site-packages\cattrs\strategies\_unions.py", line 57, in structure_union_member
    |     return _h(val, _cl)
    |            ^^^^^^^^^^^^
    |   File "<cattrs generated structure __main__.SpecialDevice>", line 19, in structure_SpecialDevice
    | cattrs.errors.ClassValidationError: While structuring SpecialDevice (1 sub-exception)
    | Structuring class Settings @ attribute device
    +-+---------------- 1 ----------------
      | Traceback (most recent call last):
      |   File "<cattrs generated structure __main__.SpecialDevice>", line 15, in structure_SpecialDevice
      | KeyError: 'type'
      | Structuring class SpecialDevice @ attribute type
      +------------------------------------

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions