Skip to content

Commit 7b69330

Browse files
committed
Fix handling of Enums in Literal types
1 parent 3ee4b40 commit 7b69330

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

src/cattr/converters.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,13 @@ def _structure_call(obj, cl):
405405

406406
@staticmethod
407407
def _structure_literal(val, type):
408-
if val not in type.__args__:
408+
vals = {
409+
(x.value if isinstance(x, Enum) else x): x for x in type.__args__
410+
}
411+
try:
412+
return vals[val]
413+
except KeyError:
409414
raise Exception(f"{val} not in literal {type}")
410-
return val
411415

412416
# Attrs classes.
413417

tests/test_structure_attrs.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Loading of attrs classes."""
2+
from enum import Enum
23
from ipaddress import IPv4Address, IPv6Address, ip_address
34
from typing import Union
45
from unittest.mock import Mock
@@ -164,6 +165,27 @@ class ClassWithLiteral:
164165
) == ClassWithLiteral(4)
165166

166167

168+
@pytest.mark.skipif(is_py37, reason="Not supported on 3.7")
169+
@pytest.mark.parametrize("converter_cls", [Converter, GenConverter])
170+
def test_structure_literal_enum(converter_cls):
171+
"""Structuring a class with a literal field works."""
172+
from typing import Literal
173+
174+
converter = converter_cls()
175+
176+
class Foo(Enum):
177+
FOO = 1
178+
BAR = 2
179+
180+
@define
181+
class ClassWithLiteral:
182+
literal_field: Literal[Foo.FOO] = Foo.FOO
183+
184+
assert converter.structure(
185+
{"literal_field": 1}, ClassWithLiteral
186+
) == ClassWithLiteral(Foo.FOO)
187+
188+
167189
@pytest.mark.skipif(is_py37, reason="Not supported on 3.7")
168190
@pytest.mark.parametrize("converter_cls", [Converter, GenConverter])
169191
def test_structure_literal_multiple(converter_cls):
@@ -172,9 +194,17 @@ def test_structure_literal_multiple(converter_cls):
172194

173195
converter = converter_cls()
174196

197+
class Foo(Enum):
198+
FOO = 7
199+
FOOFOO = 77
200+
201+
class Bar(int, Enum):
202+
BAR = 8
203+
BARBAR = 88
204+
175205
@define
176206
class ClassWithLiteral:
177-
literal_field: Literal[4, 5] = 4
207+
literal_field: Literal[4, 5, Foo.FOO, Bar.BARBAR] = 4
178208

179209
assert converter.structure(
180210
{"literal_field": 4}, ClassWithLiteral
@@ -183,6 +213,18 @@ class ClassWithLiteral:
183213
{"literal_field": 5}, ClassWithLiteral
184214
) == ClassWithLiteral(5)
185215

216+
cwl = converter.structure(
217+
{"literal_field": 7}, ClassWithLiteral
218+
)
219+
assert cwl ==ClassWithLiteral(Foo.FOO)
220+
assert isinstance(cwl.literal_field, Foo)
221+
222+
cwl = converter.structure(
223+
{"literal_field": 88}, ClassWithLiteral
224+
)
225+
assert cwl ==ClassWithLiteral(Bar.BARBAR)
226+
assert isinstance(cwl.literal_field, Bar)
227+
186228

187229
@pytest.mark.skipif(is_py37, reason="Not supported on 3.7")
188230
@pytest.mark.parametrize("converter_cls", [Converter, GenConverter])

0 commit comments

Comments
 (0)