3131import re
3232
3333try :
34- from typing import List , Optional , Any , Dict , Iterable , Sequence
34+ from typing import List , Optional , Any , Dict , Iterable , Sequence , Tuple
35+ from abc import ABC , abstractmethod
3536 import io
37+
38+ class SupportsStringCasting (ABC ): # pylint: disable=missing-class-docstring
39+ @abstractmethod
40+ def __str__ (self ) -> str : # pylint: disable=invalid-str-returned
41+ pass
42+
43+
3644except ImportError :
3745 pass
3846
3947
4048class reader : # pylint: disable=invalid-name
41- """Basic CSV reader class
49+ """Basic CSV reader class that behaves like CPython's ``csv.reader()``
4250
4351 :param csvfile: The open file to read from
44- :param delimiter: The CSV delimiter, default is comma (,)
45- :param quotechar: The CSV quote character for encapsulating special characters
52+ :type csvfile: io.TextIOWrapper
53+ :param str delimiter: (Optional) The CSV delimiter, default is comma (,)
54+ :param str quotechar: (Optional) The CSV quote character for encapsulating special characters
4655 including the delimiter, default is double quotation mark (")
4756 """
4857
@@ -97,11 +106,12 @@ def __next__(self) -> List[str]:
97106
98107
99108class writer : # pylint: disable=invalid-name
100- """Basic CSV writer class
109+ """Basic CSV writer class that behaves like CPython's ``csv.writer()``
101110
102111 :param csvfile: The open CSVfile to write to
103- :param delimiter: The CSV delimiter, default is comma (,)
104- :param quotechar: The CSV quote character for encapsulating special characters
112+ :type csvfile: io.TextIOWrapper
113+ :param str delimiter: (Optional) The CSV delimiter, default is comma (,)
114+ :param str quotechar: (Optional) The CSV quote character for encapsulating special characters
105115 including the delimiter, default is double quotation mark (")
106116 """
107117
@@ -114,10 +124,12 @@ def __init__(
114124 self .quotechar = quoterchar
115125 self .newlinechar = "\r \n "
116126
117- def writerow (self , seq : List [ str ]) -> None :
127+ def writerow (self , seq : Sequence [ SupportsStringCasting ]) -> None :
118128 """Write a row to the CSV file
119129
120- :param seq: The list of values to write
130+ :param seq: The list of values to write, which must all be str or be able to
131+ be cast to str
132+ :type seq: Sequence[SupportsStringCasting]
121133 """
122134
123135 str_seq = [str (entry ) for entry in seq ]
@@ -128,18 +140,19 @@ def writerow(self, seq: List[str]) -> None:
128140 parsed_str = (self .delimiter ).join (quoted_seq )
129141 self .file_iterator .write (parsed_str + self .newlinechar )
130142
131- def writerows (self , rows : iter ) -> None :
143+ def writerows (self , rows : Iterable [ Sequence [ SupportsStringCasting ]] ) -> None :
132144 """Write multiple rows to the CSV file
133145
134146 :param rows: An iterable item that yields multiple rows to write (e.g., list)
147+ :type rows: Iterable[Sequence[str]]
135148 """
136149 for row in rows :
137150 self .writerow (row )
138151
139152 def _apply_quotes (self , entry : str ) -> str :
140153 """Apply the quote character to entries as necessary
141154
142- :param entry: The entry to add the quote charcter to, if needed
155+ :param str entry: The entry to add the quote charcter to, if needed
143156 """
144157
145158 return (
@@ -155,18 +168,21 @@ class DictReader:
155168 it also accepts the delimiter and quotechar keywords
156169
157170 :param f: The open file to read from
158- :param fieldnames: The fieldnames for each of the columns, if none is given,
171+ :type f: io.TextIOWrapper
172+ :param fieldnames: (Optional) The fieldnames for each of the columns, if none is given,
159173 it will default to the whatever is in the first row of the CSV file
160- :param restkey: A key name for values that have no key (row is larger than
174+ :type fieldnames: Sequence[str]
175+ :param str restkey: (Optional) A key name for values that have no key (row is larger than
161176 the length of fieldnames), default is None
162- :param restval: A default value for keys that have no values (row is small
177+ :param restval: (Optional) A default value for keys that have no values (row is small
163178 than the length of fieldnames, default is None
179+ :type restval: Any
164180 """
165181
166182 def __init__ (
167183 self ,
168184 f : io .TextIOWrapper ,
169- fieldnames : Optional [List ] = None ,
185+ fieldnames : Optional [Sequence [ str ] ] = None ,
170186 restkey : Optional [str ] = None ,
171187 restval : Optional [Any ] = None ,
172188 ** kwargs
@@ -205,17 +221,20 @@ class DictWriter:
205221 delimiter and quotechar keywords
206222
207223 :param f: The open file to write to
224+ :type f: io.TextIOWrapper
208225 :param fieldnames: The fieldnames for each of the comlumns
209- :param restval: A default value for keys that have no values
210- :param extrasaction: The action to perform if a key is encountered when parsing the dict that is
211- not included in the fieldnames parameter, either "raise" or "ignore". Ignore raises a
212- ValueError, and "ignore" simply ignore that key/value pair. Default behavior is "raise"
226+ :type fieldnames: Sequence[str]
227+ :param str restval: A default value for keys that have no values
228+ :param str extrasaction: The action to perform if a key is encountered when parsing the dict
229+ that is not included in the fieldnames parameter, either "raise" or "ignore". Ignore
230+ raises a ValueError, and "ignore" simply ignore that key/value pair. Default behavior
231+ is "raise"
213232 """
214233
215234 def __init__ (
216235 self ,
217236 f : io .TextIOWrapper ,
218- fieldnames : List ,
237+ fieldnames : Sequence [ str ] ,
219238 restval : str = "" ,
220239 extrasaction : str = "raise" ,
221240 ** kwargs
@@ -233,7 +252,7 @@ def writeheader(self) -> None:
233252 """Writes the header row to the CSV file"""
234253 self .writerow (dict (zip (self .fieldnames , self .fieldnames )))
235254
236- def _dict_to_list (self , rowdict : Dict [str , Any ]) -> Sequence [Any ]:
255+ def _dict_to_tuple (self , rowdict : Dict [str , Any ]) -> Tuple [Any ]:
237256 if self .extrasaction == "raise" :
238257 wrong_fields = []
239258 for field in rowdict .keys ():
@@ -246,17 +265,19 @@ def _dict_to_list(self, rowdict: Dict[str, Any]) -> Sequence[Any]:
246265 )
247266 return (rowdict .get (key , self .restval ) for key in self .fieldnames )
248267
249- def writerow (self , rowdict : Dict [str , Any ]) -> None :
268+ def writerow (self , rowdict : Dict [str , SupportsStringCasting ]) -> None :
250269 """Writes a row to the CSV file
251270
252271 :param rowdict: The row to write as a dict, with keys of the DictWriter's
253272 fieldnames parameter
273+ :type rowdict: Dict[str, SupportsStringCasting]
254274 """
255- return self .writer .writerow (self ._dict_to_list (rowdict ))
275+ return self .writer .writerow (self ._dict_to_tuple (rowdict ))
256276
257- def writerows (self , rowdicts : Iterable [Any ]) -> None :
277+ def writerows (self , rowdicts : Iterable [Dict [ str , SupportsStringCasting ] ]) -> None :
258278 """Writes multiple rows to the CSV files
259279
260280 :param rowdicts: An iterable item that yields multiple rows to write
281+ :type rowdicts: Iterable[Dict[str, SupportsStringCasting]]
261282 """
262- return self .writer .writerows (map (self ._dict_to_list , rowdicts ))
283+ return self .writer .writerows (map (self ._dict_to_tuple , rowdicts ))
0 commit comments