diff --git a/changelog.md b/changelog.md index 52dadd9..f7aeafb 100644 --- a/changelog.md +++ b/changelog.md @@ -5,8 +5,14 @@ [Full Changelog](https://github.com/pubs/pubs/compare/v0.8.3...master) +### Implemented enhancements + +- Added support for non-standard bibtex types, e.g. @collection, @software, etc. ([#226](https://github.com/pubs/pubs/pull/226) +- The number of displayed authors in listings is now configurable, as the `max_authors` value in the `main` section of the configuration. ([#225](https://github.com/pubs/pubs/pull/225) + ### Fixed bugs +- Tests don't run on python 2.7 or <=3.4. They may still work, but support will not be tested and will eventually be dropped. ([#223](https://github.com/pubs/pubs/pull/223) ## [v0.8.3](https://github.com/pubs/pubs/compare/v0.8.2...v0.8.3) (2019-08-12) diff --git a/pubs/color.py b/pubs/color.py index 86e8622..0e41cf9 100644 --- a/pubs/color.py +++ b/pubs/color.py @@ -144,7 +144,7 @@ def setup(conf, force_colors=False): # undye -undye_re = re.compile('\x1b\[[;\d]*[A-Za-z]') +undye_re = re.compile('\x1b\\[[;\d]*[A-Za-z]') def undye(s): """Purge string s of color""" diff --git a/pubs/commands/remove_cmd.py b/pubs/commands/remove_cmd.py index 0b7f9e0..e09039c 100644 --- a/pubs/commands/remove_cmd.py +++ b/pubs/commands/remove_cmd.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from .. import repo from .. import color +from .. import pretty from ..uis import get_ui from ..utils import resolve_citekey_list from ..p3 import ustr, u_maybe @@ -25,11 +26,15 @@ def command(conf, args): rp = repo.Repository(conf) keys = resolve_citekey_list(repo=rp, citekeys=args.citekeys, ui=ui, exit_on_fail=True) + plural = 's' if len(keys) > 1 else '' if force is None: - are_you_sure = (("Are you sure you want to delete the publication(s) [{}]" - " (this will also delete associated documents)?") - .format(', '.join([color.dye_out(c, 'citekey') for c in args.citekeys]))) + to_remove_str = '\n'.join(pretty.paper_oneliner(rp.pull_paper(key), + max_authors=conf['main']['max_authors']) + for key in keys) + are_you_sure = (("Are you sure you want to delete the following publication{}" + " (this will also delete associated documents)?:\n{}\n") + .format(plural, to_remove_str)) sure = ui.input_yn(question=are_you_sure, default='n') if force or sure: failed = False # Whether something failed @@ -42,11 +47,12 @@ def command(conf, args): if failed: ui.exit() # Exit with nonzero error code else: - ui.message('The publication(s) [{}] were removed'.format( + + ui.message('The publication{} {} were removed'.format(plural, ', '.join([color.dye_out(c, 'citekey') for c in keys]))) # FIXME: print should check that removal proceeded well. else: - ui.message('The publication(s) [{}] were {} removed'.format( + ui.message('The publication{} {} were {} removed'.format(plural, ', '.join([color.dye_out(c, 'citekey') for c in keys]), color.dye_out('not','bold'))) diff --git a/pubs/endecoder.py b/pubs/endecoder.py index 346af3a..e289ddc 100644 --- a/pubs/endecoder.py +++ b/pubs/endecoder.py @@ -119,18 +119,18 @@ class EnDecoder(object): keyword for keyword in entry['keyword']) return entry - def decode_bibdata(self, bibdata): + def decode_bibdata(self, bibstr): """Decodes bibdata from string. If the decoding fails, returns a BibDecodingError. """ - if len(bibdata) == 0: + if len(bibstr) == 0: error_msg = 'parsing error: the provided string has length zero.' - raise self.BibDecodingError(error_msg, bibdata) + raise self.BibDecodingError(error_msg, bibstr) try: entries = bp.bparser.BibTexParser( - bibdata, common_strings=True, customization=customizations, - homogenize_fields=True).get_entry_dict() + bibstr, common_strings=True, customization=customizations, + homogenize_fields=True, ignore_nonstandard_types=False).get_entry_dict() # Remove id from bibtexparser attribute which is stored as citekey for e in entries: entries[e].pop(BP_ID_KEY) @@ -147,13 +147,13 @@ class EnDecoder(object): return entries else: raise self.BibDecodingError(('no valid entry found in the provided data: ' - ' {}').format(bibdata), bibdata) + ' {}').format(bibstr), bibstr) except (pyparsing.ParseException, pyparsing.ParseSyntaxException) as e: error_msg = self._format_parsing_error(e) - raise self.BibDecodingError(error_msg, bibdata) + raise self.BibDecodingError(error_msg, bibstr) except bibtexparser.bibdatabase.UndefinedString as e: error_msg = 'parsing error: undefined string in provided data: {}'.format(e) - raise self.BibDecodingError(error_msg, bibdata) + raise self.BibDecodingError(error_msg, bibstr) @classmethod def _format_parsing_error(cls, e): diff --git a/pubs/filebroker.py b/pubs/filebroker.py index f48ba93..7043f4c 100644 --- a/pubs/filebroker.py +++ b/pubs/filebroker.py @@ -16,7 +16,7 @@ def filter_filename(filename, ext): """ Return the filename without the extension if the extension matches ext. Otherwise return None """ - pattern = '.*\{}$'.format(ext) + pattern = '.*\\{}$'.format(ext) if re.match(pattern, filename) is not None: return u_maybe(filename[:-len(ext)]) diff --git a/tests/data/collection.bib b/tests/data/three_articles.bib similarity index 100% rename from tests/data/collection.bib rename to tests/data/three_articles.bib diff --git a/tests/data_non_standard/non_standard_collection.bib b/tests/data_non_standard/non_standard_collection.bib new file mode 100644 index 0000000..0dbd1f2 --- /dev/null +++ b/tests/data_non_standard/non_standard_collection.bib @@ -0,0 +1,9 @@ +@collection{Geometric_phases, + title = {Geometric phases in physics}, + editor = {Shapere, Alfred and Wilczek, Frank}, + year = {1989}, + series = {Advanced Series in Mathematical Physics}, + volume = {5}, + publisher = {World Scientific}, + isbn = {9789971506216} +} diff --git a/tests/data_non_standard/non_standard_software.bib b/tests/data_non_standard/non_standard_software.bib new file mode 100644 index 0000000..87ecd7b --- /dev/null +++ b/tests/data_non_standard/non_standard_software.bib @@ -0,0 +1,7 @@ +@software{hadoop, + author = {{Apache Software Foundation}}, + title = {Hadoop}, + url = {https://hadoop.apache.org}, + version = {0.20.2}, + date = {2010-02-19}, +} diff --git a/tests/fake_env.py b/tests/fake_env.py index 6d790a3..41d22d7 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -43,8 +43,8 @@ def capture(f, verbose=False): """ def newf(*args, **kwargs): old_stderr, old_stdout = sys.stderr, sys.stdout - sys.stdout = _fake_stdio(additional_out=old_stderr if verbose else None) - sys.stderr = _fake_stdio(additional_out=old_stderr if False else None) + sys.stdout = _fake_stdio(additional_out=old_stdout if verbose else None) + sys.stderr = _fake_stdio(additional_out=old_stderr if verbose else None) try: return f(*args, **kwargs), _get_fake_stdio_ucontent(sys.stdout), _get_fake_stdio_ucontent(sys.stderr) finally: diff --git a/tests/str_fixtures.py b/tests/str_fixtures.py index 1372a80..d2fbfec 100644 --- a/tests/str_fixtures.py +++ b/tests/str_fixtures.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals -bibtex_external0 = """ +bibtex_external0 = r""" @techreport{Page99, number = {1999-66}, month = {November}, @@ -17,7 +17,7 @@ institution = {Stanford InfoLab}, } """ -bibtex_external_alt = """ +bibtex_external_alt = r""" @techreport{Page99, number = {1999-66}, month = {November}, @@ -33,7 +33,7 @@ institution = {Stanford InfoLab}, } """ -bibtex_raw0 = """@techreport{ +bibtex_raw0 = r"""@techreport{ Page99, author = "Page, Lawrence and Brin, Sergey and Motwani, Rajeev and Winograd, Terry", publisher = "Stanford InfoLab", @@ -50,12 +50,12 @@ bibtex_raw0 = """@techreport{ """ -metadata_raw0 = """docfile: docsdir://Page99.pdf +metadata_raw0 = r"""docfile: docsdir://Page99.pdf tags: [search, network] added: '2013-11-14 13:14:20' """ -turing_bib = """@article{turing1950computing, +turing_bib = r"""@article{turing1950computing, title={Computing machinery and intelligence}, author={Turing, Alan M}, journal={Mind}, @@ -75,7 +75,7 @@ added: '2013-11-14 13:14:20' """ # Should not parse (see #113) -bibtex_no_citekey = """@Manual{, +bibtex_no_citekey = r"""@Manual{, title = {R: A Language and Environment for Statistical Computing}, author = {{R Core Team}}, organization = {R Foundation for Statistical Computing}, @@ -85,7 +85,7 @@ bibtex_no_citekey = """@Manual{, } """ -bibtex_month = """@inproceedings{Goyal2017, +bibtex_month = r"""@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}, @@ -94,15 +94,15 @@ bibtex_month = """@inproceedings{Goyal2017, } """ -not_bibtex = """@misc{this looks, +not_bibtex = r"""@misc{this looks, like = a = bibtex file but , is not a real one! """ -bibtex_with_latex = """@article{kjaer2018large, +bibtex_with_latex = r"""@article{kjaer2018large, title={A large impact crater beneath Hiawatha Glacier in northwest Greenland}, - author={Kj{\\ae}r, Kurt H and Larsen, Nicolaj K and Binder, Tobias and Bj{\\o}rk, Anders A and Eisen, Olaf and Fahnestock, Mark A and Funder, Svend and Garde, Adam A and Haack, Henning and Helm, Veit and others}, + author={Kj{\ae}r, Kurt H and Larsen, Nicolaj K and Binder, Tobias and Bj{\o}rk, Anders A and Eisen, Olaf and Fahnestock, Mark A and Funder, Svend and Garde, Adam A and Haack, Henning and Helm, Veit and others}, journal={Science advances}, volume={4}, number={11}, diff --git a/tests/test_queries.py b/tests/test_queries.py index 207c4fc..a0ca6c7 100644 --- a/tests/test_queries.py +++ b/tests/test_queries.py @@ -200,8 +200,8 @@ class TestFilterPaper(unittest.TestCase): def test_latex_enc(self): latexenc_paper = doe_paper.deepcopy() - latexenc_paper.bibentry['Doe2013']['title'] = "{E}l Ni{\~n}o" - latexenc_paper.bibentry['Doe2013']['author'][0] = "Erd\H{o}s, Paul" + latexenc_paper.bibentry['Doe2013']['title'] = r"{E}l Ni{\~n}o" + latexenc_paper.bibentry['Doe2013']['author'][0] = r"Erd\H{o}s, Paul" self.assertTrue(get_paper_filter(['title:El'])(latexenc_paper)) self.assertTrue(get_paper_filter(['title:Niño'])(latexenc_paper)) self.assertTrue(get_paper_filter(['author:erdős'])(latexenc_paper)) @@ -209,12 +209,12 @@ class TestFilterPaper(unittest.TestCase): def test_normalize_unicode(self): latexenc_paper = doe_paper.deepcopy() - latexenc_paper.bibentry['Doe2013']['title'] = "{E}l Ni{\~n}o" + latexenc_paper.bibentry['Doe2013']['title'] = r"{E}l Ni{\~n}o" self.assertTrue(get_paper_filter(['title:Nin\u0303o'])(latexenc_paper)) def test_strict(self): latexenc_paper = doe_paper.deepcopy() - latexenc_paper.bibentry['Doe2013']['title'] = "El Ni{\~n}o" + latexenc_paper.bibentry['Doe2013']['title'] = r"El Ni{\~n}o" self.assertFalse(get_paper_filter( ['title:Nin\u0303o'], strict=True)(latexenc_paper)) diff --git a/tests/test_usecase.py b/tests/test_usecase.py index a591485..891d9cc 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -1091,6 +1091,22 @@ class TestUsecase(DataCommandTestCase): actual = self.execute_cmds(cmds, capture_output=True) self.assertEqual(correct, actual) + def test_add_non_standard(self): + """Test that non-standard bibtex are correctly added""" + self.fs.add_real_directory(os.path.join(self.rootpath, 'data_non_standard'), read_only=False) + correct = ['Initializing pubs in /pubs\n', + 'added to pubs:\n[Geometric_phases] "Geometric phases in physics" (1989) \n', + 'added to pubs:\n[hadoop] Foundation, Apache Software "Hadoop" \n', + ] + cmds = ['pubs init -p /pubs', + 'pubs add data_non_standard/non_standard_collection.bib', + 'pubs add data_non_standard/non_standard_software.bib', + # 'pubs list', + ] + actual = self.execute_cmds(cmds, capture_output=True) + self.assertEqual(correct, actual) + + @mock.patch('pubs.apis.requests.get', side_effect=mock_requests.mock_requests_get) def test_readme(self, reqget): """Test that the readme example work.""" @@ -1099,7 +1115,7 @@ class TestUsecase(DataCommandTestCase): self.fs.add_real_file(os.path.join(self.rootpath, 'data/pagerank.pdf'), target_path='data/Knuth1995.pdf') cmds = ['pubs init', - 'pubs import data/collection.bib', + 'pubs import data/three_articles.bib', 'pubs add data/pagerank.bib -d data/pagerank.pdf', #'pubs add -D 10.1007/s00422-012-0514-6 -d data/pagerank.pdf', 'pubs add -I 978-0822324669 -d data/oyama2000the.pdf',