python-attrs / python-attrs/cattrs
KeyError when using configure_tagged_union with existing field
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 159
- Avg merge
- 12h 21m
- Merged PRs (30d)
- 6
Description
- 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
+------------------------------------
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with cattrs/strategies/_unions.py, especially configure_tagged_union and structure_tagged_union, then reproduce the provided Settings example. Check how the tag value is removed before the selected class is structured. Done means the union still dispatches on "type" while the selected device class can receive that field, with a regression test covering the example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100