From a70c23e310915a65b123749d16c657802c8b97cf Mon Sep 17 00:00:00 2001 From: s4int Date: Mon, 6 Jul 2020 00:00:39 +0200 Subject: [PATCH 01/13] Read from csv string --- CSVLibrary/__init__.py | 83 +++++++++++++++++++++++++++++++++-------- tests/ReadCSVTest.robot | 21 +++++++++++ 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 297135c..13debe5 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -11,23 +11,32 @@ class CSVLibrary(object): ROBOT_LIBRARY_SCOPE = 'GLOBAL' @staticmethod - def _open_csv_file_for_read(filename, csv_reader=csv.reader, line_numbers=None, **kwargs): + def _reader(to_read, csv_reader=csv.reader, line_numbers=None, **kwargs): + reader = csv_reader(to_read, **kwargs) + try: + for line_number, row in enumerate(reader): + if line_numbers is None: + yield row + elif isinstance(line_numbers, list): + if line_number in line_numbers: + yield row + line_numbers.remove(line_number) + if len(line_numbers) == 0: + break + except csv.Error as e: + logger.error('line %d: %s' % (reader.line_num, e)) + + def _open_csv_file_for_read(self, filename, csv_reader=csv.reader, line_numbers=None, **kwargs): if line_numbers is not None and isinstance(line_numbers, list): line_numbers = map(int, line_numbers) with open(filename, 'r') as csv_handler: - reader = csv_reader(csv_handler, **kwargs) - try: - for line_number, row in enumerate(reader): - if line_numbers is None: - yield row - elif isinstance(line_numbers, list): - if line_number in line_numbers: - yield row - line_numbers.remove(line_number) - if len(line_numbers) == 0: - break - except csv.Error as e: - logger.error('file %s, line %d: %s' % (filename, reader.line_num, e)) + return [row for row in self._reader(csv_handler, csv_reader, line_numbers, **kwargs)] + + def _read_csv(self, csv_string, csv_reader=csv.reader, line_numbers=None, **kwargs): + if line_numbers is not None and isinstance(line_numbers, list): + line_numbers = map(int, line_numbers) + + return [row for row in self._reader(csv_string.splitlines(), csv_reader, line_numbers, **kwargs)] @staticmethod def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs): @@ -69,7 +78,27 @@ def read_csv_file_to_list(self, filename, delimiter=',', **kwargs): delimiter=str(delimiter), **kwargs ) - return [tuple(row) for row in csv_list] + return csv_list + + def read_csv_string_to_list(self, filename, delimiter=',', **kwargs): + """Read CSV file and return its content as a Python list of tuples. + + - ``filename``: name of csv file + - ``delimiter``: Default: `,` + - ``line_numbers``: List of linenumbers to read. Default None + - ``quoting`` (int): + _0_: QUOTE_MINIMAL + _1_: QUOTE_ALL + _2_: QUOTE_NONNUMERIC + _3_: QUOTE_NONE + """ + csv_list = self._read_csv( + filename, + csv_reader=csv.reader, + delimiter=str(delimiter), + **kwargs + ) + return csv_list def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, **kwargs): """Read CSV file and return its content as a Python list of dictionaries. @@ -91,7 +120,29 @@ def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, fieldnames=fieldnames, **kwargs ) - return [item for item in csv_dict] + return csv_dict + + def read_csv_string_to_associative(self, filename, delimiter=',', fieldnames=None, **kwargs): + """Read CSV file and return its content as a Python list of dictionaries. + + - ``filename``: name of csv file + - ``delimiter``: Default: `,` + - ``fieldnames``: list of column names + - ``line_numbers``: List of linenumbers to read. Default None + - ``quoting`` (int): + _0_: QUOTE_MINIMAL + _1_: QUOTE_ALL + _2_: QUOTE_NONNUMERIC + _3_: QUOTE_NONE + """ + csv_dict = self._read_csv( + filename, + csv_reader=csv.DictReader, + delimiter=str(delimiter), + fieldnames=fieldnames, + **kwargs + ) + return csv_dict def append_to_csv_file(self, filename, data, **kwargs): """This keyword will append data to a new or existing CSV file. diff --git a/tests/ReadCSVTest.robot b/tests/ReadCSVTest.robot index 4442d7e..937adfd 100644 --- a/tests/ReadCSVTest.robot +++ b/tests/ReadCSVTest.robot @@ -8,6 +8,10 @@ Library CSVLibrary @{template_list}= 1 Douglas Morris dmorris0@mozilla.org Male 205.4.212.229 &{template_dict}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender=Male ip_address=205.4.212.229 &{template_dict_quoting}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender="Male ip_address=205.4.212.229 +${csv_string}= SEPARATOR= +... id,first_name,last_name,email,gender,ip_address\n +... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n +... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n *** Test Cases *** Read csv file to a list example test @@ -21,3 +25,20 @@ Read csv file to a dict example test Read csv file without quoting to associative @{dict}= read csv file to associative ${CURDIR}${/}data_quoting.csv delimiter=, quoting=${3} dictionaries should be equal ${template_dict_quoting} ${dict[0]} + +Read csv string to a list example test + @{list}= read csv string to list ${csv_string} + lists should be equal ${template_list} ${list[1]} + +Read csv string to a dict example test + @{dict}= read csv string to associative ${csv_string} + dictionaries should be equal ${template_dict} ${dict[0]} + +Empty csv file + Pass Execution TBA + +Append to csv file + Pass Execution TBA + +Csv file from associative + Pass Execution TBA From 98bbebca61da5876ecc65eefffb840eb9b2fd84e Mon Sep 17 00:00:00 2001 From: s4int Date: Mon, 6 Jul 2020 00:06:33 +0200 Subject: [PATCH 02/13] fixes #1 --- CSVLibrary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 13debe5..e14c646 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -49,7 +49,7 @@ def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs): writer.writerows(data) except csv.Error as e: - logger.error('file %s, line %d: %s' % (filename, writer.line_num, e)) + logger.error('file %s, %s' % (filename, e)) @staticmethod def empty_csv_file(filename): From 2749f5612ee753f21f051a27ec515e8dbb662ce9 Mon Sep 17 00:00:00 2001 From: s4int Date: Mon, 6 Jul 2020 00:13:46 +0200 Subject: [PATCH 03/13] Combine condition --- CSVLibrary/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index e14c646..650ebcd 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -17,12 +17,11 @@ def _reader(to_read, csv_reader=csv.reader, line_numbers=None, **kwargs): for line_number, row in enumerate(reader): if line_numbers is None: yield row - elif isinstance(line_numbers, list): - if line_number in line_numbers: - yield row - line_numbers.remove(line_number) - if len(line_numbers) == 0: - break + elif isinstance(line_numbers, list) and line_number in line_numbers: + yield row + line_numbers.remove(line_number) + if len(line_numbers) == 0: + break except csv.Error as e: logger.error('line %d: %s' % (reader.line_num, e)) From 04f3933b97562c793a23feb494468468d36eb820 Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:23:45 +0200 Subject: [PATCH 04/13] Fix parameters naming --- CSVLibrary/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 650ebcd..cec316d 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -79,10 +79,10 @@ def read_csv_file_to_list(self, filename, delimiter=',', **kwargs): ) return csv_list - def read_csv_string_to_list(self, filename, delimiter=',', **kwargs): - """Read CSV file and return its content as a Python list of tuples. + def read_csv_string_to_list(self, csv_string, delimiter=',', **kwargs): + """Read CSV string and return its content as a Python list of tuples. - - ``filename``: name of csv file + - ``csv_string``: name of csv file - ``delimiter``: Default: `,` - ``line_numbers``: List of linenumbers to read. Default None - ``quoting`` (int): @@ -92,7 +92,7 @@ def read_csv_string_to_list(self, filename, delimiter=',', **kwargs): _3_: QUOTE_NONE """ csv_list = self._read_csv( - filename, + csv_string, csv_reader=csv.reader, delimiter=str(delimiter), **kwargs @@ -121,10 +121,10 @@ def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, ) return csv_dict - def read_csv_string_to_associative(self, filename, delimiter=',', fieldnames=None, **kwargs): - """Read CSV file and return its content as a Python list of dictionaries. + def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=None, **kwargs): + """Read CSV from string and return its content as a Python list of dictionaries. - - ``filename``: name of csv file + - ``csv_string``: name of csv file - ``delimiter``: Default: `,` - ``fieldnames``: list of column names - ``line_numbers``: List of linenumbers to read. Default None @@ -135,7 +135,7 @@ def read_csv_string_to_associative(self, filename, delimiter=',', fieldnames=Non _3_: QUOTE_NONE """ csv_dict = self._read_csv( - filename, + csv_string, csv_reader=csv.DictReader, delimiter=str(delimiter), fieldnames=fieldnames, From eb63492eee1de2571b97ba25ccc2951c6d95eba8 Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:25:15 +0200 Subject: [PATCH 05/13] list from generator --- CSVLibrary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index cec316d..03f88f2 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -27,7 +27,7 @@ def _reader(to_read, csv_reader=csv.reader, line_numbers=None, **kwargs): def _open_csv_file_for_read(self, filename, csv_reader=csv.reader, line_numbers=None, **kwargs): if line_numbers is not None and isinstance(line_numbers, list): - line_numbers = map(int, line_numbers) + line_numbers = list(map(int, line_numbers)) with open(filename, 'r') as csv_handler: return [row for row in self._reader(csv_handler, csv_reader, line_numbers, **kwargs)] From e3ad7f6fa4ca17e6a6c56087042299a5b51cbfa3 Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:27:05 +0200 Subject: [PATCH 06/13] Write fieldnames only if file is empty --- CSVLibrary/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 03f88f2..9377e79 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -39,11 +39,14 @@ def _read_csv(self, csv_string, csv_reader=csv.reader, line_numbers=None, **kwar @staticmethod def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs): - with open(filename, 'ab') as csv_handler: + + if 'fieldnames' not in kwargs.keys() and isinstance(data[0], dict): + kwargs['fieldnames'] = data[0].keys() + + with open(filename, 'a', newline='') as csv_handler: writer = csv_writer(csv_handler, **kwargs) try: - if isinstance(writer, csv.DictWriter) and 'fieldnames' in kwargs.keys(): - csv_handler.truncate() + if isinstance(writer, csv.DictWriter) and csv_handler.tell() == 0: writer.writeheader() writer.writerows(data) From c9fd8b67db2ce48b0db10d8739b697cbdc954fbc Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:28:30 +0200 Subject: [PATCH 07/13] Add appending to strings nad fix appending to file --- CSVLibrary/__init__.py | 58 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 9377e79..96ba6f5 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -43,7 +43,7 @@ def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs): if 'fieldnames' not in kwargs.keys() and isinstance(data[0], dict): kwargs['fieldnames'] = data[0].keys() - with open(filename, 'a', newline='') as csv_handler: + with open(filename, 'a') as csv_handler: writer = csv_writer(csv_handler, **kwargs) try: if isinstance(writer, csv.DictWriter) and csv_handler.tell() == 0: @@ -149,6 +149,36 @@ def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=N def append_to_csv_file(self, filename, data, **kwargs): """This keyword will append data to a new or existing CSV file. + - ``filename``: name of csv file + - ``data``: iterable(e.g. list or tuple) data. + - ``quoting`` (int): + _0_: QUOTE_MINIMAL + _1_: QUOTE_ALL + _2_: QUOTE_NONNUMERIC + _3_: QUOTE_NONE + """ + + if isinstance(data, dict): + data = [data] + + fieldnames = self._open_csv_file_for_read( + filename, + csv_reader=csv.reader, + line_numbers=[0], + **kwargs + )[0] + + self._open_csv_file_for_write( + filename, + data=data, + csv_writer=csv.DictWriter, + fieldnames=fieldnames, + **kwargs + ) + + def append_to_csv_string(self, filename, data, **kwargs): + """This keyword will append data to a new or existing CSV file. + - ``filename``: name of csv file - ``data``: iterable(e.g. list or tuple) data. - ``quoting`` (int): @@ -159,11 +189,35 @@ def append_to_csv_file(self, filename, data, **kwargs): """ if isinstance(data[0], dict): data = map(lambda row: row.items(), data) - self._open_csv_file_for_write(filename, data=data, csv_writer=csv.writer, **kwargs) + self._open_csv_file_for_write(filename, data=data, csv_writer=csv.DictWriter, **kwargs) def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): """This keyword will create new file + - ``filename``: name of csv file + - ``data``: iterable(e.g. list or tuple) data. + - ``fieldnames``: list of column names + - ``delimiter``: Default: `,` + - ``quoting`` (int): + _0_: QUOTE_MINIMAL + _1_: QUOTE_ALL + _2_: QUOTE_NONNUMERIC + _3_: QUOTE_NONE + """ + fieldnames = fieldnames or data[0].keys() + + self._open_csv_file_for_write( + filename, + data=data, + csv_writer=csv.DictWriter, + delimiter=str(delimiter), + fieldnames=fieldnames, + **kwargs + ) + + def csv_string_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): + """This keyword will create new file + - ``filename``: name of csv file - ``data``: iterable(e.g. list or tuple) data. - ``fieldnames``: list of column names From 3d718e1cec9ff352e0ab6e839115eeddfa8c0288 Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:29:02 +0200 Subject: [PATCH 08/13] Empty line at end of the data file --- tests/data.csv | 2 +- tests/data_quoting.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data.csv b/tests/data.csv index 3066d92..4bce924 100644 --- a/tests/data.csv +++ b/tests/data.csv @@ -8,4 +8,4 @@ id,first_name,last_name,email,gender,ip_address 7,Rachel,Robinson,rrobinson6@blogger.com,Female,132.66.117.101 8,Phillip,Johnston,pjohnston7@disqus.com,Male,70.152.55.21 9,Craig,Burton,cburton8@toplist.cz,Male,73.117.157.82 -10,Patrick,Fisher,pfisher9@1und1.de,Male,2.36.191.97 \ No newline at end of file +10,Patrick,Fisher,pfisher9@1und1.de,Male,2.36.191.97 diff --git a/tests/data_quoting.csv b/tests/data_quoting.csv index 1faa8e7..36c7a58 100644 --- a/tests/data_quoting.csv +++ b/tests/data_quoting.csv @@ -8,4 +8,4 @@ id,first_name,last_name,email,gender,ip_address 7,Rachel,Robinson,rrobinson6@blogger.com,Female,132.66.117.101 8,Phillip,Johnston,pjohnston7@disqus.com,Male,70.152.55.21 9,Craig,Burton,cburton8@toplist.cz,Male,73.117.157.82 -10,Patrick,Fisher,pfisher9@1und1.de,Male,2.36.191.97 \ No newline at end of file +10,Patrick,Fisher,pfisher9@1und1.de,Male,2.36.191.97 From ea8aa83c4be904cf2b506929784f86687d88226a Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 23 Apr 2021 22:30:03 +0200 Subject: [PATCH 09/13] Add tests --- tests/ReadCSVTest.robot | 14 +++--------- tests/WriteCSVTest.robot | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 tests/WriteCSVTest.robot diff --git a/tests/ReadCSVTest.robot b/tests/ReadCSVTest.robot index 937adfd..f3918bb 100644 --- a/tests/ReadCSVTest.robot +++ b/tests/ReadCSVTest.robot @@ -9,9 +9,9 @@ Library CSVLibrary &{template_dict}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender=Male ip_address=205.4.212.229 &{template_dict_quoting}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender="Male ip_address=205.4.212.229 ${csv_string}= SEPARATOR= -... id,first_name,last_name,email,gender,ip_address\n -... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n -... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n +... id,first_name,last_name,email,gender,ip_address\n +... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n +... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n *** Test Cases *** Read csv file to a list example test @@ -34,11 +34,3 @@ Read csv string to a dict example test @{dict}= read csv string to associative ${csv_string} dictionaries should be equal ${template_dict} ${dict[0]} -Empty csv file - Pass Execution TBA - -Append to csv file - Pass Execution TBA - -Csv file from associative - Pass Execution TBA diff --git a/tests/WriteCSVTest.robot b/tests/WriteCSVTest.robot new file mode 100644 index 0000000..d9d931e --- /dev/null +++ b/tests/WriteCSVTest.robot @@ -0,0 +1,46 @@ +*** Settings *** +Documentation CSV examples for Robot Framework. +Library Collections +Library OperatingSystem +Library CSVLibrary + +*** Variables *** +# Example data generated with: https://www.mockaroo.com/ +@{template_list}= 1 Douglas Morris dmorris0@mozilla.org Male 205.4.212.229 +&{template_dict_row1}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender=Male ip_address=205.4.212.229 +&{template_dict_row2}= id=2 first_name=Stephanie last_name=Oliver email=soliver1@google.com.br gender=Female ip_address=18.101.154.106 +@{template_dict_rows}= &{template_dict_row1} &{template_dict_row2} + +&{template_dict_append}= ip_address=255.36.191.0 id=11 first_name=John last_name=ParkerFisher email=jparker00@google.com gender=Male + +${csv_string}= SEPARATOR= +... id,first_name,last_name,email,gender,ip_address\n +... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n +... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n +${data_empty_copy}= ${CURDIR}${/}data_copy.csv +${data_create_file}= ${CURDIR}${/}data_create.csv +${data_append_file}= ${CURDIR}${/}data_append.csv + +*** Test Cases *** +Empty csv file + [Setup] Copy File ${CURDIR}${/}data.csv ${data_empty_copy} + File Should Not Be Empty ${data_empty_copy} + Empty Csv File ${data_empty_copy} + File Should Be Empty ${data_empty_copy} + [Teardown] Remove File ${data_empty_copy} + +Csv file from associative + File Should Not Exist ${data_create_file} + Csv File From Associative ${data_create_file} ${template_dict_rows} + ${content}= Get File ${data_create_file} + Should Be Equal ${csv_string} ${content} + [Teardown] Remove File ${data_create_file} + +Append to csv file + [Setup] Copy File ${CURDIR}${/}data.csv ${data_append_file} + File Should Not Be Empty ${data_append_file} + ${aa}= Append To Csv File ${data_append_file} ${template_dict_append} + + @{appended_dict}= read csv file to associative ${data_append_file} + List Should Contain Value ${appended_dict} ${template_dict_append} + [Teardown] Remove File ${data_append_file} From 49c80e80739c671d40919bf75e27505cf0cef694 Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 26 Nov 2021 20:53:43 +0100 Subject: [PATCH 10/13] Operate on csv string --- CSVLibrary/__init__.py | 69 +++++++++++++++++++++++++++++----------- tests/WriteCSVTest.robot | 14 ++++++++ 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 96ba6f5..25d170a 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -1,6 +1,12 @@ import csv from robot.api import logger from .version import VERSION +import sys + +if sys.version_info.major >= 3: + from io import StringIO as IO +else: + from io import BytesIO as IO __version__ = VERSION @@ -127,7 +133,7 @@ def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=None, **kwargs): """Read CSV from string and return its content as a Python list of dictionaries. - - ``csv_string``: name of csv file + - ``csv_string``: csv formatted string - ``delimiter``: Default: `,` - ``fieldnames``: list of column names - ``line_numbers``: List of linenumbers to read. Default None @@ -176,10 +182,10 @@ def append_to_csv_file(self, filename, data, **kwargs): **kwargs ) - def append_to_csv_string(self, filename, data, **kwargs): - """This keyword will append data to a new or existing CSV file. + def append_to_csv_string(self, csv_string, data, delimiter=',', **kwargs): + """This keyword will append data to a new or existing CSV string. - - ``filename``: name of csv file + - ``csv_string``: csv formatted string - ``data``: iterable(e.g. list or tuple) data. - ``quoting`` (int): _0_: QUOTE_MINIMAL @@ -187,9 +193,31 @@ def append_to_csv_string(self, filename, data, **kwargs): _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ - if isinstance(data[0], dict): - data = map(lambda row: row.items(), data) - self._open_csv_file_for_write(filename, data=data, csv_writer=csv.DictWriter, **kwargs) + if isinstance(data, dict): + data = [data] + + fieldnames = self._read_csv( + csv_string, + csv_reader=csv.reader, + delimiter=str(delimiter), + **kwargs + )[0] + + with IO() as output: + if sys.version_info.major >= 3: + output.write(csv_string) + else: + output.write(csv_string.encode("utf-8")) + + if 'fieldnames' not in kwargs.keys(): + kwargs['fieldnames'] = fieldnames + if 'lineterminator' not in kwargs.keys(): + kwargs['lineterminator'] = '\n' + + writer = csv.DictWriter(output, **kwargs) + writer.writerows(data) + + return output.getvalue() def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): """This keyword will create new file @@ -215,10 +243,9 @@ def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=' **kwargs ) - def csv_string_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): + def csv_string_from_associative(self, data, fieldnames=None, delimiter=',', **kwargs): """This keyword will create new file - - ``filename``: name of csv file - ``data``: iterable(e.g. list or tuple) data. - ``fieldnames``: list of column names - ``delimiter``: Default: `,` @@ -228,12 +255,18 @@ def csv_string_from_associative(self, filename, data, fieldnames=None, delimiter _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ - fieldnames = fieldnames or data[0].keys() - self._open_csv_file_for_write( - filename, - data=data, - csv_writer=csv.DictWriter, - delimiter=str(delimiter), - fieldnames=fieldnames, - **kwargs - ) + if isinstance(data, dict): + data = [data] + + if 'fieldnames' not in kwargs.keys(): + kwargs['fieldnames'] = fieldnames or data[0].keys() + if 'lineterminator' not in kwargs.keys(): + kwargs['lineterminator'] = '\n' + kwargs['delimiter'] = delimiter + + with IO() as output: + writer = csv.DictWriter(output, **kwargs) + writer.writeheader() + writer.writerows(data) + + return output.getvalue() diff --git a/tests/WriteCSVTest.robot b/tests/WriteCSVTest.robot index d9d931e..86a4068 100644 --- a/tests/WriteCSVTest.robot +++ b/tests/WriteCSVTest.robot @@ -9,6 +9,7 @@ Library CSVLibrary @{template_list}= 1 Douglas Morris dmorris0@mozilla.org Male 205.4.212.229 &{template_dict_row1}= id=1 first_name=Douglas last_name=Morris email=dmorris0@mozilla.org gender=Male ip_address=205.4.212.229 &{template_dict_row2}= id=2 first_name=Stephanie last_name=Oliver email=soliver1@google.com.br gender=Female ip_address=18.101.154.106 +&{template_dict_row3}= id=3 first_name=Stephanie last_name=Oliver email=soliver1@google.com.br gender=Female ip_address=18.101.154.106 @{template_dict_rows}= &{template_dict_row1} &{template_dict_row2} &{template_dict_append}= ip_address=255.36.191.0 id=11 first_name=John last_name=ParkerFisher email=jparker00@google.com gender=Male @@ -17,6 +18,11 @@ ${csv_string}= SEPARATOR= ... id,first_name,last_name,email,gender,ip_address\n ... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n ... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n +${csv_string_ext}= SEPARATOR= +... id,first_name,last_name,email,gender,ip_address\n +... 1,Douglas,Morris,dmorris0@mozilla.org,Male,205.4.212.229\n +... 2,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n +... 3,Stephanie,Oliver,soliver1@google.com.br,Female,18.101.154.106\n ${data_empty_copy}= ${CURDIR}${/}data_copy.csv ${data_create_file}= ${CURDIR}${/}data_create.csv ${data_append_file}= ${CURDIR}${/}data_append.csv @@ -44,3 +50,11 @@ Append to csv file @{appended_dict}= read csv file to associative ${data_append_file} List Should Contain Value ${appended_dict} ${template_dict_append} [Teardown] Remove File ${data_append_file} + +Append to csv string + ${content}= append to csv string ${csv_string} ${template_dict_row3} + Should Be Equal ${csv_string_ext} ${content} + +CSV string from associative + ${content}= csv string from associative ${template_dict_rows} + Should Be Equal ${csv_string} ${content} From 7ddee3d570c9ee0097137856bbe3ae6d21a07b7c Mon Sep 17 00:00:00 2001 From: s4int Date: Fri, 26 Nov 2021 23:08:02 +0100 Subject: [PATCH 11/13] refactor --- CSVLibrary/__init__.py | 61 +++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 25d170a..1a73495 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -31,33 +31,33 @@ def _reader(to_read, csv_reader=csv.reader, line_numbers=None, **kwargs): except csv.Error as e: logger.error('line %d: %s' % (reader.line_num, e)) - def _open_csv_file_for_read(self, filename, csv_reader=csv.reader, line_numbers=None, **kwargs): + def _read_csv(self, csv_handler, csv_reader=csv.reader, line_numbers=None, **kwargs): if line_numbers is not None and isinstance(line_numbers, list): line_numbers = list(map(int, line_numbers)) - with open(filename, 'r') as csv_handler: - return [row for row in self._reader(csv_handler, csv_reader, line_numbers, **kwargs)] - def _read_csv(self, csv_string, csv_reader=csv.reader, line_numbers=None, **kwargs): - if line_numbers is not None and isinstance(line_numbers, list): - line_numbers = map(int, line_numbers) + return [row for row in self._reader(csv_handler, csv_reader, line_numbers, **kwargs)] - return [row for row in self._reader(csv_string.splitlines(), csv_reader, line_numbers, **kwargs)] + def _open_csv_file_for_read(self, filename, csv_reader=csv.reader, line_numbers=None, **kwargs): + with open(filename, 'r') as csv_handler: + return self._read_csv(csv_handler, csv_reader, line_numbers, **kwargs) @staticmethod - def _open_csv_file_for_write(filename, data, csv_writer=csv.writer, **kwargs): - + def _write_csv(csv_handler, data, csv_writer=csv.writer, **kwargs): if 'fieldnames' not in kwargs.keys() and isinstance(data[0], dict): kwargs['fieldnames'] = data[0].keys() - with open(filename, 'a') as csv_handler: - writer = csv_writer(csv_handler, **kwargs) - try: - if isinstance(writer, csv.DictWriter) and csv_handler.tell() == 0: - writer.writeheader() + writer = csv_writer(csv_handler, **kwargs) + try: + if isinstance(writer, csv.DictWriter) and csv_handler.tell() == 0: + writer.writeheader() + + writer.writerows(data) + except csv.Error as e: + logger.error('%s' % e) - writer.writerows(data) - except csv.Error as e: - logger.error('file %s, %s' % (filename, e)) + def _open_csv_file_for_write(self, filename, data, csv_writer=csv.writer, **kwargs): + with open(filename, 'a') as csv_handler: + self._write_csv(csv_handler, data, csv_writer, **kwargs) @staticmethod def empty_csv_file(filename): @@ -101,7 +101,7 @@ def read_csv_string_to_list(self, csv_string, delimiter=',', **kwargs): _3_: QUOTE_NONE """ csv_list = self._read_csv( - csv_string, + csv_string.splitlines(), csv_reader=csv.reader, delimiter=str(delimiter), **kwargs @@ -144,7 +144,7 @@ def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=N _3_: QUOTE_NONE """ csv_dict = self._read_csv( - csv_string, + csv_string.splitlines(), csv_reader=csv.DictReader, delimiter=str(delimiter), fieldnames=fieldnames, @@ -197,26 +197,23 @@ def append_to_csv_string(self, csv_string, data, delimiter=',', **kwargs): data = [data] fieldnames = self._read_csv( - csv_string, + csv_string.splitlines(), csv_reader=csv.reader, delimiter=str(delimiter), **kwargs )[0] + kwargs['fieldnames'] = fieldnames + if 'lineterminator' not in kwargs.keys(): + kwargs['lineterminator'] = '\n' + with IO() as output: if sys.version_info.major >= 3: output.write(csv_string) else: output.write(csv_string.encode("utf-8")) - if 'fieldnames' not in kwargs.keys(): - kwargs['fieldnames'] = fieldnames - if 'lineterminator' not in kwargs.keys(): - kwargs['lineterminator'] = '\n' - - writer = csv.DictWriter(output, **kwargs) - writer.writerows(data) - + self._write_csv(output, data, csv_writer=csv.DictWriter, **kwargs) return output.getvalue() def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): @@ -258,15 +255,11 @@ def csv_string_from_associative(self, data, fieldnames=None, delimiter=',', **kw if isinstance(data, dict): data = [data] - if 'fieldnames' not in kwargs.keys(): - kwargs['fieldnames'] = fieldnames or data[0].keys() + kwargs['fieldnames'] = fieldnames or data[0].keys() if 'lineterminator' not in kwargs.keys(): kwargs['lineterminator'] = '\n' kwargs['delimiter'] = delimiter with IO() as output: - writer = csv.DictWriter(output, **kwargs) - writer.writeheader() - writer.writerows(data) - + self._write_csv(output, data, csv_writer=csv.DictWriter, **kwargs) return output.getvalue() From 52c7a0915f86873bec86e25ffc531e2b7db74958 Mon Sep 17 00:00:00 2001 From: s4int Date: Sat, 27 Nov 2021 19:33:42 +0100 Subject: [PATCH 12/13] refactor --- CSVLibrary/__init__.py | 79 ++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/CSVLibrary/__init__.py b/CSVLibrary/__init__.py index 1a73495..97075a0 100644 --- a/CSVLibrary/__init__.py +++ b/CSVLibrary/__init__.py @@ -100,13 +100,17 @@ def read_csv_string_to_list(self, csv_string, delimiter=',', **kwargs): _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ - csv_list = self._read_csv( - csv_string.splitlines(), - csv_reader=csv.reader, - delimiter=str(delimiter), - **kwargs - ) - return csv_list + if sys.version_info.major < 3: + csv_string = csv_string.encode("utf-8") + + with IO(csv_string) as csv_handler: + csv_list = self._read_csv( + csv_handler, + csv_reader=csv.reader, + delimiter=str(delimiter), + **kwargs + ) + return csv_list def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, **kwargs): """Read CSV file and return its content as a Python list of dictionaries. @@ -121,11 +125,11 @@ def read_csv_file_to_associative(self, filename, delimiter=',', fieldnames=None, _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ + kwargs['fieldnames'] = fieldnames csv_dict = self._open_csv_file_for_read( filename, csv_reader=csv.DictReader, delimiter=str(delimiter), - fieldnames=fieldnames, **kwargs ) return csv_dict @@ -143,14 +147,18 @@ def read_csv_string_to_associative(self, csv_string, delimiter=',', fieldnames=N _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ - csv_dict = self._read_csv( - csv_string.splitlines(), - csv_reader=csv.DictReader, - delimiter=str(delimiter), - fieldnames=fieldnames, - **kwargs - ) - return csv_dict + if sys.version_info.major < 3: + csv_string = csv_string.encode("utf-8") + + with IO(csv_string) as csv_handler: + csv_dict = self._read_csv( + csv_handler, + csv_reader=csv.DictReader, + delimiter=str(delimiter), + fieldnames=fieldnames, + **kwargs + ) + return csv_dict def append_to_csv_file(self, filename, data, **kwargs): """This keyword will append data to a new or existing CSV file. @@ -182,7 +190,7 @@ def append_to_csv_file(self, filename, data, **kwargs): **kwargs ) - def append_to_csv_string(self, csv_string, data, delimiter=',', **kwargs): + def append_to_csv_string(self, csv_string, data, **kwargs): """This keyword will append data to a new or existing CSV string. - ``csv_string``: csv formatted string @@ -196,25 +204,23 @@ def append_to_csv_string(self, csv_string, data, delimiter=',', **kwargs): if isinstance(data, dict): data = [data] - fieldnames = self._read_csv( - csv_string.splitlines(), - csv_reader=csv.reader, - delimiter=str(delimiter), - **kwargs - )[0] - - kwargs['fieldnames'] = fieldnames if 'lineterminator' not in kwargs.keys(): kwargs['lineterminator'] = '\n' - with IO() as output: - if sys.version_info.major >= 3: - output.write(csv_string) - else: - output.write(csv_string.encode("utf-8")) + if sys.version_info.major < 3: + csv_string = csv_string.encode("utf-8") + + with IO(csv_string) as csv_handler: + fieldnames = self._read_csv( + csv_handler, + csv_reader=csv.reader, + **kwargs + )[0] + + kwargs['fieldnames'] = fieldnames - self._write_csv(output, data, csv_writer=csv.DictWriter, **kwargs) - return output.getvalue() + self._write_csv(csv_handler, data, csv_writer=csv.DictWriter, **kwargs) + return csv_handler.getvalue() def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=',', **kwargs): """This keyword will create new file @@ -229,14 +235,13 @@ def csv_file_from_associative(self, filename, data, fieldnames=None, delimiter=' _2_: QUOTE_NONNUMERIC _3_: QUOTE_NONE """ - fieldnames = fieldnames or data[0].keys() + kwargs['fieldnames'] = fieldnames or data[0].keys() self._open_csv_file_for_write( filename, data=data, csv_writer=csv.DictWriter, delimiter=str(delimiter), - fieldnames=fieldnames, **kwargs ) @@ -260,6 +265,6 @@ def csv_string_from_associative(self, data, fieldnames=None, delimiter=',', **kw kwargs['lineterminator'] = '\n' kwargs['delimiter'] = delimiter - with IO() as output: - self._write_csv(output, data, csv_writer=csv.DictWriter, **kwargs) - return output.getvalue() + with IO() as csv_handler: + self._write_csv(csv_handler, data, csv_writer=csv.DictWriter, **kwargs) + return csv_handler.getvalue() From 3b6199c0912af62bb611f352efcea548a3c438f5 Mon Sep 17 00:00:00 2001 From: s4int Date: Sat, 27 Nov 2021 22:57:36 +0100 Subject: [PATCH 13/13] update documentation --- doc/CSVLibrary.html | 1443 +++++++++++++++++++++++++++++++++---------- 1 file changed, 1109 insertions(+), 334 deletions(-) diff --git a/doc/CSVLibrary.html b/doc/CSVLibrary.html index 8513f91..840e053 100644 --- a/doc/CSVLibrary.html +++ b/doc/CSVLibrary.html @@ -1,165 +1,583 @@ - + + - - + + + @@ -557,7 +1111,7 @@

Opening library documentation failed

  • Verify that you have JavaScript enabled in your browser.
  • -
  • Make sure you are using a modern enough browser. Firefox 3.5, IE 8, or equivalent is required, newer browsers are recommended.
  • +
  • Make sure you are using a modern enough browser. If using Internet Explorer, version 11 is required.
  • Check are there messages in your browser's JavaScript error log. Please report the problem if you suspect you have encountered a bug.
@@ -565,26 +1119,83 @@

Opening library documentation failed

- + + + + + + + + +