From 6cd74a99660489e907069c4184c47bbfab7628ca Mon Sep 17 00:00:00 2001 From: "Fabien C. Y. Benureau" Date: Thu, 17 May 2018 09:22:27 +0900 Subject: [PATCH 01/18] error message when editor is missing simplified a bit the ui code, to remove unecessary out-of-class functions (needed anyway because of ui.error calls). --- pubs/uis.py | 64 ++++++++++++++++++++++++------------------- tests/fake_env.py | 9 +++--- tests/test_usecase.py | 14 ++++++---- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/pubs/uis.py b/pubs/uis.py index c2a8bfc..aae0052 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -43,32 +43,6 @@ def _get_local_editor(): return os.environ.get('EDITOR', 'nano') -def _editor_input(editor, initial='', suffix='.tmp'): - """Use an editor to get input""" - str_initial = initial.encode('utf-8') # TODO: make it a configuration item - with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp_file: - tfile_name = temp_file.name - temp_file.write(str_initial) - cmd = shlex.split(editor) # this enable editor command with option, e.g. gvim -f - cmd.append(tfile_name) - subprocess.call(cmd) - content = read_text_file(tfile_name) - os.remove(tfile_name) - return content - - -def _edit_file(editor, path_to_file, temporary=True): - if temporary: - check_file(path_to_file, fail=True) - content = read_text_file(path_to_file) - content = _editor_input(editor, content) - write_file(path_to_file, content) - else: - cmd = editor.split() # this enable editor command with option, e.g. gvim -f - cmd.append(system_path(path_to_file)) - subprocess.call(cmd) - - def get_ui(): if _ui is None: return PrintUI(config.load_default_conf()) # no editor support. (#FIXME?) @@ -223,7 +197,41 @@ class InputUI(PrintUI): return [True, False][answer] def editor_input(self, initial="", suffix='.tmp'): - return _editor_input(self.editor, initial=initial, suffix=suffix) + """Use an editor to get input""" + str_initial = initial.encode('utf-8') # TODO: make it a configuration item + with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp_file: + tfile_name = temp_file.name + temp_file.write(str_initial) + self._call_editor(tfile_name) + content = read_text_file(tfile_name) + os.remove(tfile_name) + return content def edit_file(self, path, temporary): - _edit_file(self.editor, path, temporary=temporary) + if temporary: + check_file(path, fail=True) + content = read_text_file(path) + content = self.editor_input(content) + write_file(path, content) + else: + self._call_editor(path) + + def _call_editor(self, path): + """Call the editor, and checks that no error were raised by the OS""" + cmd = shlex.split(self.editor) # this enable editor command with option, e.g. gvim -f + cmd.append(path) + try: + subprocess.call(cmd) + except OSError as e: + if e.errno == os.errno.ENOENT: + self.error(("Error while calling editor '{}'. The editor may " + "not be present. You can change the text editor " + "that pubs uses by setting the $EDITOR environment " + "variable, or by running `pubs conf` and setting " + "the `edit_cmd` field." + ).format(self.editor)) + # handle file not found error. + self.exit() + else: + # Something else went wrong while trying to run `wget` + self.handle_exception(e) diff --git a/tests/fake_env.py b/tests/fake_env.py index 4f8af1b..2e2a5c7 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -74,12 +74,11 @@ class FakeInput(): def as_global(self): for md in self.module_list: md.input = self - md._editor_input = self - md._edit_file = self.input_to_file - # if mdname.endswith('files'): - # md.editor_input = self + if md.__name__ == 'pubs.uis': + md.InputUI.editor_input = self + md.InputUI.edit_file = self.input_to_file - def input_to_file(self, _, path_to_file, temporary=True): + def input_to_file(self, path_to_file, temporary=True): content.write_file(path_to_file, self()) def add_input(self, inp): diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 7d98492..61d4380 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -45,9 +45,8 @@ class FakeSystemExit(Exception): "Exited with code: {}.".format(self.code), *args) -# code for fake fs - -class TestFakeInput(unittest.TestCase): +class TestInput(unittest.TestCase): + """Test that the fake input mechanisms work correctly in the tests""" def test_input(self): input = fake_env.FakeInput(['yes', 'no']) @@ -65,13 +64,16 @@ class TestFakeInput(unittest.TestCase): color.input() def test_editor_input(self): + sample_conf = conf.load_default_conf() + ui = uis.InputUI(sample_conf) + other_input = fake_env.FakeInput(['yes', 'no'], module_list=[uis, color]) other_input.as_global() - self.assertEqual(uis._editor_input(), 'yes') - self.assertEqual(uis._editor_input(), 'no') + self.assertEqual(ui.editor_input(), 'yes') + self.assertEqual(ui.editor_input(), 'no') with self.assertRaises(fake_env.FakeInput.UnexpectedInput): - color.input() + ui.editor_input() class CommandTestCase(fake_env.TestFakeFs): From 4b1717b7fddcec28941ba9591cec95254becb7ee Mon Sep 17 00:00:00 2001 From: "Fabien C. Y. Benureau" Date: Sun, 10 Jun 2018 19:05:17 +0900 Subject: [PATCH 02/18] dialog after editing paper --- pubs/commands/edit_cmd.py | 10 +++++++++- pubs/repo.py | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pubs/commands/edit_cmd.py b/pubs/commands/edit_cmd.py index 11d7bfc..b09c885 100644 --- a/pubs/commands/edit_cmd.py +++ b/pubs/commands/edit_cmd.py @@ -54,10 +54,17 @@ def command(conf, args): new_paper = Paper(paper.citekey, paper.bibdata, metadata=content) rp.push_paper(new_paper, overwrite=True, event=False) + ui.info(('The metadata of paper `{}` was successfully ' + 'edited.'.format(citekey))) else: new_paper = Paper.from_bibentry(content, metadata=paper.metadata) - rp.rename_paper(new_paper, old_citekey=paper.citekey) + if rp.rename_paper(new_paper, old_citekey=paper.citekey): + ui.info(('Paper `{}` was successfully edited and renamed ' + 'as `{}`.'.format(citekey, new_paper.citekey))) + else: + ui.info(('Paper `{}` was successfully edited.'.format( + citekey))) break except repo.CiteKeyCollision: @@ -71,6 +78,7 @@ def command(conf, args): break elif choice == 'overwrite': paper = rp.push_paper(paper, overwrite=True) + ui.info(('Paper `{}` was overwritten.'.format(citekey))) break # else edit again # Also handle malformed bibtex and metadata diff --git a/pubs/repo.py b/pubs/repo.py index fd942b0..51cd666 100644 --- a/pubs/repo.py +++ b/pubs/repo.py @@ -141,6 +141,13 @@ class Repository(object): pass def rename_paper(self, paper, new_citekey=None, old_citekey=None): + """Move a paper from a citekey to another one. + + Even if the new and old citekey are the same, the paper instance is + pushed to disk. + + :return: True if a rename happened, False if not. + """ if old_citekey is None: old_citekey = paper.citekey if new_citekey is None: @@ -149,6 +156,7 @@ class Repository(object): # check if new_citekey is not the same as paper.citekey if old_citekey == new_citekey: self.push_paper(paper, overwrite=True, event=False) + return False else: # check if new_citekey does not exists if new_citekey in self: @@ -171,6 +179,7 @@ class Repository(object): self.remove_paper(old_citekey, event=False) # send event events.RenameEvent(paper, old_citekey).send() + return True def push_doc(self, citekey, docfile, copy=None): p = self.pull_paper(citekey) From 4dc69bcfc238f7a1b1b07449a5848d066d5f0275 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Tue, 24 Jul 2018 11:01:06 +0200 Subject: [PATCH 03/18] Use vi as a (saner) default. Fixes #140. --- pubs/uis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubs/uis.py b/pubs/uis.py index aae0052..d5ddd88 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -38,9 +38,9 @@ def _get_encoding(conf): def _get_local_editor(): """Get the editor from environment variables. - Use nano as a default. + Use vi as a default. """ - return os.environ.get('EDITOR', 'nano') + return os.environ.get('EDITOR', 'vi') def get_ui(): From 8fe650c0407c77797ec806370f18e4c6bc92afad Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 25 Jul 2018 12:29:44 +0200 Subject: [PATCH 04/18] Use join for joining paths. --- tests/test_usecase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 8091f69..d5eea53 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -355,8 +355,8 @@ class TestList(DataCommandTestCase): def test_list_several_no_date(self): self.execute_cmds(['pubs init -p testrepo']) - os.chdir('/') # weird fix for shutil.rmtree invocation. - shutil.rmtree(self.rootpath + '/testrepo') + os.chdir('/') # weird fix for shutil.rmtree invocation. + shutil.rmtree(os.path.join(self.rootpath, 'testrepo')) os.chdir(self.rootpath) self.fs.add_real_directory(os.path.join(self.rootpath, 'testrepo'), read_only=False) From 2f48f37199de5d65883c07b0626be95fe290f11a Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 25 Jul 2018 18:30:26 +0200 Subject: [PATCH 05/18] Fixes duplicated test names in same TestCase (1st was not run). --- tests/test_endecoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_endecoder.py b/tests/test_endecoder.py index 80a9f53..ab8dc07 100644 --- a/tests/test_endecoder.py +++ b/tests/test_endecoder.py @@ -52,7 +52,7 @@ class TestEnDecode(unittest.TestCase): self.assertEqual(bibraw1, bibraw2) - def test_endecode_bibtex(self): + def test_endecode_bibtex_converts_month_string(self): """Test if `month=dec` is correctly recognized and transformed into `month={December}`""" decoder = endecoder.EnDecoder() From d8dc386a182285d64fdd21d1123bcdab4b94d81a Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 25 Jul 2018 19:53:55 +0200 Subject: [PATCH 06/18] Fix fake input behavior in tests on unexpected input. Because of the mechanism for catching exceptions in pubs, the UnexpectedInput exception raised by FakeInput never reached the catch statement in the CommandTestCase and raised a FakeSystemExit instead. This commit monkey-patches the exception handler in the ui at the same time as the patching of the input functions to ignore UnexptectedInput at the ui level. --- tests/fake_env.py | 10 ++++++++++ tests/test_usecase.py | 10 ++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/fake_env.py b/tests/fake_env.py index 2e2a5c7..2bc9a51 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -77,6 +77,16 @@ class FakeInput(): if md.__name__ == 'pubs.uis': md.InputUI.editor_input = self md.InputUI.edit_file = self.input_to_file + # Do not catch UnexpectedInput + original_handler = md.InputUI.handle_exception + + def handler(ui, exc): + if isinstance(exc, self.UnexpectedInput): + raise + else: + original_handler(ui, exc) + + md.InputUI.handle_exception = handler def input_to_file(self, path_to_file, temporary=True): content.write_file(path_to_file, self()) diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 4f3d805..bdc1de5 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -131,7 +131,7 @@ class CommandTestCase(fake_env.TestFakeFs): outs.append(color.undye(actual_out)) else: pubs_cmd.execute(actual_cmd.split()) - except fake_env.FakeInput.UnexpectedInput as e: + except fake_env.FakeInput.UnexpectedInput: self.fail('Unexpected input asked by command: {}.'.format( actual_cmd)) return outs @@ -200,7 +200,7 @@ class TestAlone(CommandTestCase): def test_alone_misses_command(self): with self.assertRaises(FakeSystemExit) as cm: self.execute_cmds(['pubs']) - self.assertEqual(cm.exception.code, 2) + self.assertEqual(cm.exception.code, 2) def test_alone_prints_help(self): # capturing the output of `pubs --help` is difficult because argparse @@ -682,13 +682,11 @@ class TestUsecase(DataCommandTestCase): self.assertFileContentEqual(os.path.expanduser('~/.pubsrc'), str_fixtures.sample_conf) - def test_editor_abort(self): + def test_editor_aborts(self): with self.assertRaises(FakeSystemExit): cmds = ['pubs init', - ('pubs add', ['abc', 'n']), - ('pubs add', ['abc', 'y', 'abc', 'n']), 'pubs add data/pagerank.bib', - ('pubs edit Page99', ['', 'a']), + ('pubs edit Page99', ['', 'n']), ] self.execute_cmds(cmds) From 8a7d1432617c6477f46f1c38cb0342e13c037182 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 25 Jul 2018 22:20:14 +0200 Subject: [PATCH 07/18] Improves behaviors related to bibtex decoding error. - from editor input in add and edit commands, - from files in import command. --- pubs/commands/add_cmd.py | 8 +++++--- pubs/commands/edit_cmd.py | 7 ++++++- pubs/commands/import_cmd.py | 12 +++++++++--- pubs/endecoder.py | 18 ++++++++++++++++-- pubs/uis.py | 3 ++- tests/test_apis.py | 4 ++-- tests/test_endecoder.py | 5 +++++ tests/test_usecase.py | 37 ++++++++++++++++++++++++++++++++++++- 8 files changed, 81 insertions(+), 13 deletions(-) diff --git a/pubs/commands/add_cmd.py b/pubs/commands/add_cmd.py index a22c0a5..d193b5e 100644 --- a/pubs/commands/add_cmd.py +++ b/pubs/commands/add_cmd.py @@ -11,6 +11,7 @@ from .. import templates from .. import apis from .. import pretty from .. import utils +from .. import endecoder from ..completion import CommaSeparatedTagsCompletion @@ -57,12 +58,13 @@ def bibentry_from_editor(conf, ui, rp): bibstruct.verify_bibdata(bibentry) # REFACTOR Generate citykey again = False - except ValueError: + + except endecoder.EnDecoder.BibDecodingError: again = ui.input_yn( - question='Invalid bibfile. Edit again ?', + question='Invalid bibfile. Edit again?', default='y') if not again: - ui.exit(0) + ui.exit() return bibentry diff --git a/pubs/commands/edit_cmd.py b/pubs/commands/edit_cmd.py index b09c885..8cc175b 100644 --- a/pubs/commands/edit_cmd.py +++ b/pubs/commands/edit_cmd.py @@ -64,9 +64,14 @@ def command(conf, args): 'as `{}`.'.format(citekey, new_paper.citekey))) else: ui.info(('Paper `{}` was successfully edited.'.format( - citekey))) + citekey))) break + except coder.BibDecodingError: + if not ui.input_yn(question="Error parsing bibdata. Edit again?"): + ui.error("Aborting, paper not updated.") + ui.exit() + except repo.CiteKeyCollision: options = ['overwrite', 'edit again', 'abort'] choice = options[ui.input_choice( diff --git a/pubs/commands/import_cmd.py b/pubs/commands/import_cmd.py index 9ccb935..05279b2 100644 --- a/pubs/commands/import_cmd.py +++ b/pubs/commands/import_cmd.py @@ -27,7 +27,7 @@ def parser(subparsers, conf): return parser -def many_from_path(bibpath): +def many_from_path(ui, bibpath): """Extract list of papers found in bibliographic files in path. The behavior is to: @@ -49,11 +49,17 @@ def many_from_path(bibpath): biblist = [] for filepath in all_files: - biblist.append(coder.decode_bibdata(read_text_file(filepath))) + try: + biblist.append(coder.decode_bibdata(read_text_file(filepath))) + except coder.BibDecodingError: + ui.error("Could not parse bibtex at {}. Aborting import.".format(filepath)) + ui.exit() papers = {} for b in biblist: for k, b in b.items(): + if k in papers: + ui.warning('Duplicated citekey {}. Keeping last.'.format(k)) try: papers[k] = Paper(k, b) papers[k].added = datetime.datetime.now() @@ -75,7 +81,7 @@ def command(conf, args): rp = repo.Repository(conf) # Extract papers from bib - papers = many_from_path(bibpath) + papers = many_from_path(ui, bibpath) keys = args.keys or papers.keys() for k in keys: p = papers[k] diff --git a/pubs/endecoder.py b/pubs/endecoder.py index 8fd6941..259b8d9 100644 --- a/pubs/endecoder.py +++ b/pubs/endecoder.py @@ -66,6 +66,16 @@ class EnDecoder(object): * encode_bibdata will try to recognize exceptions """ + class BibDecodingError(Exception): + + message = "Could not parse provided bibdata:\n---\n{}\n---" + + def __init__(self, bibdata): + self.data = bibdata + + def __str__(self): + return self.message.format(self.data) + bwriter = bp.bwriter.BibTexWriter() bwriter.display_order = BIBFIELD_ORDER @@ -103,7 +113,10 @@ class EnDecoder(object): return entry def decode_bibdata(self, bibdata): - """""" + """Decodes bibdata from string. + + If the decoding fails, returns a BibParseError. + """ try: entries = bp.bparser.BibTexParser( bibdata, common_strings=True, @@ -121,4 +134,5 @@ class EnDecoder(object): except Exception: import traceback traceback.print_exc() - raise ValueError('could not parse provided bibdata:\n{}'.format(bibdata)) + raise self.BibDecodingError(bibdata) + # TODO: filter exceptions from pyparsing and pass reason upstream diff --git a/pubs/uis.py b/pubs/uis.py index d5ddd88..3c9aac6 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -104,6 +104,7 @@ class PrintUI(object): self.exit() return True # never happens + class InputUI(PrintUI): """UI class. Stores configuration parameters and system information. """ @@ -118,7 +119,7 @@ class InputUI(PrintUI): except EOFError: self.error('Standard input ended while waiting for answer.') self.exit(1) - return ustr(data) #.decode('utf-8') + return ustr(data) #.decode('utf-8') def input_choice_ng(self, options, option_chars=None, default=None, question=''): """Ask the user to chose between a set of options. The user is asked diff --git a/tests/test_apis.py b/tests/test_apis.py index 087f32f..a83481a 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -32,7 +32,7 @@ class TestDOI2Bibtex(unittest.TestCase): def test_parse_fails_on_incorrect_DOI(self): bib = doi2bibtex('999999') - with self.assertRaises(ValueError): + with self.assertRaises(EnDecoder.BibDecodingError): self.endecoder.decode_bibdata(bib) @@ -56,7 +56,7 @@ class TestISBN2Bibtex(unittest.TestCase): def test_parse_fails_on_incorrect_ISBN(self): bib = doi2bibtex('9' * 13) - with self.assertRaises(ValueError): + with self.assertRaises(EnDecoder.BibDecodingError): self.endecoder.decode_bibdata(bib) diff --git a/tests/test_endecoder.py b/tests/test_endecoder.py index ab8dc07..a6e3043 100644 --- a/tests/test_endecoder.py +++ b/tests/test_endecoder.py @@ -147,6 +147,11 @@ class TestEnDecode(unittest.TestCase): self.assertIn('author', entry1) self.assertIn('institution', entry1) + def test_endecodes_raises_exception(self): + decoder = endecoder.EnDecoder() + with self.assertRaises(decoder.BibDecodingError): + decoder.decode_bibdata("@misc{I am not a correct bibtex{{}") + if __name__ == '__main__': unittest.main() diff --git a/tests/test_usecase.py b/tests/test_usecase.py index bdc1de5..d60bdb2 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -337,11 +337,20 @@ class TestAdd(URLContentTestCase): def test_add_no_citekey_fails(self): # See #113 cmds = ['pubs init', - ('pubs add', [str_fixtures.bibtex_no_citekey]), + ('pubs add', [str_fixtures.bibtex_no_citekey, 'n']), ] with self.assertRaises(FakeSystemExit): self.execute_cmds(cmds) + def test_add_edit_fails(self): + cmds = ['pubs init', + ('pubs add', + ['@misc{I am not a correct bibtex{{}', 'n']), + ] + with self.assertRaises(FakeSystemExit) as cm: + self.execute_cmds(cmds) + self.assertEqual(cm.exception.code, 1) + class TestList(DataCommandTestCase): @@ -690,6 +699,32 @@ class TestUsecase(DataCommandTestCase): ] self.execute_cmds(cmds) + def test_editor_succeeds_on_second_edit(self): + cmds = ['pubs init', + 'pubs add data/pagerank.bib', + ('pubs edit Page99', [ + '', 'y', + '@misc{Page99, title="TTT", author="X. YY"}', '']), + ('pubs list', [], '[Page99] YY, X. "TTT" \n') + ] + self.execute_cmds(cmds) + + def test_add_aborts(self): + with self.assertRaises(FakeSystemExit): + cmds = ['pubs init', + ('pubs add New', ['']), + ] + self.execute_cmds(cmds) + + def test_add_succeeds_on_second_edit(self): + cmds = ['pubs init', + ('pubs add', [ + '', 'y', + '@misc{New, title="TTT", author="X. YY"}', '']), + ('pubs list', [], '[New] YY, X. "TTT" \n') + ] + self.execute_cmds(cmds) + def test_editor_success(self): cmds = ['pubs init', ('pubs add', [str_fixtures.bibtex_external0]), From ea711b6b426b0b46607f4d21dd8491163c07fcef Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 25 Jul 2018 22:51:22 +0200 Subject: [PATCH 08/18] [Fix #87] Include citekey in message on bibtex decoding error from repository. Catches decoding error at databroker level to include citekey in message. Could be improved by a better exception class for BibDecodingError. --- pubs/databroker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pubs/databroker.py b/pubs/databroker.py index d529c3e..97c1398 100644 --- a/pubs/databroker.py +++ b/pubs/databroker.py @@ -45,7 +45,11 @@ class DataBroker(object): def pull_bibentry(self, citekey): bibdata_raw = self.filebroker.pull_bibfile(citekey) - return self.endecoder.decode_bibdata(bibdata_raw) + try: + return self.endecoder.decode_bibdata(bibdata_raw) + except self.endecoder.BibDecodingError as e: + e.message = "Unable to decode bibtex for paper {}.".format(citekey) + raise e def push_metadata(self, citekey, metadata): metadata_raw = self.endecoder.encode_metadata(metadata) From 7518dfd56aee97784dca3aac5310ff783393e0c8 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Thu, 26 Jul 2018 16:25:30 +0200 Subject: [PATCH 09/18] [Fix #85] Adds changelog file. --- changelog.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 changelog.md diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..0b0f0af --- /dev/null +++ b/changelog.md @@ -0,0 +1,68 @@ +# Changelog + + +## Current master + +[Full Changelog](https://github.com/pubs/pubs/compare/v0.7.0...master) + + +### Implemented enhancements + +- Better dialog after editing paper [(#142)](https://github.com/pubs/pubs/issues/142) + +- Add a command to open urls [(#139)](https://github.com/pubs/pubs/issues/139) + +- More robust cache on version change [(#138)](https://github.com/pubs/pubs/issues/138) + +- Allow utf8 citekeys [(#133)](https://github.com/pubs/pubs/issues/133) + +- Adds tag list completion in `pubs add -t ` [(#130)](https://github.com/pubs/pubs/issues/130) + +- Wider Travis coverage ([#107](https://github.com/pubs/pubs/issues/107) and [#108](https://github.com/pubs/pubs/issues/108)) + +- Uses bibtexparser bwriter instead of internal encoder and adds `--ignore-fields` option to export. [(#106)](https://github.com/pubs/pubs/issues/106) + +- Configurable alias descriptions [(#104)](https://github.com/pubs/pubs/issues/104) + +- Support year ranges in query [(#102)](https://github.com/pubs/pubs/issues/102) + + +### Fixed bugs + +- [[#95]](https://github.com/pubs/pubs/issues/95) Error message when editor is missing [(#141)](https://github.com/pubs/pubs/issues/141) + +- Fixes tests for printing help on `--help` and without argument. [(#137)](https://github.com/pubs/pubs/issues/137) + +- [[#126]](https://github.com/pubs/pubs/issues/126) Removes journal customization [(#127)](https://github.com/pubs/pubs/issues/127) + +- Fixes Travis failure on installing python3 for OSX [(#125)](https://github.com/pubs/pubs/issues/125) + +- [[#119]](https://github.com/pubs/pubs/issues/119) Removes link and DOI customization. [(#124)](https://github.com/pubs/pubs/issues/124) + +- [[#122]](https://github.com/pubs/pubs/issues/122) Fixes common strings [(#123)](https://github.com/pubs/pubs/issues/123) + +- [[#28]](https://github.com/pubs/pubs/issues/28) allow utf8 in citekeys [(#120)](https://github.com/pubs/pubs/issues/120) + +- Fixes field orders to use 'url' and fixes broken test. [(#118)](https://github.com/pubs/pubs/issues/118) + +- [[#25]](https://github.com/pubs/pubs/issues/25) Fix bibtex testcase [(#117)](https://github.com/pubs/pubs/issues/117) + +- [[#103]](https://github.com/pubs/pubs/issues/103) Fixes unicode comparison [(#116)](https://github.com/pubs/pubs/issues/116) + +- [[#95]](https://github.com/pubs/pubs/issues/95) robust handling of DOIs [(#105)](https://github.com/pubs/pubs/issues/105) + +- [[#99]](https://github.com/pubs/pubs/issues/99) Print help when no subcommand is provided [(#100)](https://github.com/pubs/pubs/issues/100) + +- Fix defaults not used in config. [(#97)](https://github.com/pubs/pubs/issues/97) + +- Fixes content not read from urls because of call to `os.abspath` [(#96)](https://github.com/pubs/pubs/issues/96) + +- [[#93]](https://github.com/pubs/pubs/issues/93) actually save the modifications on `edit -m`. [(#94)](https://github.com/pubs/pubs/issues/94) + +- [[#88]](https://github.com/pubs/pubs/issues/88) Adds proper escaping for +arguments in alias plugin. [(#91)](https://github.com/pubs/pubs/issues/91) + + +## [v0.7.0](https://github.com/pubs/pubs/compare/v0.6.0...v0.7.0) (2017-08-06) + +[Full Changelog](https://github.com/pubs/pubs/compare/v0.6.0...v0.7.0) From 754e9268938d25a0312e12d3e5e6706bcd26e08d Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Mon, 30 Jul 2018 17:27:12 +0200 Subject: [PATCH 10/18] Adds authors of PRs to changelog and missing ksunden to readme. --- changelog.md | 8 ++++---- readme.md | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 0b0f0af..957364f 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,7 @@ - Better dialog after editing paper [(#142)](https://github.com/pubs/pubs/issues/142) -- Add a command to open urls [(#139)](https://github.com/pubs/pubs/issues/139) +- Add a command to open urls ([#139](https://github.com/pubs/pubs/issues/139) *by [ksunden](https://github.com/ksunden)*) - More robust cache on version change [(#138)](https://github.com/pubs/pubs/issues/138) @@ -22,7 +22,7 @@ - Uses bibtexparser bwriter instead of internal encoder and adds `--ignore-fields` option to export. [(#106)](https://github.com/pubs/pubs/issues/106) -- Configurable alias descriptions [(#104)](https://github.com/pubs/pubs/issues/104) +- Configurable alias descriptions ([#104](https://github.com/pubs/pubs/issues/104) by [wflynny](https://github.com/wflynny)) - Support year ranges in query [(#102)](https://github.com/pubs/pubs/issues/102) @@ -49,9 +49,9 @@ - [[#103]](https://github.com/pubs/pubs/issues/103) Fixes unicode comparison [(#116)](https://github.com/pubs/pubs/issues/116) -- [[#95]](https://github.com/pubs/pubs/issues/95) robust handling of DOIs [(#105)](https://github.com/pubs/pubs/issues/105) +- [[#95]](https://github.com/pubs/pubs/issues/95) robust handling of DOIs ([#105](https://github.com/pubs/pubs/issues/105) by [wflynny](https://github.com/wflynny)) -- [[#99]](https://github.com/pubs/pubs/issues/99) Print help when no subcommand is provided [(#100)](https://github.com/pubs/pubs/issues/100) +- [[#99]](https://github.com/pubs/pubs/issues/99) Print help when no subcommand is provided ([#100](https://github.com/pubs/pubs/issues/100) by [wflynny](https://github.com/wflynny)) - Fix defaults not used in config. [(#97)](https://github.com/pubs/pubs/issues/97) diff --git a/readme.md b/readme.md index 1129229..4b03dcd 100644 --- a/readme.md +++ b/readme.md @@ -125,3 +125,4 @@ You can access the self-documented configuration by using `pubs conf`, and all t - [Tyler Earnest](https://github.com/tmearnest) - [Dennis Wilson](https://github.com/d9w) - [Bill Flynn](https://github.com/wflynny) +- [ksunden](https://github.com/ksunden) From 60650b874a7db5b2839dedb761fd7d97e5bd2f7c Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Mon, 30 Jul 2018 17:46:04 +0200 Subject: [PATCH 11/18] Addresses minor comments (error message and better test). --- pubs/commands/import_cmd.py | 2 +- tests/test_usecase.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pubs/commands/import_cmd.py b/pubs/commands/import_cmd.py index 05279b2..53d5b4c 100644 --- a/pubs/commands/import_cmd.py +++ b/pubs/commands/import_cmd.py @@ -59,7 +59,7 @@ def many_from_path(ui, bibpath): for b in biblist: for k, b in b.items(): if k in papers: - ui.warning('Duplicated citekey {}. Keeping last.'.format(k)) + ui.warning('Duplicated citekey {}. Keeping the last one.'.format(k)) try: papers[k] = Paper(k, b) papers[k].added = datetime.datetime.now() diff --git a/tests/test_usecase.py b/tests/test_usecase.py index d60bdb2..d7168b3 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -703,7 +703,7 @@ class TestUsecase(DataCommandTestCase): cmds = ['pubs init', 'pubs add data/pagerank.bib', ('pubs edit Page99', [ - '', 'y', + '@misc{Page99, title="TTT" author="X. YY"}', 'y', '@misc{Page99, title="TTT", author="X. YY"}', '']), ('pubs list', [], '[Page99] YY, X. "TTT" \n') ] From 5a47150aad9fb4ce1b4ef1879f90125a2c0556d5 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Mon, 30 Jul 2018 18:24:56 +0200 Subject: [PATCH 12/18] Adds option to ignore malformed bibtex files or entry during import. --- pubs/commands/import_cmd.py | 42 ++++++++++++++++++++++++------------- tests/str_fixtures.py | 8 ++++++- tests/test_usecase.py | 19 +++++++++++++++++ 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/pubs/commands/import_cmd.py b/pubs/commands/import_cmd.py index 53d5b4c..977b62c 100644 --- a/pubs/commands/import_cmd.py +++ b/pubs/commands/import_cmd.py @@ -13,6 +13,10 @@ from ..uis import get_ui from ..content import system_path, read_text_file +_ABORT_USE_IGNORE_MSG = "Aborting import. Use --ignore-malformed to ignore." +_IGNORING_MSG = " Ignoring." + + def parser(subparsers, conf): parser = subparsers.add_parser('import', help='import paper(s) to the repository') @@ -24,10 +28,12 @@ def parser(subparsers, conf): help="one or several keys to import from the file") parser.add_argument('-O', '--overwrite', action='store_true', default=False, help="Overwrite keys already in the database") + parser.add_argument('-i', '--ignore-malformed', action='store_true', default=False, + help="Ignore malformed and unreadable files and entries") return parser -def many_from_path(ui, bibpath): +def many_from_path(ui, bibpath, ignore=False): """Extract list of papers found in bibliographic files in path. The behavior is to: @@ -52,8 +58,12 @@ def many_from_path(ui, bibpath): try: biblist.append(coder.decode_bibdata(read_text_file(filepath))) except coder.BibDecodingError: - ui.error("Could not parse bibtex at {}. Aborting import.".format(filepath)) - ui.exit() + error = "Could not parse bibtex at {}.".format(filepath) + if ignore: + ui.warning(error + _IGNORING_MSG) + else: + ui.error(error + _ABORT_USE_IGNORE_MSG) + ui.exit() papers = {} for b in biblist: @@ -64,7 +74,12 @@ def many_from_path(ui, bibpath): papers[k] = Paper(k, b) papers[k].added = datetime.datetime.now() except ValueError as e: - papers[k] = e + error = 'Could not load entry for citekey {} ({}).'.format(k, e) + if ignore: + ui.warning(error + _IGNORING_MSG) + else: + ui.error(error + _ABORT_USE_IGNORE_MSG) + ui.exit() return papers @@ -81,20 +96,17 @@ def command(conf, args): rp = repo.Repository(conf) # Extract papers from bib - papers = many_from_path(ui, bibpath) + papers = many_from_path(ui, bibpath, ignore=args.ignore_malformed) keys = args.keys or papers.keys() for k in keys: p = papers[k] - if isinstance(p, Exception): - ui.error('Could not load entry for citekey {}.'.format(k)) + rp.push_paper(p, overwrite=args.overwrite) + ui.info('{} imported.'.format(color.dye_out(p.citekey, 'citekey'))) + docfile = bibstruct.extract_docfile(p.bibdata) + if docfile is None: + ui.warning("No file for {}.".format(p.citekey)) else: - rp.push_paper(p, overwrite=args.overwrite) - ui.info('{} imported.'.format(color.dye_out(p.citekey, 'citekey'))) - docfile = bibstruct.extract_docfile(p.bibdata) - if docfile is None: - ui.warning("No file for {}.".format(p.citekey)) - else: - rp.push_doc(p.citekey, docfile, copy=copy) - #FIXME should move the file if configured to do so. + rp.push_doc(p.citekey, docfile, copy=copy) + # FIXME should move the file if configured to do so. rp.close() diff --git a/tests/str_fixtures.py b/tests/str_fixtures.py index 03360b8..3b6ded3 100644 --- a/tests/str_fixtures.py +++ b/tests/str_fixtures.py @@ -69,7 +69,7 @@ bibtex_no_citekey = """@Manual{, } """ -bibtex_month= """@inproceedings{Goyal2017, +bibtex_month = """@inproceedings{Goyal2017, author = {Goyal, Anirudh and Sordoni, Alessandro and C{\^{o}}t{\'{e}}, Marc-Alexandre and Ke, Nan Rosemary and Bengio, Yoshua}, title = {Z-Forcing: Training Stochastic Recurrent Networks}, year = {2017}, @@ -78,6 +78,12 @@ bibtex_month= """@inproceedings{Goyal2017, } """ +not_bibtex = """@misc{this looks, + like = a = bibtex file but + , is not a real one! + +""" + sample_conf = """ [main] diff --git a/tests/test_usecase.py b/tests/test_usecase.py index d7168b3..f9ae4e4 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -826,6 +826,25 @@ class TestUsecase(DataCommandTestCase): outs = self.execute_cmds(cmds) self.assertEqual(1 + 1, len(outs[-1].split('\n'))) + def test_import_fails_without_ignore(self): + with FakeFileOpen(self.fs)('data/fake.bib', 'w') as f: + f.write(str_fixtures.not_bibtex) + cmds = ['pubs init', + 'pubs import data/ Page99', + ] + with self.assertRaises(FakeSystemExit): + self.execute_cmds(cmds) + + def test_import_ignores(self): + with FakeFileOpen(self.fs)('data/fake.bib', 'w') as f: + f.write(str_fixtures.not_bibtex) + cmds = ['pubs init', + 'pubs import --ignore-malformed data/ Page99', + 'pubs list' + ] + outs = self.execute_cmds(cmds) + self.assertEqual(1 + 1, len(outs[-1].split('\n'))) + def test_update(self): cmds = ['pubs init', 'pubs add data/pagerank.bib', From bc239aba6905b0926fa90650489657141f39634f Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Tue, 31 Jul 2018 11:08:05 +0200 Subject: [PATCH 13/18] Removes italic on one contributor. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 957364f..9b4e61b 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,7 @@ - Better dialog after editing paper [(#142)](https://github.com/pubs/pubs/issues/142) -- Add a command to open urls ([#139](https://github.com/pubs/pubs/issues/139) *by [ksunden](https://github.com/ksunden)*) +- Add a command to open urls ([#139](https://github.com/pubs/pubs/issues/139) by [ksunden](https://github.com/ksunden)) - More robust cache on version change [(#138)](https://github.com/pubs/pubs/issues/138) From c76c7607f9d23484cb2adb8baca1f3c97a554cd2 Mon Sep 17 00:00:00 2001 From: "Fabien C. Y. Benureau" Date: Wed, 1 Aug 2018 14:22:18 +0900 Subject: [PATCH 14/18] Upgrade pyfakefs to current version Fix #148. Also did some cleanup on the tests. --- pubs/uis.py | 3 +++ tests/fake_env.py | 19 ++++++++++++++----- tests/requirements.txt | 2 +- tests/test_bibstruct.py | 1 - tests/test_config.py | 2 +- tests/test_databroker.py | 5 ++--- tests/test_datacache.py | 1 - tests/test_pretty.py | 2 -- tests/test_usecase.py | 17 +++++++---------- 9 files changed, 28 insertions(+), 24 deletions(-) diff --git a/pubs/uis.py b/pubs/uis.py index c2a8bfc..326ab25 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -46,6 +46,9 @@ def _get_local_editor(): def _editor_input(editor, initial='', suffix='.tmp'): """Use an editor to get input""" str_initial = initial.encode('utf-8') # TODO: make it a configuration item + # tfile_name = '/tmp/pubs.tmp' + # with open(tfile_name, 'w') as temp_file: + # temp_file.write(str_initial) with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp_file: tfile_name = temp_file.name temp_file.write(str_initial) diff --git a/tests/fake_env.py b/tests/fake_env.py index 4f8af1b..d8404a3 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -98,11 +98,20 @@ class TestFakeFs(fake_filesystem_unittest.TestCase): def setUp(self): self.rootpath = os.path.abspath(os.path.dirname(__file__)) + self.homepath = os.path.expanduser('~') self.setUpPyfakefs() - self.fs.CreateDirectory(os.path.expanduser('~')) - self.fs.CreateDirectory(self.rootpath) - os.chdir(self.rootpath) + self.reset_fs() + # self.fs.create_dir(os.path.expanduser('~')) + # self.fs.create_dir(self.rootpath) + # os.chdir(self.rootpath) + def reset_fs(self): - self._stubber.tearDown() # renew the filesystem - self.setUp() + if self.fs.isdir(self.homepath): + self.fs.remove_object(self.homepath) + if self.fs.isdir(self.rootpath): + self.fs.remove_object(self.rootpath) + + self.fs.create_dir(os.path.expanduser('~')) + self.fs.create_dir(self.rootpath) + os.chdir(self.rootpath) diff --git a/tests/requirements.txt b/tests/requirements.txt index 2790d94..f3726e3 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,5 +1,5 @@ # those are the additional packages required to run the tests six -pyfakefs==3.3 +pyfakefs ddt mock diff --git a/tests/test_bibstruct.py b/tests/test_bibstruct.py index 7d56feb..05af028 100644 --- a/tests/test_bibstruct.py +++ b/tests/test_bibstruct.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import os import unittest import copy diff --git a/tests/test_config.py b/tests/test_config.py index 23856a2..cecd75b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,7 +2,7 @@ import unittest import dotdot -from pubs.config import conf +# from pubs.config import conf # class TestConfig(unittest.TestCase): # diff --git a/tests/test_databroker.py b/tests/test_databroker.py index 57e5c66..041ac8e 100644 --- a/tests/test_databroker.py +++ b/tests/test_databroker.py @@ -5,8 +5,7 @@ import os import dotdot import fake_env -from pubs import content, filebroker, databroker, datacache -from pubs.config import conf +from pubs import content, databroker, datacache import str_fixtures from pubs import endecoder @@ -35,7 +34,7 @@ class TestDataBroker(fake_env.TestFakeFs): self.assertEqual(db.pull_metadata('citekey1'), page99_metadata) pulled = db.pull_bibentry('citekey1')['Page99'] - for key, value in pulled.items(): + for key in pulled.keys(): self.assertEqual(pulled[key], page99_bibentry['Page99'][key]) self.assertEqual(db.pull_bibentry('citekey1'), page99_bibentry) diff --git a/tests/test_datacache.py b/tests/test_datacache.py index 4709aca..bfb9223 100644 --- a/tests/test_datacache.py +++ b/tests/test_datacache.py @@ -3,7 +3,6 @@ import unittest import time import dotdot -import fake_env from pubs.datacache import CacheEntrySet diff --git a/tests/test_pretty.py b/tests/test_pretty.py index 0d2cf23..0c09211 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -1,10 +1,8 @@ from __future__ import unicode_literals import unittest -import os import dotdot -import fake_env from pubs import endecoder, pretty, color, config diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 8091f69..2b4199c 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -15,14 +15,12 @@ from pyfakefs.fake_filesystem import FakeFileOpen import dotdot import fake_env -from pubs import pubs_cmd, update, color, content, filebroker, uis, p3, endecoder +from pubs import pubs_cmd, color, content, uis, p3, endecoder from pubs.config import conf -import configobj import str_fixtures import fixtures -from pubs.commands import init_cmd, import_cmd # makes the tests very noisy PRINT_OUTPUT = False @@ -66,12 +64,12 @@ class TestFakeInput(unittest.TestCase): def test_editor_input(self): other_input = fake_env.FakeInput(['yes', 'no'], - module_list=[uis, color]) + module_list=[uis]) other_input.as_global() - self.assertEqual(uis._editor_input(), 'yes') - self.assertEqual(uis._editor_input(), 'no') + self.assertEqual(uis._editor_input('fake_editor'), 'yes') + self.assertEqual(uis._editor_input('fake_editor'), 'no') with self.assertRaises(fake_env.FakeInput.UnexpectedInput): - color.input() + uis._editor_input() class CommandTestCase(fake_env.TestFakeFs): @@ -82,7 +80,6 @@ class CommandTestCase(fake_env.TestFakeFs): def setUp(self, nsec_stat=True): super(CommandTestCase, self).setUp() os.stat_float_times(nsec_stat) - # self.fs = fake_env.create_fake_fs([content, filebroker, conf, init_cmd, import_cmd, configobj, update], nsec_stat=nsec_stat) self.default_pubs_dir = os.path.expanduser('~/.pubs') self.default_conf_path = os.path.expanduser('~/.pubsrc') @@ -178,7 +175,7 @@ class URLContentTestCase(DataCommandTestCase): return p3.urlparse(url).path def url_exists(self, url): - return self.fs.Exists(self._url_to_path(url)) + return self.fs.exists(self._url_to_path(url)) def url_to_byte_content(self, url, ui=None): path = self._url_to_path(url) @@ -920,4 +917,4 @@ class TestCache(DataCommandTestCase): if __name__ == '__main__': - unittest.main() + unittest.main(verbosity=2) From cf97f681efe2ead29fa121df698ea286c4f6b336 Mon Sep 17 00:00:00 2001 From: "Fabien C. Y. Benureau" Date: Wed, 1 Aug 2018 14:37:23 +0900 Subject: [PATCH 15/18] cleanup commented code, better reset_fs() --- pubs/uis.py | 3 --- tests/fake_env.py | 12 ++++-------- tests/test_databroker.py | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/pubs/uis.py b/pubs/uis.py index 326ab25..c2a8bfc 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -46,9 +46,6 @@ def _get_local_editor(): def _editor_input(editor, initial='', suffix='.tmp'): """Use an editor to get input""" str_initial = initial.encode('utf-8') # TODO: make it a configuration item - # tfile_name = '/tmp/pubs.tmp' - # with open(tfile_name, 'w') as temp_file: - # temp_file.write(str_initial) with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as temp_file: tfile_name = temp_file.name temp_file.write(str_initial) diff --git a/tests/fake_env.py b/tests/fake_env.py index d8404a3..028d656 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -101,16 +101,12 @@ class TestFakeFs(fake_filesystem_unittest.TestCase): self.homepath = os.path.expanduser('~') self.setUpPyfakefs() self.reset_fs() - # self.fs.create_dir(os.path.expanduser('~')) - # self.fs.create_dir(self.rootpath) - # os.chdir(self.rootpath) - def reset_fs(self): - if self.fs.isdir(self.homepath): - self.fs.remove_object(self.homepath) - if self.fs.isdir(self.rootpath): - self.fs.remove_object(self.rootpath) + """Reset the fake filesystem""" + for dir_name in self.fs.listdir('/'): + if dir_name not in ['var', 'tmp']: + self.fs.remove_object(os.path.join('/', dir_name)) self.fs.create_dir(os.path.expanduser('~')) self.fs.create_dir(self.rootpath) diff --git a/tests/test_databroker.py b/tests/test_databroker.py index 041ac8e..8891931 100644 --- a/tests/test_databroker.py +++ b/tests/test_databroker.py @@ -90,4 +90,4 @@ class TestDataBroker(fake_env.TestFakeFs): if __name__ == '__main__': - unittest.main() + unittest.main(verbosity=2) From 9093cbfbed211274f36f3014a4037094c64459d7 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 1 Aug 2018 14:39:15 +0200 Subject: [PATCH 16/18] Add changelog entry for #151. --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 9b4e61b..1a3267b 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,8 @@ ### Fixed bugs +- [[#148]](https://github.com/pubs/pubs/issues/148) Fix compatibility with Pyfakefs 3.7 [(#151)](https://github.com/pubs/pubs/pull/151) + - [[#95]](https://github.com/pubs/pubs/issues/95) Error message when editor is missing [(#141)](https://github.com/pubs/pubs/issues/141) - Fixes tests for printing help on `--help` and without argument. [(#137)](https://github.com/pubs/pubs/issues/137) From 13f21a3d28645269912b9e4b961f1da8f7a63cde Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 1 Aug 2018 14:43:32 +0200 Subject: [PATCH 17/18] Minor grammar in warning. --- pubs/commands/import_cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubs/commands/import_cmd.py b/pubs/commands/import_cmd.py index 977b62c..fa6236e 100644 --- a/pubs/commands/import_cmd.py +++ b/pubs/commands/import_cmd.py @@ -14,7 +14,7 @@ from ..content import system_path, read_text_file _ABORT_USE_IGNORE_MSG = "Aborting import. Use --ignore-malformed to ignore." -_IGNORING_MSG = " Ignoring." +_IGNORING_MSG = " Ignoring it." def parser(subparsers, conf): From 03900c324a5281f52d8389fe3e2213e0b8d1f967 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 1 Aug 2018 15:12:25 +0200 Subject: [PATCH 18/18] Adds changelog entry. --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 1a3267b..314ccbd 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,8 @@ ### Fixed bugs +- [[#149]](https://github.com/pubs/pubs/issues/149) More robust handling of parsing and citekey errors [(#87)](https://github.com/pubs/pubs/pull/87) + - [[#148]](https://github.com/pubs/pubs/issues/148) Fix compatibility with Pyfakefs 3.7 [(#151)](https://github.com/pubs/pubs/pull/151) - [[#95]](https://github.com/pubs/pubs/issues/95) Error message when editor is missing [(#141)](https://github.com/pubs/pubs/issues/141)