blob: 0349e0bd1162c4bd6bc7ea3a387e7fbee2e7d873 [file] [log] [blame]
Skip Montanaro04ae7052003-04-24 20:21:31 +00001
2"""
3csv.py - read/write/investigate CSV files
4"""
5
6import re
7from _csv import Error, __version__, writer, reader, register_dialect, \
8 unregister_dialect, get_dialect, list_dialects, \
Andrew McNamara31d88962005-01-12 03:45:10 +00009 field_size_limit, \
Skip Montanaro04ae7052003-04-24 20:21:31 +000010 QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \
11 __doc__
Andrew McNamara7130ff52005-01-11 02:22:47 +000012from _csv import Dialect as _Dialect
Skip Montanaro04ae7052003-04-24 20:21:31 +000013
Raymond Hettinger43ca4522016-08-30 12:35:50 -070014from collections import OrderedDict
Guido van Rossum68937b42007-05-18 00:51:22 +000015from io import StringIO
Skip Montanaro1448d472003-04-25 14:47:16 +000016
Martin Panter19e69c52015-11-14 12:46:42 +000017__all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE",
18 "Error", "Dialect", "__doc__", "excel", "excel_tab",
19 "field_size_limit", "reader", "writer",
20 "register_dialect", "get_dialect", "list_dialects", "Sniffer",
21 "unregister_dialect", "__version__", "DictReader", "DictWriter",
22 "unix_dialect"]
Skip Montanaro04ae7052003-04-24 20:21:31 +000023
24class Dialect:
Georg Brandl7424dd32010-10-27 07:27:06 +000025 """Describe a CSV dialect.
Skip Montanarof26285c2005-01-05 06:54:58 +000026
27 This must be subclassed (see csv.excel). Valid attributes are:
28 delimiter, quotechar, escapechar, doublequote, skipinitialspace,
29 lineterminator, quoting.
30
31 """
Skip Montanaro04ae7052003-04-24 20:21:31 +000032 _name = ""
33 _valid = False
34 # placeholders
35 delimiter = None
36 quotechar = None
37 escapechar = None
38 doublequote = None
39 skipinitialspace = None
40 lineterminator = None
41 quoting = None
42
43 def __init__(self):
44 if self.__class__ != Dialect:
45 self._valid = True
Andrew McNamara7130ff52005-01-11 02:22:47 +000046 self._validate()
Skip Montanaro04ae7052003-04-24 20:21:31 +000047
48 def _validate(self):
Andrew McNamara7130ff52005-01-11 02:22:47 +000049 try:
50 _Dialect(self)
Guido van Rossumb940e112007-01-10 16:19:56 +000051 except TypeError as e:
Andrew McNamara7130ff52005-01-11 02:22:47 +000052 # We do this for compatibility with py2.3
53 raise Error(str(e))
Skip Montanaro04ae7052003-04-24 20:21:31 +000054
55class excel(Dialect):
Skip Montanarof26285c2005-01-05 06:54:58 +000056 """Describe the usual properties of Excel-generated CSV files."""
Skip Montanaro04ae7052003-04-24 20:21:31 +000057 delimiter = ','
58 quotechar = '"'
59 doublequote = True
60 skipinitialspace = False
61 lineterminator = '\r\n'
62 quoting = QUOTE_MINIMAL
63register_dialect("excel", excel)
64
65class excel_tab(excel):
Skip Montanarof26285c2005-01-05 06:54:58 +000066 """Describe the usual properties of Excel-generated TAB-delimited files."""
Skip Montanaro04ae7052003-04-24 20:21:31 +000067 delimiter = '\t'
68register_dialect("excel-tab", excel_tab)
69
Georg Brandl7424dd32010-10-27 07:27:06 +000070class unix_dialect(Dialect):
71 """Describe the usual properties of Unix-generated CSV files."""
72 delimiter = ','
73 quotechar = '"'
74 doublequote = True
75 skipinitialspace = False
76 lineterminator = '\n'
77 quoting = QUOTE_ALL
78register_dialect("unix", unix_dialect)
79
Skip Montanaro04ae7052003-04-24 20:21:31 +000080
81class DictReader:
Skip Montanarodffeed32003-10-03 14:03:01 +000082 def __init__(self, f, fieldnames=None, restkey=None, restval=None,
Skip Montanaro3f7a9482003-09-06 19:52:12 +000083 dialect="excel", *args, **kwds):
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +000084 self._fieldnames = fieldnames # list of keys for the dict
Skip Montanaro04ae7052003-04-24 20:21:31 +000085 self.restkey = restkey # key to catch long rows
86 self.restval = restval # default value for short rows
Skip Montanaro3f7a9482003-09-06 19:52:12 +000087 self.reader = reader(f, dialect, *args, **kwds)
Christian Heimes4fbc72b2008-03-22 00:47:35 +000088 self.dialect = dialect
89 self.line_num = 0
Skip Montanaro04ae7052003-04-24 20:21:31 +000090
91 def __iter__(self):
92 return self
93
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +000094 @property
95 def fieldnames(self):
96 if self._fieldnames is None:
97 try:
98 self._fieldnames = next(self.reader)
99 except StopIteration:
100 pass
101 self.line_num = self.reader.line_num
102 return self._fieldnames
103
104 @fieldnames.setter
105 def fieldnames(self, value):
106 self._fieldnames = value
107
Georg Brandla18af4e2007-04-21 15:47:16 +0000108 def __next__(self):
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +0000109 if self.line_num == 0:
110 # Used only for its side effect.
111 self.fieldnames
Georg Brandla18af4e2007-04-21 15:47:16 +0000112 row = next(self.reader)
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000113 self.line_num = self.reader.line_num
Skip Montanarodffeed32003-10-03 14:03:01 +0000114
Skip Montanaro04ae7052003-04-24 20:21:31 +0000115 # unlike the basic reader, we prefer not to return blanks,
116 # because we will typically wind up with a dict full of None
117 # values
118 while row == []:
Georg Brandla18af4e2007-04-21 15:47:16 +0000119 row = next(self.reader)
Raymond Hettinger43ca4522016-08-30 12:35:50 -0700120 d = OrderedDict(zip(self.fieldnames, row))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000121 lf = len(self.fieldnames)
122 lr = len(row)
123 if lf < lr:
124 d[self.restkey] = row[lf:]
125 elif lf > lr:
126 for key in self.fieldnames[lr:]:
127 d[key] = self.restval
128 return d
129
130
131class DictWriter:
132 def __init__(self, f, fieldnames, restval="", extrasaction="raise",
Skip Montanaro3f7a9482003-09-06 19:52:12 +0000133 dialect="excel", *args, **kwds):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000134 self.fieldnames = fieldnames # list of keys for the dict
135 self.restval = restval # for writing short dicts
136 if extrasaction.lower() not in ("raise", "ignore"):
Collin Winterce36ad82007-08-30 01:19:48 +0000137 raise ValueError("extrasaction (%s) must be 'raise' or 'ignore'"
138 % extrasaction)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000139 self.extrasaction = extrasaction
Skip Montanaro3f7a9482003-09-06 19:52:12 +0000140 self.writer = writer(f, dialect, *args, **kwds)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000141
R. David Murraybe0698b2010-02-23 22:57:58 +0000142 def writeheader(self):
143 header = dict(zip(self.fieldnames, self.fieldnames))
144 self.writerow(header)
145
Skip Montanaro04ae7052003-04-24 20:21:31 +0000146 def _dict_to_list(self, rowdict):
147 if self.extrasaction == "raise":
INADA Naoki0a421a22016-10-21 19:47:57 +0900148 wrong_fields = rowdict.keys() - self.fieldnames
Guido van Rossumd8faa362007-04-27 19:54:29 +0000149 if wrong_fields:
Collin Winterce36ad82007-08-30 01:19:48 +0000150 raise ValueError("dict contains fields not in fieldnames: "
R David Murrayfb099c92013-11-19 13:16:20 -0500151 + ", ".join([repr(x) for x in wrong_fields]))
Serhiy Storchaka7901b482015-03-30 09:09:54 +0300152 return (rowdict.get(key, self.restval) for key in self.fieldnames)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000153
154 def writerow(self, rowdict):
155 return self.writer.writerow(self._dict_to_list(rowdict))
156
157 def writerows(self, rowdicts):
Serhiy Storchaka7901b482015-03-30 09:09:54 +0300158 return self.writer.writerows(map(self._dict_to_list, rowdicts))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000159
Raymond Hettinger39a55922003-06-12 03:01:55 +0000160# Guard Sniffer's type checking against builds that exclude complex()
161try:
162 complex
163except NameError:
164 complex = float
Skip Montanaro04ae7052003-04-24 20:21:31 +0000165
166class Sniffer:
167 '''
168 "Sniffs" the format of a CSV file (i.e. delimiter, quotechar)
Skip Montanaro1448d472003-04-25 14:47:16 +0000169 Returns a Dialect object.
Skip Montanaro04ae7052003-04-24 20:21:31 +0000170 '''
Skip Montanaro1448d472003-04-25 14:47:16 +0000171 def __init__(self):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000172 # in case there is more than one possible delimiter
173 self.preferred = [',', '\t', ';', ' ', ':']
174
Skip Montanaro04ae7052003-04-24 20:21:31 +0000175
Skip Montanaro77892372003-05-19 15:33:36 +0000176 def sniff(self, sample, delimiters=None):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000177 """
Skip Montanaro1448d472003-04-25 14:47:16 +0000178 Returns a dialect (or None) corresponding to the sample
Skip Montanaro04ae7052003-04-24 20:21:31 +0000179 """
Skip Montanaro04ae7052003-04-24 20:21:31 +0000180
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000181 quotechar, doublequote, delimiter, skipinitialspace = \
Skip Montanaro77892372003-05-19 15:33:36 +0000182 self._guess_quote_and_delimiter(sample, delimiters)
Skip Montanaro39b29be2005-12-30 05:09:48 +0000183 if not delimiter:
Skip Montanaro77892372003-05-19 15:33:36 +0000184 delimiter, skipinitialspace = self._guess_delimiter(sample,
185 delimiters)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000186
Skip Montanaro39b29be2005-12-30 05:09:48 +0000187 if not delimiter:
Collin Winterce36ad82007-08-30 01:19:48 +0000188 raise Error("Could not determine delimiter")
Skip Montanaro39b29be2005-12-30 05:09:48 +0000189
Skip Montanaro1448d472003-04-25 14:47:16 +0000190 class dialect(Dialect):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000191 _name = "sniffed"
192 lineterminator = '\r\n'
Fred Drake7c852f32003-04-25 14:27:00 +0000193 quoting = QUOTE_MINIMAL
Skip Montanaro04ae7052003-04-24 20:21:31 +0000194 # escapechar = ''
Skip Montanaro04ae7052003-04-24 20:21:31 +0000195
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000196 dialect.doublequote = doublequote
Skip Montanaro1448d472003-04-25 14:47:16 +0000197 dialect.delimiter = delimiter
198 # _csv.reader won't accept a quotechar of ''
199 dialect.quotechar = quotechar or '"'
200 dialect.skipinitialspace = skipinitialspace
201
202 return dialect
Skip Montanaro04ae7052003-04-24 20:21:31 +0000203
204
Skip Montanaro77892372003-05-19 15:33:36 +0000205 def _guess_quote_and_delimiter(self, data, delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000206 """
207 Looks for text enclosed between two identical quotes
208 (the probable quotechar) which are preceded and followed
209 by the same character (the probable delimiter).
210 For example:
211 ,'some text',
212 The quote with the most wins, same with the delimiter.
213 If there is no quotechar the delimiter can't be determined
214 this way.
215 """
216
217 matches = []
R David Murray44b548d2016-09-08 13:59:53 -0400218 for restr in (r'(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*?(?P=quote)(?P=delim)', # ,".*?",
219 r'(?:^|\n)(?P<quote>["\']).*?(?P=quote)(?P<delim>[^\w\n"\'])(?P<space> ?)', # ".*?",
220 r'(?P<delim>>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*?(?P=quote)(?:$|\n)', # ,".*?"
221 r'(?:^|\n)(?P<quote>["\']).*?(?P=quote)(?:$|\n)'): # ".*?" (no delim, no space)
Fred Drake6f7b2132003-09-02 16:01:07 +0000222 regexp = re.compile(restr, re.DOTALL | re.MULTILINE)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000223 matches = regexp.findall(data)
224 if matches:
225 break
226
227 if not matches:
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000228 # (quotechar, doublequote, delimiter, skipinitialspace)
229 return ('', False, None, 0)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000230 quotes = {}
231 delims = {}
232 spaces = 0
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300233 groupindex = regexp.groupindex
Skip Montanaro04ae7052003-04-24 20:21:31 +0000234 for m in matches:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300235 n = groupindex['quote'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000236 key = m[n]
237 if key:
238 quotes[key] = quotes.get(key, 0) + 1
239 try:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300240 n = groupindex['delim'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000241 key = m[n]
242 except KeyError:
243 continue
Skip Montanaro77892372003-05-19 15:33:36 +0000244 if key and (delimiters is None or key in delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000245 delims[key] = delims.get(key, 0) + 1
246 try:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300247 n = groupindex['space'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000248 except KeyError:
249 continue
250 if m[n]:
251 spaces += 1
252
Guido van Rossum89da5d72006-08-22 00:21:25 +0000253 quotechar = max(quotes, key=quotes.get)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000254
255 if delims:
Guido van Rossum89da5d72006-08-22 00:21:25 +0000256 delim = max(delims, key=delims.get)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000257 skipinitialspace = delims[delim] == spaces
258 if delim == '\n': # most likely a file with a single column
259 delim = ''
260 else:
261 # there is *no* delimiter, it's a single column of quoted data
262 delim = ''
263 skipinitialspace = 0
264
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000265 # if we see an extra quote between delimiters, we've got a
266 # double quoted format
R David Murray925a3222013-06-29 18:40:53 -0400267 dq_regexp = re.compile(
268 r"((%(delim)s)|^)\W*%(quote)s[^%(delim)s\n]*%(quote)s[^%(delim)s\n]*%(quote)s\W*((%(delim)s)|$)" % \
269 {'delim':re.escape(delim), 'quote':quotechar}, re.MULTILINE)
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000270
271
272
273 if dq_regexp.search(data):
274 doublequote = True
275 else:
276 doublequote = False
277
278 return (quotechar, doublequote, delim, skipinitialspace)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000279
280
Skip Montanaro77892372003-05-19 15:33:36 +0000281 def _guess_delimiter(self, data, delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000282 """
283 The delimiter /should/ occur the same number of times on
284 each row. However, due to malformed data, it may not. We don't want
285 an all or nothing approach, so we allow for small variations in this
286 number.
287 1) build a table of the frequency of each character on every line.
Ezio Melotti13925002011-03-16 11:05:33 +0200288 2) build a table of frequencies of this frequency (meta-frequency?),
Skip Montanaro04ae7052003-04-24 20:21:31 +0000289 e.g. 'x occurred 5 times in 10 rows, 6 times in 1000 rows,
290 7 times in 2 rows'
291 3) use the mode of the meta-frequency to determine the /expected/
292 frequency for that character
293 4) find out how often the character actually meets that goal
294 5) the character that best meets its goal is the delimiter
295 For performance reasons, the data is evaluated in chunks, so it can
296 try and evaluate the smallest portion of the data possible, evaluating
297 additional chunks as necessary.
298 """
299
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000300 data = list(filter(None, data.split('\n')))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000301
302 ascii = [chr(c) for c in range(127)] # 7-bit ASCII
303
304 # build frequency tables
305 chunkLength = min(10, len(data))
306 iteration = 0
307 charFrequency = {}
308 modes = {}
309 delims = {}
310 start, end = 0, min(chunkLength, len(data))
311 while start < len(data):
312 iteration += 1
313 for line in data[start:end]:
314 for char in ascii:
Skip Montanaro1448d472003-04-25 14:47:16 +0000315 metaFrequency = charFrequency.get(char, {})
Skip Montanaro04ae7052003-04-24 20:21:31 +0000316 # must count even if frequency is 0
Skip Montanaro91bb70c2005-12-28 15:37:25 +0000317 freq = line.count(char)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000318 # value is the mode
Skip Montanaro1448d472003-04-25 14:47:16 +0000319 metaFrequency[freq] = metaFrequency.get(freq, 0) + 1
320 charFrequency[char] = metaFrequency
Skip Montanaro04ae7052003-04-24 20:21:31 +0000321
322 for char in charFrequency.keys():
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000323 items = list(charFrequency[char].items())
Skip Montanaro04ae7052003-04-24 20:21:31 +0000324 if len(items) == 1 and items[0][0] == 0:
325 continue
326 # get the mode of the frequencies
327 if len(items) > 1:
Guido van Rossum89da5d72006-08-22 00:21:25 +0000328 modes[char] = max(items, key=lambda x: x[1])
Skip Montanaro04ae7052003-04-24 20:21:31 +0000329 # adjust the mode - subtract the sum of all
330 # other frequencies
331 items.remove(modes[char])
332 modes[char] = (modes[char][0], modes[char][1]
Guido van Rossum89da5d72006-08-22 00:21:25 +0000333 - sum(item[1] for item in items))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000334 else:
335 modes[char] = items[0]
336
337 # build a list of possible delimiters
338 modeList = modes.items()
339 total = float(chunkLength * iteration)
340 # (rows of consistent data) / (number of rows) = 100%
341 consistency = 1.0
342 # minimum consistency threshold
343 threshold = 0.9
344 while len(delims) == 0 and consistency >= threshold:
345 for k, v in modeList:
346 if v[0] > 0 and v[1] > 0:
Skip Montanaro77892372003-05-19 15:33:36 +0000347 if ((v[1]/total) >= consistency and
348 (delimiters is None or k in delimiters)):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000349 delims[k] = v
350 consistency -= 0.01
351
352 if len(delims) == 1:
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000353 delim = list(delims.keys())[0]
Skip Montanaro04ae7052003-04-24 20:21:31 +0000354 skipinitialspace = (data[0].count(delim) ==
355 data[0].count("%c " % delim))
356 return (delim, skipinitialspace)
357
358 # analyze another chunkLength lines
359 start = end
360 end += chunkLength
361
362 if not delims:
363 return ('', 0)
364
365 # if there's more than one, fall back to a 'preferred' list
366 if len(delims) > 1:
367 for d in self.preferred:
368 if d in delims.keys():
369 skipinitialspace = (data[0].count(d) ==
370 data[0].count("%c " % d))
371 return (d, skipinitialspace)
372
Skip Montanaro39b29be2005-12-30 05:09:48 +0000373 # nothing else indicates a preference, pick the character that
374 # dominates(?)
375 items = [(v,k) for (k,v) in delims.items()]
376 items.sort()
377 delim = items[-1][1]
378
Skip Montanaro04ae7052003-04-24 20:21:31 +0000379 skipinitialspace = (data[0].count(delim) ==
380 data[0].count("%c " % delim))
381 return (delim, skipinitialspace)
382
383
Skip Montanaro1448d472003-04-25 14:47:16 +0000384 def has_header(self, sample):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000385 # Creates a dictionary of types of data in each column. If any
386 # column is of a single type (say, integers), *except* for the first
387 # row, then the first row is presumed to be labels. If the type
388 # can't be determined, it is assumed to be a string in which case
389 # the length of the string is the determining factor: if all of the
390 # rows except for the first are the same length, it's a header.
391 # Finally, a 'vote' is taken at the end for each column, adding or
392 # subtracting from the likelihood of the first row being a header.
393
Skip Montanaro1448d472003-04-25 14:47:16 +0000394 rdr = reader(StringIO(sample), self.sniff(sample))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000395
Georg Brandla18af4e2007-04-21 15:47:16 +0000396 header = next(rdr) # assume first row is header
Skip Montanaro04ae7052003-04-24 20:21:31 +0000397
398 columns = len(header)
399 columnTypes = {}
400 for i in range(columns): columnTypes[i] = None
401
402 checked = 0
Skip Montanaro1448d472003-04-25 14:47:16 +0000403 for row in rdr:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000404 # arbitrary number of rows to check, to keep it sane
405 if checked > 20:
406 break
407 checked += 1
408
409 if len(row) != columns:
410 continue # skip rows that have irregular number of columns
411
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000412 for col in list(columnTypes.keys()):
Raymond Hettinger39a55922003-06-12 03:01:55 +0000413
Amaury Forgeot d'Arca4618732008-04-24 18:26:53 +0000414 for thisType in [int, float, complex]:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000415 try:
Raymond Hettinger39a55922003-06-12 03:01:55 +0000416 thisType(row[col])
417 break
Raymond Hettingerabe14e62003-06-12 03:59:17 +0000418 except (ValueError, OverflowError):
Raymond Hettinger39a55922003-06-12 03:01:55 +0000419 pass
420 else:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000421 # fallback to length of string
422 thisType = len(row[col])
423
424 if thisType != columnTypes[col]:
425 if columnTypes[col] is None: # add new column type
426 columnTypes[col] = thisType
427 else:
428 # type is inconsistent, remove column from
429 # consideration
430 del columnTypes[col]
431
432 # finally, compare results against first row and "vote"
433 # on whether it's a header
434 hasHeader = 0
435 for col, colType in columnTypes.items():
436 if type(colType) == type(0): # it's a length
437 if len(header[col]) != colType:
438 hasHeader += 1
439 else:
440 hasHeader -= 1
441 else: # attempt typecast
442 try:
Raymond Hettinger39a55922003-06-12 03:01:55 +0000443 colType(header[col])
Raymond Hettingerf31cb0c2003-06-12 04:05:00 +0000444 except (ValueError, TypeError):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000445 hasHeader += 1
446 else:
447 hasHeader -= 1
448
449 return hasHeader > 0