@@ -24,6 +24,8 @@ def strtobool(value: str) -> bool:
2424 return value .lower () in trueValues
2525
2626
27+ T = TypeVar ("T" )
28+
2729def _verify_duplicate_header_items (header ):
2830 if header is not None and len (header ) == 0 :
2931 return
@@ -42,6 +44,17 @@ def _verify_duplicate_header_items(header):
4244 )
4345
4446
47+ def strtobool (value : str ) -> bool :
48+ trueValues = ["true" , "yes" , "t" , "y" , "on" , "1" ]
49+
50+ validValues = ["false" , "no" , "f" , "n" , "off" , "0" , * trueValues ]
51+
52+ if value .lower () not in validValues :
53+ raise ValueError (f"invalid boolean value { value } " )
54+
55+ return value .lower () in trueValues
56+
57+
4558def is_union_type (t ):
4659 if hasattr (t , "__origin__" ) and t .__origin__ is Union :
4760 return True
@@ -60,7 +73,7 @@ class DataclassReader(Generic[T]):
6073 def __init__ (
6174 self ,
6275 f : Any ,
63- cls : Type [T ],
76+ klass : Type [T ],
6477 fieldnames : Optional [Sequence [str ]] = None ,
6578 restkey : Optional [str ] = None ,
6679 restval : Optional [Any ] = None ,
@@ -72,10 +85,10 @@ def __init__(
7285 if not f :
7386 raise ValueError ("The f argument is required." )
7487
75- if cls is None or not dataclasses .is_dataclass (cls ):
76- raise ValueError ("cls argument needs to be a dataclass." )
88+ if klass is None or not dataclasses .is_dataclass (klass ):
89+ raise ValueError ("klass argument needs to be a dataclass." )
7790
78- self ._cls = cls
91+ self ._cls = klass
7992 self ._optional_fields = self ._get_optional_fields ()
8093 self ._field_mapping : Dict [str , Dict [str , Any ]] = {}
8194
@@ -88,7 +101,7 @@ def __init__(
88101 if validate_header :
89102 _verify_duplicate_header_items (self ._reader .fieldnames )
90103
91- self .type_hints = typing .get_type_hints (cls )
104+ self .type_hints = typing .get_type_hints (klass )
92105
93106 def _get_optional_fields (self ):
94107 return [
@@ -120,53 +133,52 @@ def _get_possible_keys(self, fieldname, row):
120133 def _get_value (self , row , field ):
121134 is_field_mapped = False
122135
123- try :
124- if field .name in self ._field_mapping .keys ():
125- is_field_mapped = True
126- key = self ._field_mapping .get (field .name )
127- else :
128- key = field .name
136+ if field .name in self ._field_mapping .keys ():
137+ is_field_mapped = True
138+ key = self ._field_mapping .get (field .name )
139+ else :
140+ key = field .name
129141
130- if key in row .keys ():
131- value = row [key ]
132- else :
142+ if key in row .keys ():
143+ value = row [key ]
144+ else :
145+ try :
133146 possible_key = self ._get_possible_keys (field .name , row )
134147 key = possible_key if possible_key else key
135148 value = row [key ]
136-
137- except KeyError :
138- if field .name in self ._optional_fields :
139- return self ._get_default_value (field )
140- else :
141- keyerror_message = f"The value for the column `{ field .name } `"
142- if is_field_mapped :
143- keyerror_message = f"The value for the mapped column `{ key } `"
144- raise KeyError (f"{ keyerror_message } is missing in the CSV file" )
145- else :
146- if not value and field .name in self ._optional_fields :
147- return self ._get_default_value (field )
148- elif not value and field .name not in self ._optional_fields :
149- raise ValueError (f"The field `{ field .name } ` is required." )
150- elif (
151- value
152- and field .type is str
153- and not len (value .strip ())
154- and not self ._get_metadata_option (field , "accept_whitespaces" )
155- ):
156- raise ValueError (
157- (
158- f"It seems like the value of `{ field .name } ` contains "
159- "only white spaces. To allow white spaces to all "
160- "string fields, use the @accept_whitespaces "
161- "decorator. "
162- "To allow white spaces specifically for the field "
163- f"`{ field .name } ` change its definition to: "
164- f"`{ field .name } : str = field(metadata="
165- "{'accept_whitespaces': True})`."
166- )
149+ except KeyError :
150+ if field .name in self ._optional_fields :
151+ return self ._get_default_value (field )
152+ else :
153+ keyerror_message = f"The value for the column `{ field .name } `"
154+ if is_field_mapped :
155+ keyerror_message = f"The value for the mapped column `{ key } `"
156+ raise KeyError (f"{ keyerror_message } is missing in the CSV file" )
157+
158+ if not value and field .name in self ._optional_fields :
159+ return self ._get_default_value (field )
160+ elif not value and field .name not in self ._optional_fields :
161+ raise ValueError (f"The field `{ field .name } ` is required." )
162+ elif (
163+ value
164+ and field .type is str
165+ and not len (value .strip ())
166+ and not self ._get_metadata_option (field , "accept_whitespaces" )
167+ ):
168+ raise ValueError (
169+ (
170+ f"It seems like the value of `{ field .name } ` contains "
171+ "only white spaces. To allow white spaces to all "
172+ "string fields, use the @accept_whitespaces "
173+ "decorator. "
174+ "To allow white spaces specifically for the field "
175+ f"`{ field .name } ` change its definition to: "
176+ f"`{ field .name } : str = field(metadata="
177+ "{'accept_whitespaces': True})`."
167178 )
168- else :
169- return value
179+ )
180+ else :
181+ return value
170182
171183 def _parse_date_value (self , field , date_value , field_type ):
172184 dateformat = self ._get_metadata_option (field , "dateformat" )
@@ -231,7 +243,7 @@ def _process_row(self, row) -> T:
231243 transformed_value = (
232244 value
233245 if isinstance (value , bool )
234- else strtobool (str (value ).strip ()) == 1
246+ else strtobool (str (value ).strip ())
235247 )
236248 except ValueError as ex :
237249 raise CsvValueError (ex , line_number = self ._reader .line_num ) from None
0 commit comments