Skip to content

Commit f64a1e6

Browse files
authored
Merge pull request #11 from s4int/string_to_csv
String to csv and correct py3 support
2 parents 1925e14 + 3b6199c commit f64a1e6

File tree

6 files changed

+1356
-370
lines changed

6 files changed

+1356
-370
lines changed

CSVLibrary/__init__.py

Lines changed: 172 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import csv
22
from robot.api import logger
33
from .version import VERSION
4+
import sys
5+
6+
if sys.version_info.major >= 3:
7+
from io import StringIO as IO
8+
else:
9+
from io import BytesIO as IO
410

511
__version__ = VERSION
612

@@ -11,36 +17,47 @@ class CSVLibrary(object):
1117
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
1218

1319
@staticmethod
14-
def _open_csv_file_for_read(filename, csv_reader=csv.reader, line_numbers=None, **kwargs):
20+
def _reader(to_read, csv_reader=csv.reader, line_numbers=None, **kwargs):
21+
reader = csv_reader(to_read, **kwargs)
22+
try:
23+
for line_number, row in enumerate(reader):
24+
if line_numbers is None:
25+
yield row
26+
elif isinstance(line_numbers, list) and line_number in line_numbers:
27+
yield row
28+
line_numbers.remove(line_number)
29+
if len(line_numbers) == 0:
30+
break
31+
except csv.Error as e:
32+
logger.error('line %d: %s' % (reader.line_num, e))
33+
34+
def _read_csv(self, csv_handler, csv_reader=csv.reader, line_numbers=None, **kwargs):
1535
if line_numbers is not None and isinstance(line_numbers, list):
16-
line_numbers = map(int, line_numbers)
36+
line_numbers = list(map(int, line_numbers))
37+
38+
return [row for row in self._reader(csv_handler, csv_reader, line_numbers, **kwargs)]
39+
40+
def _open_csv_file_for_read(self, filename, csv_reader=csv.reader, line_numbers=None, **kwargs):
1741
with open(filename, 'r') as csv_handler:
18-
reader = csv_reader(csv_handler, **kwargs)
19-
try:
20-
for line_number, row in enumerate(reader):
21-
if line_numbers is None:
22-
yield row
23-
elif isinstance(line_numbers, list):
24-
if line_number in line_numbers:
25-
yield row
26-
line_numbers.remove(line_number)
27-
if len(line_numbers) == 0:
28-
break
29-
except csv.Error as e:
30-
logger.error('file %s, line %d: %s' % (filename, reader.line_num, e))
42+
return self._read_csv(csv_handler, csv_reader, line_numbers, **kwargs)
3143

3244
@staticmethod
33-
def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs):
34-
with open(filename, 'ab') as csv_handler:
35-
writer = csv_writer(csv_handler, **kwargs)
36-
try:
37-
if isinstance(writer, csv.DictWriter) and 'fieldnames' in kwargs.keys():
38-
csv_handler.truncate()
39-
writer.writeheader()
40-
41-
writer.writerows(data)
42-
except csv.Error as e:
43-
logger.error('file %s, line %d: %s' % (filename, writer.line_num, e))
45+
def _write_csv(csv_handler, data, csv_writer=csv.writer, **kwargs):
46+
if 'fieldnames' not in kwargs.keys() and isinstance(data[0], dict):
47+
kwargs['fieldnames'] = data[0].keys()
48+
49+
writer = csv_writer(csv_handler, **kwargs)
50+
try:
51+
if isinstance(writer, csv.DictWriter) and csv_handler.tell() == 0:
52+
writer.writeheader()
53+
54+
writer.writerows(data)
55+
except csv.Error as e:
56+
logger.error('%s' % e)
57+
58+
def _open_csv_file_for_write(self, filename, data, csv_writer=csv.writer, **kwargs):
59+
with open(filename, 'a') as csv_handler:
60+
self._write_csv(csv_handler, data, csv_writer, **kwargs)
4461

4562
@staticmethod
4663
def empty_csv_file(filename):
@@ -69,7 +86,31 @@ def read_csv_file_to_list(self, filename, delimiter=',', **kwargs):
6986
delimiter=str(delimiter),
7087
**kwargs
7188
)
72-
return [tuple(row) for row in csv_list]
89+
return csv_list
90+
91+
def read_csv_string_to_list(self, csv_string, delimiter=',', **kwargs):
92+
"""Read CSV string and return its content as a Python list of tuples.
93+
94+
- ``csv_string``: name of csv file
95+
- ``delimiter``: Default: `,`
96+
- ``line_numbers``: List of linenumbers to read. Default None
97+
- ``quoting`` (int):
98+
_0_: QUOTE_MINIMAL
99+
_1_: QUOTE_ALL
100+
_2_: QUOTE_NONNUMERIC
101+
_3_: QUOTE_NONE
102+
"""
103+
if sys.version_info.major < 3:
104+
csv_string = csv_string.encode("utf-8")
105+
106+
with IO(csv_string) as csv_handler:
107+
csv_list = self._read_csv(
108+
csv_handler,
109+
csv_reader=csv.reader,
110+
delimiter=str(delimiter),
111+
**kwargs
112+
)
113+
return csv_list
73114

74115
def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, **kwargs):
75116
"""Read CSV file and return its content as a Python list of dictionaries.
@@ -84,14 +125,40 @@ def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None,
84125
_2_: QUOTE_NONNUMERIC
85126
_3_: QUOTE_NONE
86127
"""
128+
kwargs['fieldnames'] = fieldnames
87129
csv_dict = self._open_csv_file_for_read(
88130
filename,
89131
csv_reader=csv.DictReader,
90132
delimiter=str(delimiter),
91-
fieldnames=fieldnames,
92133
**kwargs
93134
)
94-
return [item for item in csv_dict]
135+
return csv_dict
136+
137+
def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=None, **kwargs):
138+
"""Read CSV from string and return its content as a Python list of dictionaries.
139+
140+
- ``csv_string``: csv formatted string
141+
- ``delimiter``: Default: `,`
142+
- ``fieldnames``: list of column names
143+
- ``line_numbers``: List of linenumbers to read. Default None
144+
- ``quoting`` (int):
145+
_0_: QUOTE_MINIMAL
146+
_1_: QUOTE_ALL
147+
_2_: QUOTE_NONNUMERIC
148+
_3_: QUOTE_NONE
149+
"""
150+
if sys.version_info.major < 3:
151+
csv_string = csv_string.encode("utf-8")
152+
153+
with IO(csv_string) as csv_handler:
154+
csv_dict = self._read_csv(
155+
csv_handler,
156+
csv_reader=csv.DictReader,
157+
delimiter=str(delimiter),
158+
fieldnames=fieldnames,
159+
**kwargs
160+
)
161+
return csv_dict
95162

96163
def append_to_csv_file(self, filename, data, **kwargs):
97164
"""This keyword will append data to a new or existing CSV file.
@@ -104,9 +171,56 @@ def append_to_csv_file(self, filename, data, **kwargs):
104171
_2_: QUOTE_NONNUMERIC
105172
_3_: QUOTE_NONE
106173
"""
107-
if isinstance(data[0], dict):
108-
data = map(lambda row: row.items(), data)
109-
self._open_csv_file_for_write(filename, data=data, csv_writer=csv.writer, **kwargs)
174+
175+
if isinstance(data, dict):
176+
data = [data]
177+
178+
fieldnames = self._open_csv_file_for_read(
179+
filename,
180+
csv_reader=csv.reader,
181+
line_numbers=[0],
182+
**kwargs
183+
)[0]
184+
185+
self._open_csv_file_for_write(
186+
filename,
187+
data=data,
188+
csv_writer=csv.DictWriter,
189+
fieldnames=fieldnames,
190+
**kwargs
191+
)
192+
193+
def append_to_csv_string(self, csv_string, data, **kwargs):
194+
"""This keyword will append data to a new or existing CSV string.
195+
196+
- ``csv_string``: csv formatted string
197+
- ``data``: iterable(e.g. list or tuple) data.
198+
- ``quoting`` (int):
199+
_0_: QUOTE_MINIMAL
200+
_1_: QUOTE_ALL
201+
_2_: QUOTE_NONNUMERIC
202+
_3_: QUOTE_NONE
203+
"""
204+
if isinstance(data, dict):
205+
data = [data]
206+
207+
if 'lineterminator' not in kwargs.keys():
208+
kwargs['lineterminator'] = '\n'
209+
210+
if sys.version_info.major < 3:
211+
csv_string = csv_string.encode("utf-8")
212+
213+
with IO(csv_string) as csv_handler:
214+
fieldnames = self._read_csv(
215+
csv_handler,
216+
csv_reader=csv.reader,
217+
**kwargs
218+
)[0]
219+
220+
kwargs['fieldnames'] = fieldnames
221+
222+
self._write_csv(csv_handler, data, csv_writer=csv.DictWriter, **kwargs)
223+
return csv_handler.getvalue()
110224

111225
def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs):
112226
"""This keyword will create new file
@@ -121,12 +235,36 @@ def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter='
121235
_2_: QUOTE_NONNUMERIC
122236
_3_: QUOTE_NONE
123237
"""
124-
fieldnames = fieldnames or data[0].keys()
238+
kwargs['fieldnames'] = fieldnames or data[0].keys()
239+
125240
self._open_csv_file_for_write(
126241
filename,
127242
data=data,
128243
csv_writer=csv.DictWriter,
129244
delimiter=str(delimiter),
130-
fieldnames=fieldnames,
131245
**kwargs
132246
)
247+
248+
def csv_string_from_associative(self, data, fieldnames=None, delimiter=',', **kwargs):
249+
"""This keyword will create new file
250+
251+
- ``data``: iterable(e.g. list or tuple) data.
252+
- ``fieldnames``: list of column names
253+
- ``delimiter``: Default: `,`
254+
- ``quoting`` (int):
255+
_0_: QUOTE_MINIMAL
256+
_1_: QUOTE_ALL
257+
_2_: QUOTE_NONNUMERIC
258+
_3_: QUOTE_NONE
259+
"""
260+
if isinstance(data, dict):
261+
data = [data]
262+
263+
kwargs['fieldnames'] = fieldnames or data[0].keys()
264+
if 'lineterminator' not in kwargs.keys():
265+
kwargs['lineterminator'] = '\n'
266+
kwargs['delimiter'] = delimiter
267+
268+
with IO() as csv_handler:
269+
self._write_csv(csv_handler, data, csv_writer=csv.DictWriter, **kwargs)
270+
return csv_handler.getvalue()

0 commit comments

Comments
 (0)