Skip to content

Commit 23f5a8f

Browse files
committed
Merge branch 'master' into prop-init-handling
2 parents 65d12f2 + c932e21 commit 23f5a8f

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
## Contributors
88

99
* Nick Schober
10+
* Zoltan Ivanfi

dataclass_csv/dataclass_reader.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from datetime import datetime
55
from distutils.util import strtobool
6+
from typing import Union
67

78
from .field_mapper import FieldMapper
89
from .exceptions import CsvValueError
@@ -149,7 +150,22 @@ def _process_row(self, row):
149150
values.append(None)
150151
continue
151152

152-
if field.type is datetime:
153+
field_type = field.type
154+
# Special handling for Optional (Union of a single real type and None)
155+
if (
156+
# The first part of the condition is for Python < 3.8
157+
type(field_type).__name__ == '_Union'
158+
# The second part of the condition is for Python >= 3.8
159+
or '__origin__' in field_type.__dict__
160+
and field_type.__origin__ is Union
161+
):
162+
real_types = [
163+
t for t in field_type.__args__ if t is not type(None)
164+
]
165+
if len(real_types) == 1:
166+
field_type = real_types[0]
167+
168+
if field_type is datetime:
153169
try:
154170
transformed_value = self._parse_date_value(field, value)
155171
except ValueError as ex:
@@ -160,7 +176,7 @@ def _process_row(self, row):
160176
values.append(transformed_value)
161177
continue
162178

163-
if field.type is bool:
179+
if field_type is bool:
164180
try:
165181
transformed_value = (
166182
value
@@ -176,7 +192,7 @@ def _process_row(self, row):
176192
continue
177193

178194
try:
179-
transformed_value = field.type(value)
195+
transformed_value = field_type(value)
180196
except ValueError:
181197
raise CsvValueError(
182198
(

tests/mocks.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from dataclass_csv import dateformat, accept_whitespaces
66

7+
from typing import Optional
8+
79

810
@dataclasses.dataclass
911
class User:
@@ -79,3 +81,9 @@ class UserWithInitFalseAndDefaultValue:
7981
firstname: str
8082
lastname: str
8183
age: int = dataclasses.field(init=False, default=0)
84+
85+
@dataclasses.dataclass
86+
class UserWithOptionalAge:
87+
name: str
88+
age: Optional[int]
89+

tests/test_dataclass_reader.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from .mocks import (
77
User,
8+
UserWithOptionalAge,
89
DataclassWithBooleanValue,
910
DataclassWithBooleanValueNoneDefault,
1011
UserWithInitFalse,
@@ -158,3 +159,11 @@ def test_try_to_access_not_initialized_prop_with_default_value(create_csv):
158159
items = list(reader)
159160
user = items[0]
160161
assert user.age == 0
162+
163+
164+
def test_reader_with_optional_types(create_csv):
165+
csv_file = create_csv({'name': 'User', 'age': 40})
166+
167+
with csv_file.open() as f:
168+
reader = DataclassReader(f, UserWithOptionalAge)
169+
list(reader)

0 commit comments

Comments
 (0)