Skip to content

Commit 6f95a00

Browse files
authored
Raise an error when mapped column is not found (dfurtado#35)
Raise an error when mapped column is not found Improve the error message when a mapped column is not found in the CSV file. Added additional tests to reproduce two different scenarios, the first when a mapped column is not found and the second and a non-mapped column is not found.
1 parent 558ecd8 commit 6f95a00

File tree

4 files changed

+48
-35
lines changed

4 files changed

+48
-35
lines changed

dataclass_csv/__init__.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,4 @@
3838
from .exceptions import CsvValueError
3939

4040

41-
__all__ = [
42-
'DataclassReader',
43-
'dateformat',
44-
'accept_whitespaces',
45-
'CsvValueError',
46-
]
41+
__all__ = ['DataclassReader', 'dateformat', 'accept_whitespaces', 'CsvValueError']

dataclass_csv/dataclass_reader.py

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def __init__(
3232
self.optional_fields = self._get_optional_fields()
3333
self.field_mapping = {}
3434

35-
3635
self.reader = csv.DictReader(
3736
f, fieldnames, restkey, restval, dialect, *args, **kwds
3837
)
@@ -60,19 +59,19 @@ def _get_default_value(self, field):
6059
)
6160

6261
def _get_possible_keys(self, fieldname, row):
63-
possible_keys = list(
64-
filter(lambda x: x.strip() == fieldname, row.keys())
65-
)
62+
possible_keys = list(filter(lambda x: x.strip() == fieldname, row.keys()))
6663
if possible_keys:
6764
return possible_keys[0]
6865

6966
def _get_value(self, row, field):
67+
is_field_mapped = False
68+
7069
try:
71-
key = (
72-
field.name
73-
if field.name not in self.field_mapping.keys()
74-
else self.field_mapping.get(field.name)
75-
)
70+
if field.name in self.field_mapping.keys():
71+
is_field_mapped = True
72+
key = self.field_mapping.get(field.name)
73+
else:
74+
key = field.name
7675

7776
if key in row.keys():
7877
value = row[key]
@@ -85,14 +84,15 @@ def _get_value(self, row, field):
8584
if field.name in self.optional_fields:
8685
return self._get_default_value(field)
8786
else:
88-
raise KeyError(
89-
f'The value `{field.name}` is missing in the CSV file.'
90-
)
87+
keyerror_message = f'The value for the column `{field.name}` is missing in the CSV file.'
88+
if is_field_mapped:
89+
keyerror_message = f'The value for the mapped column `{key}` is missing in the CSV file'
90+
raise KeyError(keyerror_message)
9191
else:
9292
if not value and field.name in self.optional_fields:
9393
return self._get_default_value(field)
9494
elif not value and field.name not in self.optional_fields:
95-
raise ValueError((f'The field `{field.name}` is required.'))
95+
raise ValueError(f'The field `{field.name}` is required.')
9696
elif (
9797
value
9898
and field.type is str
@@ -144,9 +144,7 @@ def _process_row(self, row):
144144
try:
145145
value = self._get_value(row, field)
146146
except ValueError as ex:
147-
raise CsvValueError(
148-
ex, line_number=self.reader.line_num
149-
) from None
147+
raise CsvValueError(ex, line_number=self.reader.line_num) from None
150148

151149
if not value and field.default is None:
152150
values.append(None)
@@ -161,19 +159,15 @@ def _process_row(self, row):
161159
or '__origin__' in field_type.__dict__
162160
and field_type.__origin__ is Union
163161
):
164-
real_types = [
165-
t for t in field_type.__args__ if t is not type(None)
166-
]
162+
real_types = [t for t in field_type.__args__ if t is not type(None)]
167163
if len(real_types) == 1:
168164
field_type = real_types[0]
169165

170166
if field_type is datetime:
171167
try:
172168
transformed_value = self._parse_date_value(field, value)
173169
except ValueError as ex:
174-
raise CsvValueError(
175-
ex, line_number=self.reader.line_num
176-
) from None
170+
raise CsvValueError(ex, line_number=self.reader.line_num) from None
177171
else:
178172
values.append(transformed_value)
179173
continue
@@ -186,9 +180,7 @@ def _process_row(self, row):
186180
else strtobool(str(value).strip()) == 1
187181
)
188182
except ValueError as ex:
189-
raise CsvValueError(
190-
ex, line_number=self.reader.line_num
191-
) from None
183+
raise CsvValueError(ex, line_number=self.reader.line_num) from None
192184
else:
193185
values.append(transformed_value)
194186
continue
@@ -219,7 +211,5 @@ def map(self, csv_fieldname):
219211
:param csv_fieldname: The name of the CSV field
220212
"""
221213
return FieldMapper(
222-
lambda property_name: self._add_to_mapping(
223-
property_name, csv_fieldname
224-
)
214+
lambda property_name: self._add_to_mapping(property_name, csv_fieldname)
225215
)

tests/mocks.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,10 @@ def __init__(self, val):
108108
class UserWithSSN:
109109
name: str
110110
ssn: SSN
111+
112+
113+
@dataclasses.dataclass
114+
class UserWithEmail:
115+
name: str
116+
email: str
117+

tests/test_dataclass_reader.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
UserWithDefaultDatetimeField,
1515
UserWithSSN,
1616
SSN,
17+
UserWithEmail,
1718
)
1819

1920

@@ -173,7 +174,7 @@ def test_reader_with_optional_types(create_csv):
173174
list(reader)
174175

175176
def test_reader_with_datetime_default_value(create_csv):
176-
csv_file = create_csv({'name': 'User', 'bithday': ''})
177+
csv_file = create_csv({'name': 'User', 'birthday': ''})
177178

178179
with csv_file.open() as f:
179180
reader = DataclassReader(f, UserWithDefaultDatetimeField)
@@ -198,3 +199,23 @@ def test_should_parse_user_defined_types(create_csv):
198199

199200
assert isinstance(items[1].ssn, SSN)
200201
assert items[1].ssn.val == '123-45-6789'
202+
203+
204+
def test_raise_error_when_mapped_column_not_found(create_csv):
205+
csv_file = create_csv({'name': 'User1', 'e-mail': '[email protected]'})
206+
207+
with csv_file.open() as f:
208+
with pytest.raises(KeyError, match='The value for the mapped column `e_mail` is missing in the CSV file'):
209+
reader = DataclassReader(f, UserWithEmail)
210+
reader.map('e_mail').to('email')
211+
list(reader)
212+
213+
214+
def test_raise_error_when_field_not_found(create_csv):
215+
csv_file = create_csv({'name': 'User1', 'e-mail': '[email protected]'})
216+
217+
with csv_file.open() as f:
218+
with pytest.raises(KeyError, match='The value for the column `email` is missing in the CSV file.'):
219+
reader = DataclassReader(f, UserWithEmail)
220+
list(reader)
221+

0 commit comments

Comments
 (0)