diff --git a/pubs/bibstruct.py b/pubs/bibstruct.py index a9dc7c7..9832164 100644 --- a/pubs/bibstruct.py +++ b/pubs/bibstruct.py @@ -12,8 +12,9 @@ TYPE_KEY = 'type' CONTROL_CHARS = ''.join(map(uchr, list(range(0, 32)) + list(range(127, 160)))) CITEKEY_FORBIDDEN_CHARS = '@\'\\,#}{~%/ ' # '/' is OK for bibtex but forbidden # here since we transform citekeys into filenames -CITEKEY_EXCLUDE_RE = re.compile('[%s]' - % re.escape(CONTROL_CHARS + CITEKEY_FORBIDDEN_CHARS)) +CITEKEY_EXCLUDE_RE = re.compile( + '[%s]' % re.escape(CONTROL_CHARS + CITEKEY_FORBIDDEN_CHARS)) + def str2citekey(s): key = unicodedata.normalize('NFKD', ustr(s)).encode('ascii', 'ignore').decode() @@ -21,32 +22,40 @@ def str2citekey(s): # Normalize chars and remove non-ascii return key + def check_citekey(citekey): + if citekey is None or not citekey.strip(): + raise ValueError(u"Empty citekeys are not valid") # TODO This is not the right way to test that (17/12/2012) if ustr(citekey) != str2citekey(citekey): raise ValueError(u"Invalid `{}` citekey; ".format(citekey) + u"utf-8 citekeys are not supported yet.\n" u"See https://github.com/pubs/pubs/issues/28 for details.") + def verify_bibdata(bibdata): if bibdata is None or len(bibdata) == 0: raise ValueError(u"no valid bibdata") if len(bibdata) > 1: raise ValueError(u"ambiguous: multiple entries in the bibdata.") + def get_entry(bibdata): verify_bibdata(bibdata) for e in bibdata.items(): return e + def extract_citekey(bibdata): citekey, entry = get_entry(bibdata) return citekey + def author_last(author_str): """ Return the last name of the author """ return author_str.split(',')[0] + def generate_citekey(bibdata): """ Generate a citekey from bib_data. @@ -60,7 +69,7 @@ def generate_citekey(bibdata): first_author = entry[author_key][0] except KeyError: raise ValueError( - u"No author or editor defined: cannot generate a citekey.") + u"No author or editor defined: cannot generate a citekey.") try: year = entry['year'] except KeyError: @@ -69,6 +78,7 @@ def generate_citekey(bibdata): return str2citekey(citekey) + def extract_docfile(bibdata, remove=False): """ Try extracting document file from bib data. Returns None if not found. diff --git a/tests/str_fixtures.py b/tests/str_fixtures.py index 5b9f6f7..2036943 100644 --- a/tests/str_fixtures.py +++ b/tests/str_fixtures.py @@ -58,6 +58,17 @@ tags: [AI, computer] added: '2013-11-14 13:14:20' """ +# Should not parse (see #113) +bibtex_no_citekey = """@Manual{, + title = {R: A Language and Environment for Statistical Computing}, + author = {{R Core Team}}, + organization = {R Foundation for Statistical Computing}, + address = {Vienna, Austria}, + year = {2017}, + url = {https://www.R-project.org/}, +} +""" + sample_conf = """ [main] diff --git a/tests/test_paper.py b/tests/test_paper.py index cd2c94e..9c78554 100644 --- a/tests/test_paper.py +++ b/tests/test_paper.py @@ -42,6 +42,10 @@ class TestAttributes(unittest.TestCase): self.assertEqual(self.p.tags, set()) self.p.remove_tag('ranking') + def test_fails_with_empty_citekey(self): + with self.assertRaises(ValueError): + Paper(" ", fixtures.doe_bibdata) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 0b27780..0960a21 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -331,6 +331,14 @@ class TestAdd(URLContentTestCase): ] self.execute_cmds(cmds) + def test_add_no_citekey_fails(self): + # See #113 + cmds = ['pubs init', + ('pubs add', [str_fixtures.bibtex_no_citekey]), + ] + with self.assertRaises(FakeSystemExit): + self.execute_cmds(cmds) + class TestList(DataCommandTestCase):