Skip Montanaro | b4a0417 | 2003-03-20 23:29:12 +0000 | [diff] [blame] | 1 | from _csv import Error, __version__, writer, reader, register_dialect, \ |
| 2 | unregister_dialect, get_dialect, list_dialects, \ |
| 3 | QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \ |
| 4 | __doc__ |
| 5 | |
| 6 | __all__ = [ "QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE", |
| 7 | "Error", "Dialect", "excel", "excel_tab", "reader", "writer", |
| 8 | "register_dialect", "get_dialect", "list_dialects", |
| 9 | "unregister_dialect", "__version__", "DictReader", "DictWriter" ] |
| 10 | |
| 11 | class Dialect: |
| 12 | _name = "" |
| 13 | _valid = False |
| 14 | # placeholders |
| 15 | delimiter = None |
| 16 | quotechar = None |
| 17 | escapechar = None |
| 18 | doublequote = None |
| 19 | skipinitialspace = None |
| 20 | lineterminator = None |
| 21 | quoting = None |
| 22 | |
| 23 | def __init__(self): |
| 24 | if self.__class__ != Dialect: |
| 25 | self._valid = True |
| 26 | errors = self._validate() |
| 27 | if errors != []: |
| 28 | raise Error, "Dialect did not validate: %s" % ", ".join(errors) |
| 29 | |
| 30 | def _validate(self): |
| 31 | errors = [] |
| 32 | if not self._valid: |
| 33 | errors.append("can't directly instantiate Dialect class") |
| 34 | |
| 35 | if self.delimiter is None: |
| 36 | errors.append("delimiter character not set") |
| 37 | elif (not isinstance(self.delimiter, str) or |
| 38 | len(self.delimiter) > 1): |
| 39 | errors.append("delimiter must be one-character string") |
| 40 | |
| 41 | if self.quotechar is None: |
| 42 | if self.quoting != QUOTE_NONE: |
| 43 | errors.append("quotechar not set") |
| 44 | elif (not isinstance(self.quotechar, str) or |
| 45 | len(self.quotechar) > 1): |
| 46 | errors.append("quotechar must be one-character string") |
| 47 | |
| 48 | if self.lineterminator is None: |
| 49 | errors.append("lineterminator not set") |
| 50 | elif not isinstance(self.lineterminator, str): |
| 51 | errors.append("lineterminator must be a string") |
| 52 | |
| 53 | if self.doublequote not in (True, False): |
| 54 | errors.append("doublequote parameter must be True or False") |
| 55 | |
| 56 | if self.skipinitialspace not in (True, False): |
| 57 | errors.append("skipinitialspace parameter must be True or False") |
| 58 | |
| 59 | if self.quoting is None: |
| 60 | errors.append("quoting parameter not set") |
| 61 | |
| 62 | if self.quoting is QUOTE_NONE: |
| 63 | if (not isinstance(self.escapechar, (unicode, str)) or |
| 64 | len(self.escapechar) > 1): |
| 65 | errors.append("escapechar must be a one-character string or unicode object") |
| 66 | |
| 67 | return errors |
| 68 | |
| 69 | class excel(Dialect): |
| 70 | delimiter = ',' |
| 71 | quotechar = '"' |
| 72 | doublequote = True |
| 73 | skipinitialspace = False |
| 74 | lineterminator = '\r\n' |
| 75 | quoting = QUOTE_MINIMAL |
| 76 | register_dialect("excel", excel) |
| 77 | |
| 78 | class excel_tab(excel): |
| 79 | delimiter = '\t' |
| 80 | register_dialect("excel-tab", excel_tab) |
| 81 | |
| 82 | |
| 83 | class DictReader: |
| 84 | def __init__(self, f, fieldnames, restkey=None, restval=None, |
| 85 | dialect="excel", *args): |
| 86 | self.fieldnames = fieldnames # list of keys for the dict |
| 87 | self.restkey = restkey # key to catch long rows |
| 88 | self.restval = restval # default value for short rows |
| 89 | self.reader = reader(f, dialect, *args) |
| 90 | |
| 91 | def __iter__(self): |
| 92 | return self |
| 93 | |
| 94 | def next(self): |
| 95 | row = self.reader.next() |
| 96 | # unlike the basic reader, we prefer not to return blanks, |
| 97 | # because we will typically wind up with a dict full of None |
| 98 | # values |
| 99 | while row == []: |
| 100 | row = self.reader.next() |
| 101 | d = dict(zip(self.fieldnames, row)) |
| 102 | lf = len(self.fieldnames) |
| 103 | lr = len(row) |
| 104 | if lf < lr: |
| 105 | d[self.restkey] = row[lf:] |
| 106 | elif lf > lr: |
| 107 | for key in self.fieldnames[lr:]: |
| 108 | d[key] = self.restval |
| 109 | return d |
| 110 | |
| 111 | |
| 112 | class DictWriter: |
| 113 | def __init__(self, f, fieldnames, restval="", extrasaction="raise", |
| 114 | dialect="excel", *args): |
| 115 | self.fieldnames = fieldnames # list of keys for the dict |
| 116 | self.restval = restval # for writing short dicts |
| 117 | if extrasaction.lower() not in ("raise", "ignore"): |
| 118 | raise ValueError, \ |
| 119 | ("extrasaction (%s) must be 'raise' or 'ignore'" % |
| 120 | extrasaction) |
| 121 | self.extrasaction = extrasaction |
| 122 | self.writer = writer(f, dialect, *args) |
| 123 | |
| 124 | def _dict_to_list(self, rowdict): |
| 125 | if self.extrasaction == "raise": |
| 126 | for k in rowdict.keys(): |
| 127 | if k not in self.fieldnames: |
| 128 | raise ValueError, "dict contains fields not in fieldnames" |
| 129 | return [rowdict.get(key, self.restval) for key in self.fieldnames] |
| 130 | |
| 131 | def writerow(self, rowdict): |
| 132 | return self.writer.writerow(self._dict_to_list(rowdict)) |
| 133 | |
| 134 | def writerows(self, rowdicts): |
| 135 | rows = [] |
| 136 | for rowdict in rowdicts: |
| 137 | rows.append(self._dict_to_list(rowdict)) |
| 138 | return self.writer.writerows(rows) |