From f103997352b787d79b49c198f2cc6660ba6e5d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9ger?= <olivierleger@gmail.com> Date: Thu, 22 Nov 2018 16:47:37 -0500 Subject: [PATCH 01/13] Copy all values when combining fields --- src/formpack/pack.py | 36 ++++++++++++++++++++++++++++++-- src/formpack/reporting/export.py | 2 -- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/formpack/pack.py b/src/formpack/pack.py index 3f5cfa3d..2710c7a3 100644 --- a/src/formpack/pack.py +++ b/src/formpack/pack.py @@ -202,11 +202,41 @@ def _combine_field_choices(old_field, new_field): old_choice = old_field.choice new_choice = new_field.choice except AttributeError: - return + return new_field combined_options = old_choice.options.copy() combined_options.update(new_choice.options) new_choice.options = combined_options + # Copy value_names as well. Even if some options have been deleted, + # renamed, reordered, we need to export the corresponding data. + try: + old_value_names = old_field.value_names + new_value_names = new_field.value_names + except AttributeError: + return new_field + + # We need to get the names' position of their label counterpart. + # New choices are always appended at the end in each new form version + combined_value_names = list(old_value_names) + for name in new_value_names: + if name not in old_value_names: + combined_value_names.append(name) + new_field.value_names = combined_value_names + + # We need also to merge empty results because we've just merged options + # and value_names + try: + old_empty_results = old_field.empty_results + new_empty_results = new_field.empty_results + except AttributeError: + return new_field + + combined_empty_results = old_empty_results.copy() + combined_empty_results.update(new_empty_results) + new_field.empty_results = combined_empty_results + + return new_field + def get_fields_for_versions(self, versions=-1, data_types=None): """ @@ -274,7 +304,9 @@ def get_fields_for_versions(self, versions=-1, data_types=None): # Because versions_desc are ordered from latest to oldest, # we use current field object as the old one and the one already # in position as the latest one. - self._combine_field_choices(field_object, latest_field_object) + new_object = self._combine_field_choices( + field_object, latest_field_object) + tmp2d[position[0]][position[1]] = new_object else: try: current_index_list = tmp2d[index] diff --git a/src/formpack/reporting/export.py b/src/formpack/reporting/export.py index 18a81da4..c8ab7752 100644 --- a/src/formpack/reporting/export.py +++ b/src/formpack/reporting/export.py @@ -113,7 +113,6 @@ def parse_submissions(self, submissions): except KeyError: pass - def reset(self): """ Reset sections and indexes to initial values """ @@ -222,7 +221,6 @@ def get_fields_labels_tags_for_all_versions(self, len(field.value_names) ) - names = [name for name_list in name_lists for name in name_list] # add auto fields: From a4e4240831bbd575eb9a1ce7db817b52e546ad8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9ger?= <olivierleger@gmail.com> Date: Fri, 30 Nov 2018 15:30:59 -0500 Subject: [PATCH 02/13] Refactored code according to PR review --- src/formpack/pack.py | 48 ++++++++--------------------------- src/formpack/schema/fields.py | 25 ++++++++++++++++++ 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/formpack/pack.py b/src/formpack/pack.py index 2710c7a3..ac06b8f1 100644 --- a/src/formpack/pack.py +++ b/src/formpack/pack.py @@ -23,7 +23,6 @@ class FormPack(object): - def __init__(self, versions=None, title='Submissions', id_string=None, default_version_id_key='__version__', strict_schema=False, @@ -193,47 +192,22 @@ def summr(v): @staticmethod def _combine_field_choices(old_field, new_field): - """ Update `new_field.choice` so that it contains everything from - `old_field.choice`. In the event of a conflict, `new_field.choice` - wins. If either field does not have a `choice` attribute, do - nothing + """ + Update `new_field.choice` so that it contains everything from + `old_field.choice`. In the event of a conflict, `new_field.choice` + wins. If either field does not have a `choice` attribute, do + nothing + + :param old_field: FormField + :param new_field: FormField + :return: FormField. Updated new_field """ try: old_choice = old_field.choice new_choice = new_field.choice + new_field.merge_choice(old_choice) except AttributeError: - return new_field - combined_options = old_choice.options.copy() - combined_options.update(new_choice.options) - new_choice.options = combined_options - - # Copy value_names as well. Even if some options have been deleted, - # renamed, reordered, we need to export the corresponding data. - try: - old_value_names = old_field.value_names - new_value_names = new_field.value_names - except AttributeError: - return new_field - - # We need to get the names' position of their label counterpart. - # New choices are always appended at the end in each new form version - combined_value_names = list(old_value_names) - for name in new_value_names: - if name not in old_value_names: - combined_value_names.append(name) - new_field.value_names = combined_value_names - - # We need also to merge empty results because we've just merged options - # and value_names - try: - old_empty_results = old_field.empty_results - new_empty_results = new_field.empty_results - except AttributeError: - return new_field - - combined_empty_results = old_empty_results.copy() - combined_empty_results.update(new_empty_results) - new_field.empty_results = combined_empty_results + pass return new_field diff --git a/src/formpack/schema/fields.py b/src/formpack/schema/fields.py index 0454f93e..6dae7790 100644 --- a/src/formpack/schema/fields.py +++ b/src/formpack/schema/fields.py @@ -704,12 +704,37 @@ def sum_frequencies(element): return stats + def merge_choice(self, choice): + """ + Update `new_field.choice` so that it contains everything from + `old_field.choice`. In the event of a conflict, `new_field.choice` + wins. If either field does not have a `choice` attribute, do + nothing + + :param choice: formpack.schema.datadef.FormChoice + """ + combined_options = choice.options.copy() + combined_options.update(self.choice.options) + self.choice.options = combined_options + + self._empty_result() + self.value_names = self.get_value_names() + + def _empty_result(self): + """ + Nothing to do here + """ + pass + class FormChoiceFieldWithMultipleSelect(FormChoiceField): """ Same as FormChoiceField, but you can select several answer """ def __init__(self, *args, **kwargs): super(FormChoiceFieldWithMultipleSelect, self).__init__(*args, **kwargs) + self._empty_result() + + def _empty_result(self): # reset empty result so it doesn't contain '0' self.empty_result = dict.fromkeys(self.empty_result, '') From 758a00205c90e0ad0e2ef14383b9e0c72baf939c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9ger?= <olivierleger@gmail.com> Date: Fri, 30 Nov 2018 16:30:42 -0500 Subject: [PATCH 03/13] Added a unittest --- tests/test_exports.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/test_exports.py b/tests/test_exports.py index 58657dba..4ca6213f 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -191,7 +191,6 @@ def test_export_with_choice_lists(self): '', 'traditionnel']]) - def test_headers_of_group_exports(self): title, schemas, submissions = build_fixture('grouped_questions') fp = FormPack(schemas, title) @@ -1746,3 +1745,34 @@ def test_untranslated_spss_labels(self): assert actual.read() == expected.read() zipped.close() raw_zip.close() + + def test_select_multiple_with_different_options_in_multiple_versions(self): + title, schemas, submissions = build_fixture('favorite_coffee') + fp = FormPack(schemas, title) + self.assertEqual(len(fp.versions), 2) + + export = fp.export(versions=fp.versions.keys()).to_dict(submissions) + + headers = export['Favorite coffee']['fields'] + self.assertListEqual(headers, [ + 'favorite_coffee_type', + 'favorite_coffee_type/french', + 'favorite_coffee_type/italian', + 'favorite_coffee_type/american', + 'favorite_coffee_type/british', + 'brand_of_coffee_machine' + ]) + + # Check length of each row + for row in export['Favorite coffee']['data']: + self.assertEqual(len(headers), len(row)) + + # Ensure latest submissions is not shifted + self.assertListEqual(export['Favorite coffee']['data'][-1], [ + 'american british', + '0', + '0', + '1', + '1', + 'Keurig' + ]) From 4896f59ed17ffaebf2b49c3943ee0c67cfd265d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9ger?= <olivierleger@gmail.com> Date: Fri, 30 Nov 2018 16:31:07 -0500 Subject: [PATCH 04/13] Added forgotten fixture for test --- tests/fixtures/favorite_coffee/__init__.py | 16 ++++++ tests/fixtures/favorite_coffee/v1.json | 65 ++++++++++++++++++++++ tests/fixtures/favorite_coffee/v2.json | 61 ++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 tests/fixtures/favorite_coffee/__init__.py create mode 100644 tests/fixtures/favorite_coffee/v1.json create mode 100644 tests/fixtures/favorite_coffee/v2.json diff --git a/tests/fixtures/favorite_coffee/__init__.py b/tests/fixtures/favorite_coffee/__init__.py new file mode 100644 index 00000000..66ee9955 --- /dev/null +++ b/tests/fixtures/favorite_coffee/__init__.py @@ -0,0 +1,16 @@ +# coding: utf-8 + +from __future__ import (unicode_literals, print_function, + absolute_import, division) + + +from ..load_fixture_json import load_fixture_json + +DATA = { + u'title': 'Favorite coffee', + u'id_string': 'favorite_coffee', + u'versions': [ + load_fixture_json('favorite_coffee/v1'), + load_fixture_json('favorite_coffee/v2') + ] +} diff --git a/tests/fixtures/favorite_coffee/v1.json b/tests/fixtures/favorite_coffee/v1.json new file mode 100644 index 00000000..7020b538 --- /dev/null +++ b/tests/fixtures/favorite_coffee/v1.json @@ -0,0 +1,65 @@ +{ + "id_string": "favorite_coffee", + "version": "fcv1", + "content": { + "choices": [ + { + "name": "french", + "label": [ + "French" + ], + "list_name": "al1hv46", + "order": 0 + }, + { + "name": "italian", + "label": [ + "Italian" + ], + "list_name": "al1hv46", + "order": 1 + }, + { + "name": "american", + "label": [ + "American" + ], + "list_name": "al1hv46", + "order": 2 + } + ], + "survey": [ + { + "select_from_list_name": "al1hv46", + "required": false, + "label": [ + "Favorite coffee type" + ], + "name": "favorite_coffee_type", + "type": "select_multiple" + }, + { + "required": false, + "type": "text", + "label": [ + "Brand of coffee machine" + ], + "name": "brand_of_coffee_machine" + } + ] + }, + "submissions": [ + { + "brand_of_coffee_machine": "Breville", + "favorite_coffee_type": "french italian" + }, + { + "brand_of_coffee_machine": "DeLonghi", + "favorite_coffee_type": "italian" + }, + { + "brand_of_coffee_machine": "Nespresso", + "favorite_coffee_type": "american" + } + ] +} diff --git a/tests/fixtures/favorite_coffee/v2.json b/tests/fixtures/favorite_coffee/v2.json new file mode 100644 index 00000000..f09add3f --- /dev/null +++ b/tests/fixtures/favorite_coffee/v2.json @@ -0,0 +1,61 @@ +{ + "id_string": "favorite_coffee", + "version": "fcv2", + "content": { + "choices": [ + { + "name": "french", + "label": [ + "French" + ], + "list_name": "al1hv46", + "order": 0 + }, + { + "name": "american", + "label": [ + "American" + ], + "list_name": "al1hv46", + "order": 1 + }, + { + "name": "british", + "label": [ + "British" + ], + "list_name": "al1hv46", + "order": 2 + } + ], + "survey": [ + { + "select_from_list_name": "al1hv46", + "required": false, + "label": [ + "Favorite coffee type" + ], + "name": "favorite_coffee_type", + "type": "select_multiple" + }, + { + "required": false, + "type": "text", + "label": [ + "Brand of coffee machine" + ], + "name": "brand_of_coffee_machine" + } + ] + }, + "submissions": [ + { + "brand_of_coffee_machine": "Saico", + "favorite_coffee_type": "french" + }, + { + "brand_of_coffee_machine": "Keurig", + "favorite_coffee_type": "american british" + } + ] +} From 6f39abc642920964b6fbcfae16deb4b935956ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9ger?= <olivierleger@gmail.com> Date: Mon, 17 Dec 2018 11:55:06 -0500 Subject: [PATCH 05/13] Update pyxform to v0.12 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b473e7d6..28997c47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,6 @@ jsonschema==2.6.0 lxml==4.2.1 path.py==11.0.1 pyquery==1.4.0 -pyxform==0.11.5 +pyxform==0.12.0 statistics==1.0.3.5 XlsxWriter==1.0.4 From ce24cb700b507e0391ea1e522d653a929fd99d76 Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Tue, 15 Jan 2019 18:48:28 -0500 Subject: [PATCH 06/13] Add failing test for #193 --- .../quotes_newlines_and_long_urls/__init__.py | 101 ++++++++++++++++++ tests/test_exports.py | 37 +++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/fixtures/quotes_newlines_and_long_urls/__init__.py diff --git a/tests/fixtures/quotes_newlines_and_long_urls/__init__.py b/tests/fixtures/quotes_newlines_and_long_urls/__init__.py new file mode 100644 index 00000000..29d6c21c --- /dev/null +++ b/tests/fixtures/quotes_newlines_and_long_urls/__init__.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +from __future__ import (unicode_literals, print_function, + absolute_import, division) + +''' +Quotes, newlines, and long URLs: oh, my! + +Double quotation marks need to be escaped (by doubling them!) in CSV exports, + e.g. `Introduce excerpts with "` would become `Introduce excerpts with ""`. + +Excel does not tolerate hyperlinks with URLs longer than 255 characters. Not +that we ever explicitly create Excel hyperlinks, but +https://github.com/jmcnamara/XlsxWriter tries to be helpful and add them +automatically. Excessively long URLs should just be written as strings. +''' + +DATA = { + 'title': 'Quotes, newlines, and long URLs', + 'id_string': 'quotes_newlines_and_long_urls', + 'versions': [ + { + "version": "first", + "content": { + "choices": [ + { + "label": ["yes"], + "list_name": "yes_no", + "name": "yes", + }, + { + "label": ["no"], + "list_name": "yes_no", + "name": "no", + }, + ], + "survey": [ + { + "required": False, + "appearance": "multiline", + "name": "Enter_some_long_text_and_linebreaks_here", + "label": [ + "Enter some long text with \" and linebreaks here" + ], + "type": "text", + }, + { + "select_from_list_name": "yes_no", + "required": False, + "name": "Some_other_question", + "label": ["Some other question"], + "type": "select_one", + }, + ], + }, + 'submissions': [ + { + "Enter_some_long_text_and_linebreaks_here": + "Check out this URL I found:\n" + "https://now.read.this/?Never%20forget%20that%20you%20" + "are%20one%20of%20a%20kind.%20Never%20forget%20that%20" + "if%20there%20weren%27t%20any%20need%20for%20you%20in" + "%20all%20your%20uniqueness%20to%20be%20on%20this%20" + "earth%2C%20you%20wouldn%27t%20be%20here%20in%20the%20" + "first%20place.%20And%20never%20forget%2C%20no%20" + "matter%20how%20overwhelming%20life%27s%20challenges" + "%20and%20problems%20seem%20to%20be%2C%20that%20one%20" + "person%20can%20make%20a%20difference%20in%20the%20" + "world.%20In%20fact%2C%20it%20is%20always%20because%20" + "of%20one%20person%20that%20all%20the%20changes%20that" + "%20matter%20in%20the%20world%20come%20about.%20So%20" + "be%20that%20one%20person.", + "Some_other_question": "yes", + }, + # Thanks to @tinok for the whimisical sample data below + { + "Enter_some_long_text_and_linebreaks_here": + "Hi, my name is Roger.\"\n\nI like to enter quotes " + "randomly and follow them with new lines.", + "Some_other_question": "yes", + }, + { + "Enter_some_long_text_and_linebreaks_here": + "This one has no linebreaks", + "Some_other_question": "no", + }, + { + "Enter_some_long_text_and_linebreaks_here": + "This\nis\nnot\na Haiku", + "Some_other_question": "yes", + }, + { + "Enter_some_long_text_and_linebreaks_here": + "\"Hands up!\" He yelled.\nWhy?\"\nShe couldn't " + "understand anything.", + "Some_other_question": "yes", + }, + ], + }, + ], +} diff --git a/tests/test_exports.py b/tests/test_exports.py index 4ca6213f..3a9898f9 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -1107,6 +1107,43 @@ def test_csv(self): self.assertTextEqual(csv_data, expected) + def test_csv_quote_escaping(self): + title, schemas, submissions = build_fixture( + 'quotes_newlines_and_long_urls') + fp = FormPack(schemas, title) + lss = list(submissions) + csv_lines = list(fp.export().to_csv(submissions)) + expected_lines = [] + expected_lines.append( + '"Enter_some_long_text_and_linebreaks_here";' + '"Some_other_question"' + ) + expected_lines.append( + '"Check out this URL I found:\nhttps://now.read.this/?Never%20forg' + 'et%20that%20you%20are%20one%20of%20a%20kind.%20Never%20forget%20t' + 'hat%20if%20there%20weren%27t%20any%20need%20for%20you%20in%20all%' + '20your%20uniqueness%20to%20be%20on%20this%20earth%2C%20you%20woul' + 'dn%27t%20be%20here%20in%20the%20first%20place.%20And%20never%20fo' + 'rget%2C%20no%20matter%20how%20overwhelming%20life%27s%20challenge' + 's%20and%20problems%20seem%20to%20be%2C%20that%20one%20person%20ca' + 'n%20make%20a%20difference%20in%20the%20world.%20In%20fact%2C%20it' + '%20is%20always%20because%20of%20one%20person%20that%20all%20the%2' + '0changes%20that%20matter%20in%20the%20world%20come%20about.%20So%' + '20be%20that%20one%20person.";"yes"' + ) + expected_lines.append( + '"Hi, my name is Roger.""\n\nI like to enter quotes randomly and ' + 'follow them with new lines.";"yes"' + ) + expected_lines.append('"This one has no linebreaks";"no"') + expected_lines.append('"This\nis\nnot\na Haiku";"yes"') + expected_lines.append( + '"""Hands up!"" He yelled.\nWhy?""\n' + '''She couldn't understand anything.";"yes"''' + ) + + self.assertListEqual(csv_lines, expected_lines) + def test_csv_with_tag_headers(self): title, schemas, submissions = build_fixture('dietary_needs') fp = FormPack(schemas, title) From d5e791d9715db22adf5e6f1f3d432e19028a3913 Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 00:26:46 -0500 Subject: [PATCH 07/13] Escape `quote` during CSV exports by doubling it. Fixes #193 --- src/formpack/reporting/export.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/formpack/reporting/export.py b/src/formpack/reporting/export.py index c8ab7752..86de9575 100644 --- a/src/formpack/reporting/export.py +++ b/src/formpack/reporting/export.py @@ -427,8 +427,22 @@ def to_csv(self, submissions, sep=";", quote='"'): # if len(sections) > 1: # raise RuntimeError("CSV export does not support repeatable groups") + def escape_quote(value, quote): + ''' + According to https://www.ietf.org/rfc/rfc4180.txt, + + If double-quotes are used to enclose fields, then a + double-quote appearing inside a field must be escaped by + preceding it with another double quote. + + We will follow this convention by doubling `quote` wherever it + appears in `value`, regardless of what `quote` is. Perhaps this + is not the best idea. + ''' + return value.replace(quote, quote * 2) + def format_line(line, sep, quote): - line = [unicode(x) for x in line] + line = [escape_quote(unicode(x), quote) for x in line] return quote + (quote + sep + quote).join(line) + quote section, labels = sections[0] From 9fed37f85a7bc53d60570aee05d38a3caf8cbdb0 Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 00:40:10 -0500 Subject: [PATCH 08/13] Upgrade dev requirements to fix(?) Travis failure --- dev-requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c919e984..96b724c0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,5 @@ -coverage==4.5.1 +coverage==4.5.2 nose==1.3.7 -tox==2.9.1 -flake8==3.5.0 -pytest==3.4.0 +tox==3.7.0 +flake8==3.6.0 +pytest==4.1.1 From f18bbc1f69bba6c0543347c5a8f37636486ffe1c Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 00:47:57 -0500 Subject: [PATCH 09/13] Move Travis dependencies to dev requirements --- .travis.yml | 2 -- dev-requirements.txt | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce41bbd9..df781fc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ language: python python: - "2.7" install: - - "pip install funcsigs" - - "pip install pytest pytest-cov python-coveralls" - "python setup.py develop" script: - "py.test tests --cov=src -vv" diff --git a/dev-requirements.txt b/dev-requirements.txt index 96b724c0..c0d153a8 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,3 +3,6 @@ nose==1.3.7 tox==3.7.0 flake8==3.6.0 pytest==4.1.1 +funcsigs==1.0.2 +pytest-cov==2.6.1 +python-coveralls==2.9.1 From b3afdfcf2ddf89052999acc557eac5fa3b553150 Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 01:02:21 -0500 Subject: [PATCH 10/13] Switch from python-coveralls to coveralls-python --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c0d153a8..50ba3e08 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,4 +5,4 @@ flake8==3.6.0 pytest==4.1.1 funcsigs==1.0.2 pytest-cov==2.6.1 -python-coveralls==2.9.1 +coveralls==1.5.1 From 684d301e2e7cae1bae0f91533dd0d64c069fd2b7 Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 01:07:30 -0500 Subject: [PATCH 11/13] Remove obsolete `.` from pytest invocation --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index df781fc0..c8621f8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,6 @@ python: install: - "python setup.py develop" script: - - "py.test tests --cov=src -vv" + - "pytest tests --cov=src -vv" after_success: - coveralls From a18db3551a0426e166960a40ea2855646b86a81e Mon Sep 17 00:00:00 2001 From: "John N. Milner" <john@tmoj.net> Date: Wed, 16 Jan 2019 01:15:42 -0500 Subject: [PATCH 12/13] Explicitly uninstall Travis' old version of pytest --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index c8621f8e..fa71c587 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,12 @@ language: python python: - "2.7" install: + # Get rid of Travis' pytest 3.3.0, which somehow persists even though our + # requirements pin a newer version! + - "pip uninstall --yes pytest" - "python setup.py develop" script: + - "pip freeze" - "pytest tests --cov=src -vv" after_success: - coveralls From bf52b878520d72e375bbf5284c11d8e7e1eab8ef Mon Sep 17 00:00:00 2001 From: jnm <john@tmoj.net> Date: Thu, 17 Jan 2019 13:23:16 -0500 Subject: [PATCH 13/13] Remove debugging statement from Travis config --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa71c587..e920e18e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ install: - "pip uninstall --yes pytest" - "python setup.py develop" script: - - "pip freeze" - "pytest tests --cov=src -vv" after_success: - coveralls