Repository: sciunto-org/python-bibtexparser
Pull request #299 (2022-07-07 15:09:26)

:tada: Various nonfunctional / minor fixes 
This merges all the minor changes I added to my fork into the main repo. 

Much relies on other PRs (to which, at the time, I did not have access yet).


Patch:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 06d0895..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-dist: trusty
-language: python
-matrix:
-  include:
-    - python: "3.3"
-      env: TEST_SUITE=suite_3_3
-      arch: amd64
-    - python: "3.4"
-      env: TEST_SUITE=suite_3_4
-      arch: amd64
-    - python: "3.5"
-      env: TEST_SUITE=suite_3_5
-      arch: amd64
-    - python: "3.5"
-      env: TEST_SUITE=suite_3_5
-      arch: ppc64le
-    - python: "3.6"
-      env: TEST_SUITE=suite_3_6
-      arch: amd64
-    - python: "3.6"
-      env: TEST_SUITE=suite_3_6
-      arch: ppc64le  
-    - python: "3.7"
-      dist: xenial # Required for Python 3.7
-      sudo: true   # travis-ci/travis-ci#9069
-      env: TEST_SUITE=suite_3_7
-      arch: amd64
-    - python: "3.7"
-      dist: xenial # Required for Python 3.7
-      sudo: true   # travis-ci/travis-ci#9069
-      env: TEST_SUITE=suite_3_7
-      arch: ppc64le
-    - python: "pypy3.5-5.8.0"
-      env: TEST_SUITE=suite_pypy3
-      arch: amd64
-  
- 
-
-install:
-  - if [[ $TEST_SUITE == suite_3_6 ]]; then
-        pip install sphinx;
-    fi;
-  - pip install coverage 
-  - pip install -r requirements.txt
-  - python setup.py install
-script:
-  - nosetests --with-coverage  --cover-erase --cover-package=bibtexparser
-  - if [[ $TEST_SUITE == suite_3_6 ]]; then
-        cd docs;
-        make html;
-    fi;
-
-after_success:
-  - pip install coveralls
-  - coveralls
diff --git a/bibtexparser/bparser.py b/bibtexparser/bparser.py
index 133b6cb..e11180c 100644
--- a/bibtexparser/bparser.py
+++ b/bibtexparser/bparser.py
@@ -77,7 +77,7 @@ class BibTexParser(object):
                  common_strings=False,
                  add_missing_from_crossref=False):
         """
-        Creates a parser for rading BibTeX files
+        Creates a parser for reading BibTeX files
 
         :return: parser
         :rtype: `BibTexParser`
@@ -309,7 +309,7 @@ class BibTexParser(object):
         :type string: string
         """
         if string_key in self.bib_database.strings:
-            logger.warning('Overwritting existing string for key: %s.',
+            logger.warning('Overwriting existing string for key: %s.',
                            string_key)
         logger.debug(u'Store string: {} -> {}'.format(string_key, string))
         self.bib_database.strings[string_key] = self._clean_val(string)
diff --git a/bibtexparser/bwriter.py b/bibtexparser/bwriter.py
index e028147..454a977 100644
--- a/bibtexparser/bwriter.py
+++ b/bibtexparser/bwriter.py
@@ -97,7 +97,6 @@ class BibTexWriter(object):
         return bibtex
 
     def _entries_to_bibtex(self, bib_database):
-        bibtex = ''
         if self.order_entries_by:
             # TODO: allow sort field does not exist for entry
             entries = sorted(bib_database.entries, key=lambda x: BibDatabase.entry_sort_key(x, self.order_entries_by))
@@ -109,9 +108,7 @@ class BibTexWriter(object):
             widths = [max(map(len, entry.keys())) for entry in entries]
             self._max_field_width = max(widths)
 
-        for entry in entries:
-            bibtex += self._entry_to_bibtex(entry)
-        return bibtex
+        return self.entry_separator.join(self._entry_to_bibtex(entry) for entry in entries)
 
     def _entry_to_bibtex(self, entry):
         bibtex = ''
@@ -143,7 +140,7 @@ class BibTexWriter(object):
                 bibtex += '\n'+self.indent+','
             else:
                 bibtex += ','
-        bibtex += "\n}\n" + self.entry_separator
+        bibtex += "\n}\n"
         return bibtex
 
     def _comments_to_bibtex(self, bib_database):
diff --git a/bibtexparser/customization.py b/bibtexparser/customization.py
index 3e7447d..99c48fb 100644
--- a/bibtexparser/customization.py
+++ b/bibtexparser/customization.py
@@ -6,9 +6,9 @@ You can find inspiration from these functions to design yours.
 Each of them takes a record and return the modified record.
 """
 
-import re
 import logging
-
+import re
+import warnings
 from builtins import str
 
 from bibtexparser.latexenc import latex_to_unicode, string_to_latex, protect_uppercase
@@ -72,14 +72,14 @@ def splitname(name, strict_mode=True):
     # We'll iterate over the input once, dividing it into a list of words for
     # each comma-separated section. We'll also calculate the case of each word
     # as we work.
-    sections = [[]]      # Sections of the name.
-    cases = [[]]         # 1 = uppercase, 0 = lowercase, -1 = caseless.
-    word = []            # Current word.
-    case = -1            # Case of the current word.
-    level = 0            # Current brace level.
-    bracestart = False   # Will the next character be the first within a brace?
-    controlseq = True    # Are we currently processing a control sequence?
-    specialchar = None   # Are we currently processing a special character?
+    sections = [[]]  # Sections of the name.
+    cases = [[]]  # 1 = uppercase, 0 = lowercase, -1 = caseless.
+    word = []  # Current word.
+    case = -1  # Case of the current word.
+    level = 0  # Current brace level.
+    bracestart = False  # Will the next character be the first within a brace?
+    controlseq = True  # Are we currently processing a control sequence?
+    specialchar = None  # Are we currently processing a special character?
 
     # Using an iterator allows us to deal with escapes in a simple manner.
     nameiter = iter(name)
@@ -246,12 +246,12 @@ def splitname(name, strict_mode=True):
                 firstl = cases.index(0) - len(cases)
                 lastl = -cases[::-1].index(0) - 1
                 if lastl == -1:
-                    lastl -= 1      # Cannot consume the rest of the string.
+                    lastl -= 1  # Cannot consume the rest of the string.
 
                 # Pull the parts out.
                 parts['first'] = p0[:firstl]
-                parts['von'] = p0[firstl:lastl+1]
-                parts['last'] = p0[lastl+1:]
+                parts['von'] = p0[firstl:lastl + 1]
+                parts['last'] = p0[lastl + 1:]
 
             # No lowercase: last is the last word, first is everything else.
             else:
@@ -287,7 +287,7 @@ def splitname(name, strict_mode=True):
             if 0 in lcases:
                 split = len(lcases) - lcases[::-1].index(0)
                 if split == len(lcases):
-                    split = 0            # Last cannot be empty.
+                    split = 0  # Last cannot be empty.
                 parts['von'] = sections[0][:split]
                 parts['last'] = sections[0][split:]
 
@@ -345,7 +345,8 @@ def author(record):
     """
     if "author" in record:
         if record["author"]:
-            record["author"] = getnames([i.strip() for i in record["author"].replace('\n', ' ').split(" and ")])
+            record["author"] = getnames([i.strip() for i in re.split(r"\ and\ ", record["author"].replace('\n', ' '),
+                                                                     flags=re.IGNORECASE)])
         else:
             del record["author"]
     return record
@@ -365,7 +366,8 @@ def editor(record):
         if record["editor"]:
             record["editor"] = getnames([i.strip() for i in record["editor"].replace('\n', ' ').split(" and ")])
             # convert editor to object
-            record["editor"] = [{"name": i, "ID": i.replace(',', '').replace(' ', '').replace('.', '')} for i in record["editor"]]
+            record["editor"] = [{"name": i, "ID": i.replace(',', '').replace(' ', '').replace('.', '')} for i in
+                                record["editor"]]
         else:
             del record["editor"]
     return record
@@ -417,7 +419,8 @@ def journal(record):
     if "journal" in record:
         # switch journal to object
         if record["journal"]:
-            record["journal"] = {"name": record["journal"], "ID": record["journal"].replace(',', '').replace(' ', '').replace('.', '')}
+            record["journal"] = {"name": record["journal"],
+                                 "ID": record["journal"].replace(',', '').replace(' ', '').replace('.', '')}
 
     return record
 
@@ -518,13 +521,21 @@ def homogenize_latex_encoding(record):
     :type record: dict
     :returns: dict -- the modified record.
     """
-    # First, we convert everything to unicode
+    #  First, we convert everything to unicode
     record = convert_to_unicode(record)
     # And then, we fall back
     for val in record:
         if val not in ('ID',):
             logger.debug('Apply string_to_latex to: %s', val)
-            record[val] = string_to_latex(record[val])
+            if isinstance(record[val], list):
+                record[val] = [
+                    string_to_latex(x) for x in record[val]
+                ]
+            elif isinstance(record[val], str):
+                record[val] = string_to_latex(record[val])
+            else:
+                warnings.warn('Unable to homogenize latex encoding for %s: Expected string or list,' % val,
+                              RuntimeWarning)
             if val == 'title':
                 logger.debug('Protect uppercase in title')
                 logger.debug('Before: %s', record[val])
@@ -543,6 +554,7 @@ def add_plaintext_fields(record):
     :type record: dict
     :returns: dict -- the modified record.
     """
+
     def _strip_string(string):
         for stripped in ['{', '}']:
             string = string.replace(stripped, "")
diff --git a/requirements.txt b/requirements.txt
index 50c1129..e7c21d1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,1 @@
-future>=0.16.0
 pyparsing>=2.0.3
-unittest2>=1.1.0
diff --git a/setup.py b/setup.py
index 5e1e1e7..cbcf55a 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,5 @@ setup(
     author_email = "devel@sciunto.org",
     description  = "Bibtex parser for python 2.7 and 3.3 and newer",
     packages     = ['bibtexparser'],
-    install_requires = ['pyparsing>=2.0.3',
-                        'future>=0.16.0'],
-    extra_requires = {'unittest': 'unittest2>=1.1.0'}
+    install_requires = ['pyparsing>=2.0.3'],
 )


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..d2b386e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,31 @@
+
+name: Tests
+
+on: [push, pull_request]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-node@v2-beta
+        with:
+          node-version: '12'
+          check-latest: true
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install dependencies (tf ${{ matrix.tf-version }} )
+        run: |
+          pip install -r requirements.txt
+
+      - name: Run Tests
+        run: |
+          python -m unittest discover -s ./bibtexparser/tests
diff --git a/bibtexparser/tests/data/article_comma_first.bib b/bibtexparser/tests/data/article_comma_first.bib
index 83882f4..db86cfc 100644
--- a/bibtexparser/tests/data/article_comma_first.bib
+++ b/bibtexparser/tests/data/article_comma_first.bib
@@ -16,3 +16,4 @@
         , journal = {Nice Journal}
         , comments = {A comment}
         , keyword = {keyword1, keyword2}}
+
diff --git a/bibtexparser/tests/data/article_comma_first_and_trailing_comma_output.bib b/bibtexparser/tests/data/article_comma_first_and_trailing_comma_output.bib
index 6126c7f..7008678 100644
--- a/bibtexparser/tests/data/article_comma_first_and_trailing_comma_output.bib
+++ b/bibtexparser/tests/data/article_comma_first_and_trailing_comma_output.bib
@@ -12,4 +12,3 @@ multilines... and with a french érudit word}
  , year = {2013}
  ,
 }
-
diff --git a/bibtexparser/tests/data/article_output.bib b/bibtexparser/tests/data/article_output.bib
index 5289e8c..7966875 100644
--- a/bibtexparser/tests/data/article_output.bib
+++ b/bibtexparser/tests/data/article_output.bib
@@ -11,4 +11,3 @@ multilines... and with a french érudit word},
  volume = {12},
  year = {2013}
 }
-
diff --git a/bibtexparser/tests/data/article_trailing_comma_output.bib b/bibtexparser/tests/data/article_trailing_comma_output.bib
index f5cfd6c..de2bacf 100644
--- a/bibtexparser/tests/data/article_trailing_comma_output.bib
+++ b/bibtexparser/tests/data/article_trailing_comma_output.bib
@@ -11,4 +11,3 @@ multilines... and with a french érudit word},
  volume = {12},
  year = {2013},
 }
-
diff --git a/bibtexparser/tests/data/article_with_annotation_output.bib b/bibtexparser/tests/data/article_with_annotation_output.bib
index 7595535..72704f8 100644
--- a/bibtexparser/tests/data/article_with_annotation_output.bib
+++ b/bibtexparser/tests/data/article_with_annotation_output.bib
@@ -12,4 +12,3 @@ multilines... and with a french érudit word},
  volume = {12},
  year = {2013}
 }
-
diff --git a/bibtexparser/tests/data/article_with_strings_output.bib b/bibtexparser/tests/data/article_with_strings_output.bib
index 0c70dbf..1d59b7a 100644
--- a/bibtexparser/tests/data/article_with_strings_output.bib
+++ b/bibtexparser/tests/data/article_with_strings_output.bib
@@ -15,4 +15,3 @@
  volume = {12},
  year = {2013}
 }
-
diff --git a/bibtexparser/tests/data/book_capital_AND.bib b/bibtexparser/tests/data/book_capital_AND.bib
new file mode 100644
index 0000000..afd444d
--- /dev/null
+++ b/bibtexparser/tests/data/book_capital_AND.bib
@@ -0,0 +1,8 @@
+@BOOK{Bird1987,
+  title = {Dynamics of Polymeric Liquid},
+  publisher = {Wiley Edition},
+  year = {1987},
+  author = {Bird, R.B. and Armstrong, R.C. AND Hassager, O.},
+  volume = {1},
+  edition = {2},
+}
diff --git a/bibtexparser/tests/data/book_comma_first.bib b/bibtexparser/tests/data/book_comma_first.bib
index 5691da9..2221cc1 100644
--- a/bibtexparser/tests/data/book_comma_first.bib
+++ b/bibtexparser/tests/data/book_comma_first.bib
@@ -6,4 +6,3 @@
    , volume = {1}
    , year = {1987}
 }
-
diff --git a/bibtexparser/tests/data/book_output.bib b/bibtexparser/tests/data/book_output.bib
index 958f90c..60458e8 100644
--- a/bibtexparser/tests/data/book_output.bib
+++ b/bibtexparser/tests/data/book_output.bib
@@ -6,4 +6,3 @@
  volume = {1},
  year = {1987}
 }
-
diff --git a/bibtexparser/tests/data/multiple_entries_and_comments_output.bib b/bibtexparser/tests/data/multiple_entries_and_comments_output.bib
index 969f145..8660a6b 100644
--- a/bibtexparser/tests/data/multiple_entries_and_comments_output.bib
+++ b/bibtexparser/tests/data/multiple_entries_and_comments_output.bib
@@ -26,4 +26,3 @@
  title = {Optical fiber fusion slicing},
  year = {2005}
 }
-
diff --git a/bibtexparser/tests/data/multiple_entries_output.bib b/bibtexparser/tests/data/multiple_entries_output.bib
index a5414b8..718a8ed 100644
--- a/bibtexparser/tests/data/multiple_entries_output.bib
+++ b/bibtexparser/tests/data/multiple_entries_output.bib
@@ -22,4 +22,3 @@
  title = {Optical fiber fusion slicing},
  year = {2005}
 }
-
diff --git a/bibtexparser/tests/test_bibtexparser.py b/bibtexparser/tests/test_bibtexparser.py
index 041f0e5..ea5e3df 100644
--- a/bibtexparser/tests/test_bibtexparser.py
+++ b/bibtexparser/tests/test_bibtexparser.py
@@ -73,7 +73,6 @@ class TestBibtexparserWriteMethods(unittest.TestCase):
  volume = {1},
  year = {1987}
 }
-
 """
 
     def test_write_str(self):
diff --git a/bibtexparser/tests/test_bibtexwriter.py b/bibtexparser/tests/test_bibtexwriter.py
index 05dfa43..2443644 100644
--- a/bibtexparser/tests/test_bibtexwriter.py
+++ b/bibtexparser/tests/test_bibtexwriter.py
@@ -38,7 +38,6 @@ class TestBibTexWriter(unittest.TestCase):
  title = {Optical fiber fusion slicing},
  year = {2005}
 }
-
 """
         self.assertEqual(result, expected)
 
@@ -68,7 +67,6 @@ class TestBibTexWriter(unittest.TestCase):
 """@book{abc123,
   author = {test}
 }
-
 """
         self.assertEqual(result, expected)
 
@@ -86,7 +84,6 @@ class TestBibTexWriter(unittest.TestCase):
  author             = {test},
  thisisaverylongkey = {longvalue}
 }
-
 """
         self.assertEqual(result, expected)
 
@@ -121,7 +118,6 @@ class TestBibTexWriter(unittest.TestCase):
  title     = {Optical fiber fusion slicing},
  year      = {2005}
 }
-
 """
         self.assertEqual(result, expected)
 
@@ -173,7 +169,6 @@ class TestBibTexWriter(unittest.TestCase):
  title = {Optical fiber fusion slicing},
  author = {Yablon, A.D.}
 }
-
 """
         self.assertEqual(result, expected)
 
@@ -189,35 +184,35 @@ class TestEntrySorting(unittest.TestCase):
 
     def test_sort_default(self):
         result = bibtexparser.dumps(self.bib_database)
-        expected = "@book{a\n}\n\n@article{b\n}\n\n@book{c\n}\n\n"
+        expected = "@book{a\n}\n\n@article{b\n}\n\n@book{c\n}\n"
         self.assertEqual(result, expected)
 
     def test_sort_none(self):
         writer = BibTexWriter()
         writer.order_entries_by = None
         result = bibtexparser.dumps(self.bib_database, writer)
-        expected = "@article{b\n}\n\n@book{c\n}\n\n@book{a\n}\n\n"
+        expected = "@article{b\n}\n\n@book{c\n}\n\n@book{a\n}\n"
         self.assertEqual(result, expected)
 
     def test_sort_id(self):
         writer = BibTexWriter()
         writer.order_entries_by = ('ID', )
         result = bibtexparser.dumps(self.bib_database, writer)
-        expected = "@book{a\n}\n\n@article{b\n}\n\n@book{c\n}\n\n"
+        expected = "@book{a\n}\n\n@article{b\n}\n\n@book{c\n}\n"
         self.assertEqual(result, expected)
 
     def test_sort_type(self):
         writer = BibTexWriter()
         writer.order_entries_by = ('ENTRYTYPE', )
         result = bibtexparser.dumps(self.bib_database, writer)
-        expected = "@article{b\n}\n\n@book{c\n}\n\n@book{a\n}\n\n"
+        expected = "@article{b\n}\n\n@book{c\n}\n\n@book{a\n}\n"
         self.assertEqual(result, expected)
 
     def test_sort_type_id(self):
         writer = BibTexWriter()
         writer.order_entries_by = ('ENTRYTYPE', 'ID')
         result = bibtexparser.dumps(self.bib_database, writer)
-        expected = "@article{b\n}\n\n@book{a\n}\n\n@book{c\n}\n\n"
+        expected = "@article{b\n}\n\n@book{a\n}\n\n@book{c\n}\n"
         self.assertEqual(result, expected)
 
     def test_sort_missing_field(self):
@@ -233,7 +228,7 @@ class TestEntrySorting(unittest.TestCase):
         writer = BibTexWriter()
         writer.order_entries_by = ('year', )
         result = bibtexparser.dumps(bib_database, writer)
-        expected = "@book{a\n}\n\n@article{b,\n year = {2000}\n}\n\n@book{c,\n year = {2010}\n}\n\n"
+        expected = "@book{a\n}\n\n@article{b,\n year = {2000}\n}\n\n@book{c,\n year = {2010}\n}\n"
         self.assertEqual(result, expected)
 
     def test_unicode_problems(self):
diff --git a/bibtexparser/tests/test_bparser.py b/bibtexparser/tests/test_bparser.py
index dd6f6e1..2f3aadc 100644
--- a/bibtexparser/tests/test_bparser.py
+++ b/bibtexparser/tests/test_bparser.py
@@ -2,6 +2,8 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import unicode_literals
+
+import os
 import unittest
 import codecs
 
@@ -209,7 +211,7 @@ class TestBibtexParserList(unittest.TestCase):
             res = bib.get_entry_list()
         with open('bibtexparser/tests/data/multiple_entries.bib', 'r') as bibfile:
             bib2 = BibTexParser(bibfile.read(), customization=cust2)
-            res2 = bib.get_entry_list()
+            res2 = bib2.get_entry_list()
         self.assertEqual(res, res2)
 
     def test_article_missing_coma(self):
@@ -398,7 +400,7 @@ class TestBibtexParserList(unittest.TestCase):
         self.assertEqual(res, expected)
 
     def test_book_cust_unicode(self):
-        with open('bibtexparser/tests/data/book.bib', 'r') as bibfile:
+        with open('bibtexparser/tests/data/book_capital_AND.bib', 'r') as bibfile:
             bib = BibTexParser(bibfile.read(), customization=customizations_unicode)
             res = bib.get_entry_list()
             expected = [{'ENTRYTYPE': 'book',
diff --git a/bibtexparser/tests/test_crossref_resolving.py b/bibtexparser/tests/test_crossref_resolving.py
index f5cd6e5..d04dfdf 100644
--- a/bibtexparser/tests/test_crossref_resolving.py
+++ b/bibtexparser/tests/test_crossref_resolving.py
@@ -1,7 +1,4 @@
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
+import unittest
 from bibtexparser.bibdatabase import BibDatabase
 from bibtexparser.bparser import BibTexParser