New Upstream Release - bibtexparser
Ready changes
Summary
Merged new upstream version: 1.4.0+ds (was: 1.1.0+ds).
Diff
diff --git a/.editorconfig b/.editorconfig
new file mode 100755
index 0000000..40335f7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+charset=utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+end_of_line = lf
+
+[*.{yaml,yml}]
+indent_size = 2
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..6aaa93d
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,72 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ "master" ]
+ schedule:
+ - cron: '25 22 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'python' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+
+ # If the Autobuild fails above, remove it and uncomment the following three lines.
+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
+
+ # - run: |
+ # echo "Run, Build Application using script"
+ # ./location_of_script_within_repo/buildscript.sh
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
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/CHANGELOG b/CHANGELOG
index a1b888e..fd59e95 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,15 +1,53 @@
-v1.xxx
+v1.4.0
======
+Breaking Changes
+----------------
+* Using common strings in string interpolation is now the default (#311 by @MiWeiss).
+ See the PR for more details, and how to fall back to old behavior.
+
+New Features / Improvements
+---------------------------
+* Add option to adjust alignment of text of multi-line values. (#290 by @michaelfruth)
+* Raise warning if parser is used multiple times (#312 by @mrn97),
+ which leads to a merged library. Set `parser.expect_multiple_parse = True` to disable the warning.
+* Allow preservation of existing order of entry fields in writer (#317 by @michaelfruth)
+
+v1.3.0
+======
+
+Most of these changes come from @MiWeiss personal fork, as one,
+hence have a commit id instead of a PR number in CHANGELOG.
+
+* Support for python 3.10
+* Removing unused dependencies (fb86407 and d4e3f42)
+* No empty extra lines at end of generated files (f84e318)
+* Allow capital AND when splitting author list (a8527ff)
+* Fix problem in `homogenize_latex_encoding` when authors are lists (528714c)
+* Long description in setup.py (#304)
+* Typo fixes (2b2c8ee, 11340f3)
+
+v1.2.0
+======
+
+* Support for python 3 only
+
v1.1.0
======
+* Handles declarations on lines starting with spaces after comments (#199)
+* accept extra characters in @string (#148)
+* Add support for BibLaTeX annotations (#208)
+* Strip the latex command str for latex_to_unicode() (#182)
+* Add comma_last to BibTexWriter (#219)
+* Feature: crossref Support (#216)
* BUGFIX: Fix for pyparsing 2.3.1 (#226)
* NEW: Add support for BibLaTeX annotations (#208)
* NEW: Feature: crossref Support (#216)
* ENH: Handles declarations on lines starting with spaces after comments (#199)
* ENH: Checks for empty citekeys and whitespaces (#213)
+
v1.0.1
======
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
deleted file mode 100644
index 93674a8..0000000
--- a/CONTRIBUTORS.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-- François Boulogne
- Project coordinator
-
-- bibserver's contributors
- for the parser's core and the permission to release this project under LGPLv3 and BSD
-
-- Shuen-Huei (Drake) Guan
- Python 2.7 porting
-
-- Sebastien Diemer
- Bugfix
-
-- Georg C. Brückmann
- Support for non-standard entry types
-
-- Uwe Schmidt
- String replacement
-
-- faph
- coma fixes, optional keys sanitising, refactoring and other improvements
-
-- Steven M. Bellovin
- Fix braces detection
-
-- Sven Goossens
- Support for bibtex with leading spaces
-
-- Michal Grochmal
- Comma first syntax support
-
-- Cschaffner
- New features in bwriter
-
-- Olivier Mangin
- Pyparsing implementation of the parser.
-
-- Blair Bonnett
- customization.splitname() function
diff --git a/MANIFEST.in b/MANIFEST.in
index 3eabe2b..72f1d7e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,5 @@
include *.txt
include *.md
+include COPYING
include docs/Makefile
include docs/source/*
diff --git a/README.rst b/README.rst
index d7a7996..220597f 100644
--- a/README.rst
+++ b/README.rst
@@ -4,12 +4,10 @@ python-bibtexparser
Python library to parse `bibtex <https://en.wikipedia.org/wiki/BibTeX>`_ files.
-IMPORTANT: the library is looking for new maintainers. Please, manifest yourself if you are interested.
-
.. contents::
-Bibtexparser relies on `pyparsing <https://pypi.python.org/pypi/pyparsing>`_ and is compatible with Python 2.7 and 3.3 or newer.
+Bibtexparser relies on `pyparsing <https://pypi.python.org/pypi/pyparsing>`_ and is compatible with Python 3.3 or newer.
Documentation
-------------
@@ -23,7 +21,6 @@ Upgrading
---------
Please, read the changelog before upgrading regarding API modifications.
-Prior version 1.0, we do not hesitate to modify the API to get the best API from your feedbacks.
License
-------
@@ -41,3 +38,7 @@ History and evolutions
The original source code was part of bibserver from `OKFN <http://github.com/okfn/bibserver>`_. This project is released under the AGPLv3. OKFN and the original authors kindly provided the permission to use a subpart of their project (ie the bibtex parser) under LGPLv3. Many thanks to them!
The parser evolved to a new core based on pyparsing.
+
+News (July 7h, 2022): This library has a new maintainer (`@MiWeiss <https://github.com/MiWeiss>`_).
+Versions 1.x will be mostly bugfixes and maintenance.
+In the meantime, we are working on a new version 2.0.0 which will be a complete rewrite of the library.
diff --git a/RELEASE b/RELEASE
index 8cd85ae..9cb1ab6 100644
--- a/RELEASE
+++ b/RELEASE
@@ -3,9 +3,17 @@ How to release
* Update CHANGELOG
* Update version in __init__.py
-* git tag -a 'vX'
-* merge in branch latest
-* Send the package on pypi
+* Commit and open PR on Github to see that all tests pass,
+ and then merge the PR.
+* Check out master locally
+* Build wheel
python setup.py sdist upload
-* tick the doc version on readthedocs
-* Update version in __init__.py
+* Check long description
+ twine check dist/*
+* Upload to pypi-test
+ twine upload --repository testpypi dist/*
+* Upload to pypi
+ twine check dist/*
+* Create release on Github
+* Verify docs are automatically updated
+
diff --git a/bibtexparser/__init__.py b/bibtexparser/__init__.py
index f0ef20f..0518ff0 100644
--- a/bibtexparser/__init__.py
+++ b/bibtexparser/__init__.py
@@ -25,9 +25,7 @@ __all__ = [
'loads', 'load', 'dumps', 'dump', 'bibdatabase',
'bparser', 'bwriter', 'bibtexexpression', 'latexenc', 'customization',
]
-__version__ = '1.1.0'
-
-import sys
+__version__ = '1.4.0'
from . import bibdatabase, bibtexexpression, bparser, bwriter, latexenc, customization
@@ -107,8 +105,4 @@ def dump(bib_database, bibtex_file, writer=None):
"""
if writer is None:
writer = bwriter.BibTexWriter()
- if sys.version_info >= (3, 0):
- bibtex_file.write(writer.write(bib_database))
- else:
- # Encode to UTF-8
- bibtex_file.write(writer.write(bib_database).encode("utf-8"))
+ bibtex_file.write(writer.write(bib_database))
diff --git a/bibtexparser/bibdatabase.py b/bibtexparser/bibdatabase.py
index 2857d25..75600b7 100644
--- a/bibtexparser/bibdatabase.py
+++ b/bibtexparser/bibdatabase.py
@@ -1,17 +1,10 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import OrderedDict
-import sys
import logging
logger = logging.getLogger(__name__)
-if sys.version_info >= (3, 0):
- ustr = str
-else:
- ustr = unicode
-
STANDARD_TYPES = set([
'article',
@@ -90,7 +83,7 @@ class BibDatabase(object):
def entry_sort_key(entry, fields):
result = []
for field in fields:
- result.append(ustr(entry.get(field, '')).lower()) # Sorting always as string
+ result.append(str(entry.get(field, '')).lower()) # Sorting always as string
return tuple(result)
def _make_entries_dict(self):
@@ -276,4 +269,4 @@ def as_text(text_string_or_expression):
(BibDataString, BibDataStringExpression)):
return text_string_or_expression.get_value()
else:
- return ustr(text_string_or_expression)
+ return str(text_string_or_expression)
diff --git a/bibtexparser/bibtexexpression.py b/bibtexparser/bibtexexpression.py
index cf8bdd1..855d2e8 100644
--- a/bibtexparser/bibtexexpression.py
+++ b/bibtexparser/bibtexexpression.py
@@ -99,6 +99,8 @@ class BibtexExpression(object):
ParseException = pp.ParseException
def __init__(self):
+ # Init parse action functions
+ self.set_string_name_parse_action(lambda s, l, t: None)
# Bibtex keywords
@@ -141,7 +143,6 @@ class BibtexExpression(object):
string_expr = pp.delimitedList(
(quoted_value | braced_value | string_name), delim='#'
)('StringExpression')
- self.set_string_expression_parse_action(lambda s, l, t: None)
string_expr.addParseAction(self._string_expr_parse_action)
value = (integer | string_expr)('Value')
@@ -270,17 +271,8 @@ class BibtexExpression(object):
def _string_name_parse_action(self, s, l, t):
return self._string_name_parse_action_fun(s, l, t)
- def set_string_expression_parse_action(self, fun):
- """Set the parseAction for string_expression expression.
-
- .. Note::
-
- See set_string_name_parse_action.
- """
- self._string_expr_parse_action_fun = fun
-
def _string_expr_parse_action(self, s, l, t):
- return self._string_expr_parse_action_fun(s, l, t)
+ return BibDataStringExpression.expression_if_needed(t)
def parseFile(self, file_obj):
return self.main_expression.parseFile(file_obj, parseAll=True)
diff --git a/bibtexparser/bparser.py b/bibtexparser/bparser.py
index c34ba8a..34620a4 100644
--- a/bibtexparser/bparser.py
+++ b/bibtexparser/bparser.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Original source: github.com/okfn/bibserver
@@ -7,9 +6,9 @@
# Etienne Posthumus (epoz)
# Francois Boulogne <fboulogne at april dot org>
-import sys
import io
import logging
+import warnings
from bibtexparser.bibdatabase import (BibDatabase, BibDataString, as_text,
BibDataStringExpression, STANDARD_TYPES)
@@ -20,12 +19,6 @@ logger = logging.getLogger(__name__)
__all__ = ['BibTexParser']
-if sys.version_info >= (3, 0):
- ustr = str
-else:
- ustr = unicode
-
-
def parse(data, *args, **kwargs):
parser = BibTexParser(*args, **kwargs)
return parser.parse(data)
@@ -82,18 +75,24 @@ class BibTexParser(object):
ignore_nonstandard_types=True,
homogenize_fields=False,
interpolate_strings=True,
- common_strings=False,
+ common_strings=True,
add_missing_from_crossref=False):
"""
- Creates a parser for rading BibTeX files
+ Creates a parser for reading BibTeX files
:return: parser
:rtype: `BibTexParser`
"""
+
+ self._parse_call_count = 0
+ #: Parsers can be called multiple times, merging databases, but raising a warning.
+ # Set this to `True` to disable the warning. Default: `False`
+ self.expect_multiple_parse = False
+
self.bib_database = BibDatabase()
#: Load common strings such as months abbreviation
- #: Default: `False`.
+ #: Default: `True` (new in 1.4.0 - was previously `False`)
self.common_strings = common_strings
if self.common_strings:
self.bib_database.load_common_strings()
@@ -150,6 +149,14 @@ class BibTexParser(object):
:return: bibliographic database
:rtype: BibDatabase
"""
+
+ self._parse_call_count += 1
+ if self._parse_call_count == 2 and not self.expect_multiple_parse:
+ warnings.warn("The parser has been called more than once. "
+ "Subsequent parse calls lead to a combined BibTeX library. \n"
+ "To avoid the warning, set property `parser.expect_multiple_parse` to `True`.",
+ category=UserWarning, stacklevel=2)
+
bibtex_file_obj = self._bibtex_file_obj(bibtex_str)
try:
self._expr.parseFile(bibtex_file_obj)
@@ -186,14 +193,6 @@ class BibTexParser(object):
self._expr.set_string_name_parse_action(
lambda s, l, t:
BibDataString(self.bib_database, t[0]))
- if self.interpolate_strings:
- maybe_interpolate = lambda expr: as_text(expr)
- else:
- maybe_interpolate = lambda expr: expr
- self._expr.set_string_expression_parse_action(
- lambda s, l, t:
- maybe_interpolate(
- BibDataStringExpression.expression_if_needed(t)))
# Add notice to logger
self._expr.add_log_function(logger.debug)
@@ -220,12 +219,13 @@ class BibTexParser(object):
def _bibtex_file_obj(self, bibtex_str):
# Some files have Byte-order marks inserted at the start
byte = b'\xef\xbb\xbf'
- if isinstance(bibtex_str, ustr):
- byte = ustr(byte, self.encoding, 'ignore')
- if bibtex_str[0] == byte:
+
+ if isinstance(bibtex_str, str):
+ byte = str(byte, self.encoding, 'ignore')
+ if len(bibtex_str) >= 1 and bibtex_str[0] == byte:
bibtex_str = bibtex_str[1:]
else:
- if bibtex_str[:3] == byte:
+ if len(bibtex_str) >= 3 and bibtex_str[:3] == byte:
bibtex_str = bibtex_str[3:]
bibtex_str = bibtex_str.decode(encoding=self.encoding)
return io.StringIO(bibtex_str)
@@ -239,7 +239,10 @@ class BibTexParser(object):
"""
if not val or val == "{}":
return ''
- return val
+ elif self.interpolate_strings:
+ return as_text(val)
+ else:
+ return val
def _clean_key(self, key):
""" Lowercase a key and return as unicode.
@@ -249,8 +252,8 @@ class BibTexParser(object):
:returns: (unicode) string -- value
"""
key = key.lower()
- if not isinstance(key, ustr):
- return ustr(key, 'utf-8')
+ if not isinstance(key, str):
+ return str(key, 'utf-8')
else:
return key
@@ -321,7 +324,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 9ccbea2..67190e6 100644
--- a/bibtexparser/bwriter.py
+++ b/bibtexparser/bwriter.py
@@ -1,10 +1,11 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Francois Boulogne
# License:
import logging
+from enum import Enum, auto
+from typing import Dict, Callable, Iterable
from bibtexparser.bibdatabase import (BibDatabase, COMMON_STRINGS,
BibDataString,
BibDataStringExpression)
@@ -15,6 +16,38 @@ logger = logging.getLogger(__name__)
__all__ = ['BibTexWriter']
+class SortingStrategy(Enum):
+ """
+ Defines different strategies for sorting the entries not defined in :py:attr:`~.BibTexWriter.display_order` and that are added at the end.
+ """
+ ALPHABETICAL_ASC = auto()
+ """
+ Alphabetical sorting in ascending order.
+ """
+ ALPHABETICAL_DESC = auto()
+ """
+ Alphabetical sorting in descending order.
+ """
+ PRESERVE = auto()
+ """
+ Preserves the order of the entries. Entries are not sorted.
+ """
+
+
+def _apply_sorting_strategy(strategy: SortingStrategy, items: Iterable[str]) -> Iterable[str]:
+ """
+ Sorts the items based on the given sorting strategy.
+ """
+ if strategy == SortingStrategy.ALPHABETICAL_ASC:
+ return sorted(items)
+ elif strategy == SortingStrategy.ALPHABETICAL_DESC:
+ return reversed(sorted(items))
+ elif strategy == SortingStrategy.PRESERVE:
+ return items
+ else:
+ raise NotImplementedError(f"The strategy {strategy.name} is not implemented.")
+
+
def to_bibtex(parsed):
"""
Convenience function for backwards compatibility.
@@ -59,13 +92,19 @@ class BibTexWriter(object):
#: Align values. Determines the maximal number of characters used in any fieldname and aligns all values
# according to that by filling up with single spaces. Default: False
self.align_values = False
+ #: Align multi-line values. Formats a multi-line value such that the text is aligned exactly
+ # on top of each other. Default: False
+ self.align_multiline_values = False
#: Characters(s) for separating BibTeX entries. Default: new line.
self.entry_separator = '\n'
#: Tuple of fields for ordering BibTeX entries. Set to `None` to disable sorting. Default: BibTeX key `('ID', )`.
self.order_entries_by = ('ID', )
- #: Tuple of fields for display order in a single BibTeX entry. Fields not listed here will be displayed
- #: alphabetically at the end. Set to '[]' for alphabetical order. Default: '[]'
+ #: Tuple of fields for display order in a single BibTeX entry. Fields not listed here will be displayed at the
+ # end in the order defined by display_order_sorting. Default: '[]'
self.display_order = []
+ # Sorting strategy for entries not contained in display_order. Entries not defined in display_order are added
+ # at the end in the order defined by this strategy. Default: SortingStrategy.ALPHABETICAL_ASC
+ self.display_order_sorting: SortingStrategy = SortingStrategy.ALPHABETICAL_ASC
#: BibTeX syntax allows comma first syntax
#: (common in functional languages), use this to enable
#: comma first syntax as the bwriter output
@@ -98,7 +137,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))
@@ -110,9 +148,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 = ''
@@ -123,7 +159,7 @@ class BibTexWriter(object):
# first those keys which are both in self.display_order and in entry.keys
display_order = [i for i in self.display_order if i in entry]
# then all the other fields sorted alphabetically
- display_order += [i for i in sorted(entry) if i not in self.display_order]
+ display_order += [i for i in _apply_sorting_strategy(self.display_order_sorting, entry) if i not in self.display_order]
if self.comma_first:
field_fmt = u"\n{indent}, {field:<{field_max_w}} = {value}"
else:
@@ -131,11 +167,31 @@ class BibTexWriter(object):
# Write field = value lines
for field in [i for i in display_order if i not in ['ENTRYTYPE', 'ID']]:
try:
+ value = _str_or_expr_to_bibtex(entry[field])
+
+ if self.align_multiline_values:
+ # Calculate indent of multi-line values. Text from a multiline string
+ # should be aligned, i.e., be exactly on top of each other.
+ # E.g.: title = {Hello
+ # World}
+ # Calculate the indent of "World":
+ # Left of field (whitespaces before e.g. 'title')
+ value_indent = len(self.indent)
+ # Field itself (e.g. len('title'))
+ if self._max_field_width > 0:
+ value_indent += self._max_field_width
+ else:
+ value_indent += len(field)
+ # Right of field ' = ' (<- 3 chars) + '{' (<- 1 char)
+ value_indent += 3 + 1
+
+ value = value.replace('\n', '\n' + value_indent * ' ')
+
bibtex += field_fmt.format(
indent=self.indent,
field=field,
field_max_w=self._max_field_width,
- value=_str_or_expr_to_bibtex(entry[field]))
+ value=value)
except TypeError:
raise TypeError(u"The field %s in entry %s must be a string"
% (field, entry['ID']))
@@ -144,7 +200,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 c8535e3..99c48fb 100644
--- a/bibtexparser/customization.py
+++ b/bibtexparser/customization.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@@ -7,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
@@ -73,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)
@@ -247,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:
@@ -288,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:]
@@ -346,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
@@ -366,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
@@ -418,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
@@ -482,7 +484,7 @@ def doi(record):
if nodoi:
link = record['doi']
if link.startswith('10'):
- link = 'http://dx.doi.org/' + link
+ link = 'https://doi.org/' + link
record['link'].append({"url": link, "anchor": "doi"})
return record
@@ -519,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])
@@ -544,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/bibtexparser/latexenc.py b/bibtexparser/latexenc.py
index e192fc4..c6a2cfb 100644
--- a/bibtexparser/latexenc.py
+++ b/bibtexparser/latexenc.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Original source: github.com/okfn/bibserver
@@ -9,7 +8,6 @@
import itertools
import re
-import sys
import unicodedata
__all__ = ['string_to_latex', 'latex_to_unicode', 'protect_uppercase',
@@ -2686,16 +2684,10 @@ def prepare_unicode_to_latex():
("\uD7FF", "\\mathtt{9}"),
)
- if sys.version_info >= (3, 0):
- unicode_to_latex = to_latex
- unicode_to_crappy_latex1 = to_crappy1
- unicode_to_crappy_latex2 = to_crappy2
- unicode_to_latex_map = dict(unicode_to_latex)
- else:
- unicode_to_latex = tuple((k.decode('unicode-escape'), v) for k, v in to_latex)
- unicode_to_crappy_latex1 = tuple((k.decode('unicode-escape'), v) for k, v in to_crappy1)
- unicode_to_crappy_latex2 = tuple((k.decode('unicode-escape'), v) for k, v in to_crappy2)
- unicode_to_latex_map = dict(unicode_to_latex)
+ unicode_to_latex = to_latex
+ unicode_to_crappy_latex1 = to_crappy1
+ unicode_to_crappy_latex2 = to_crappy2
+ unicode_to_latex_map = dict(unicode_to_latex)
prepare_unicode_to_latex()
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_comma_normal_multiple.bib b/bibtexparser/tests/data/article_comma_normal_multiple.bib
new file mode 100644
index 0000000..e703785
--- /dev/null
+++ b/bibtexparser/tests/data/article_comma_normal_multiple.bib
@@ -0,0 +1,42 @@
+@article{FuMetalhalideperovskite2019,
+ author = {Yongping Fu and Haiming Zhu and Jie Chen and Matthew P. Hautzinger and X.-Y. Zhu and Song Jin},
+ doi = {10.1038/s41578-019-0080-9},
+ journal = {Nature Reviews Materials},
+ month = {feb},
+ number = {3},
+ pages = {169-188},
+ publisher = {Springer Science and Business Media {LLC}},
+ title = {Metal halide perovskite nanostructures for optoelectronic applications and the study of physical properties},
+ url = {https://www.nature.com/articles/s41578-019-0080-9},
+ volume = {4},
+ year = {2019}
+}
+
+@article{SunEnablingSiliconSolar2014,
+ author = {Ke Sun and Shaohua Shen and Yongqi Liang and Paul E. Burrows and Samuel S. Mao and Deli Wang},
+ doi = {10.1021/cr300459q},
+ journal = {Chemical Reviews},
+ month = {aug},
+ number = {17},
+ pages = {8662-8719},
+ publisher = {American Chemical Society ({ACS})},
+ title = {Enabling Silicon for Solar-Fuel Production},
+ url = {http://pubs.acs.org/doi/10.1021/cr300459q},
+ volume = {114},
+ year = {2014}
+}
+
+@article{LiuPhotocatalytichydrogenproduction2016,
+ author = {Maochang Liu and Yubin Chen and Jinzhan Su and Jinwen Shi and Xixi Wang and Liejin Guo},
+ doi = {10.1038/nenergy.2016.151},
+ impactfactor = {54.000},
+ journal = {Nature Energy},
+ month = {sep},
+ number = {11},
+ pages = {16151},
+ publisher = {Springer Science and Business Media {LLC}},
+ title = {Photocatalytic hydrogen production using twinned nanocrystals and an unanchored {NiSx} co-catalyst},
+ url = {http://www.nature.com/articles/nenergy2016151},
+ volume = {1},
+ year = {2016}
+}
diff --git a/bibtexparser/tests/data/article_comma_normal_single.bib b/bibtexparser/tests/data/article_comma_normal_single.bib
new file mode 100644
index 0000000..24a0937
--- /dev/null
+++ b/bibtexparser/tests/data/article_comma_normal_single.bib
@@ -0,0 +1,13 @@
+@article{FuMetalhalideperovskite2019,
+ author = {Yongping Fu and Haiming Zhu and Jie Chen and Matthew P. Hautzinger and X.-Y. Zhu and Song Jin},
+ doi = {10.1038/s41578-019-0080-9},
+ journal = {Nature Reviews Materials},
+ month = {feb},
+ number = {3},
+ pages = {169-188},
+ publisher = {Springer Science and Business Media {LLC}},
+ title = {Metal halide perovskite nanostructures for optoelectronic applications and the study of physical properties},
+ url = {https://www.nature.com/articles/s41578-019-0080-9},
+ volume = {4},
+ year = {2019}
+}
diff --git a/bibtexparser/tests/data/article_multilines.bib b/bibtexparser/tests/data/article_multilines.bib
new file mode 100644
index 0000000..7a6bbe3
--- /dev/null
+++ b/bibtexparser/tests/data/article_multilines.bib
@@ -0,0 +1,13 @@
+@ARTICLE{Cesar2013,
+ author = {Jean César},
+ title = {A mutline line title is very amazing. It should be
+long enough to test multilines... with two lines or should we
+even test three lines... What an amazing title.},
+ year = {2013},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+multilines... and with a french érudit word},
+ comments = {A comment},
+ keyword = {keyword1, keyword2,
+multiline-keyword1, multiline-keyword2},
+}
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/empty.bib b/bibtexparser/tests/data/empty.bib
new file mode 100644
index 0000000..e69de29
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_bibtex_strings.py b/bibtexparser/tests/test_bibtex_strings.py
index 4ca5e3a..5b64c0f 100644
--- a/bibtexparser/tests/test_bibtex_strings.py
+++ b/bibtexparser/tests/test_bibtex_strings.py
@@ -12,24 +12,28 @@ from collections import OrderedDict
class TestStringParse(unittest.TestCase):
def test_single_string_parse_count(self):
+ parser = BibTexParser(common_strings=False)
bibtex_str = '@string{name1 = "value1"}\n\n'
- bib_database = bibtexparser.loads(bibtex_str)
+ bib_database = bibtexparser.loads(bibtex_str, parser)
self.assertEqual(len(bib_database.strings), 1)
def test_multiple_string_parse_count(self):
+ parser = BibTexParser(common_strings=False)
bibtex_str = '@string{name1 = "value1"}\n\n@string{name2 = "value2"}\n\n'
- bib_database = bibtexparser.loads(bibtex_str)
+ bib_database = bibtexparser.loads(bibtex_str, parser)
self.assertEqual(len(bib_database.strings), 2)
def test_single_string_parse(self):
+ parser = BibTexParser(common_strings=False)
bibtex_str = '@string{name1 = "value1"}\n\n'
- bib_database = bibtexparser.loads(bibtex_str)
+ bib_database = bibtexparser.loads(bibtex_str, parser)
expected = {'name1': 'value1'}
self.assertEqual(bib_database.strings, expected)
def test_multiple_string_parse(self):
+ parser = BibTexParser(common_strings=False)
bibtex_str = '@string{name1 = "value1"}\n\n@string{name2 = "value2"}\n\n'
- bib_database = bibtexparser.loads(bibtex_str)
+ bib_database = bibtexparser.loads(bibtex_str, parser)
expected = OrderedDict()
expected['name1'] = 'value1'
expected['name2'] = 'value2'
@@ -50,8 +54,9 @@ class TestStringParse(unittest.TestCase):
self.assertEqual(res, expected)
def test_string_parse_accept_chars(self):
+ parser = BibTexParser(common_strings=False)
bibtex_str = '@string{pub-ieee-std = {IEEE}}\n\n@string{pub-ieee-std:adr = {New York, NY, USA}}'
- bib_database = bibtexparser.loads(bibtex_str)
+ bib_database = bibtexparser.loads(bibtex_str, parser)
self.assertEqual(len(bib_database.strings), 2)
expected = OrderedDict()
expected['pub-ieee-std'] = 'IEEE'
diff --git a/bibtexparser/tests/test_bibtexparser.py b/bibtexparser/tests/test_bibtexparser.py
index 041f0e5..462958a 100644
--- a/bibtexparser/tests/test_bibtexparser.py
+++ b/bibtexparser/tests/test_bibtexparser.py
@@ -32,11 +32,20 @@ class TestBibtexParserParserMethods(unittest.TestCase):
def test_parse_bom_str(self):
parser = BibTexParser()
- with open(self.input_bom_file_path) as bibtex_file:
+ with open(self.input_bom_file_path, encoding="utf-8") as bibtex_file:
bibtex_str = bibtex_file.read()
bibtex_database = parser.parse(bibtex_str)
self.assertEqual(bibtex_database.entries, self.entries_expected)
+ def test_parse_empty(self):
+ parser = BibTexParser()
+ bibtex_database_from_str = parser.parse('')
+ self.assertEqual(bibtex_database_from_str.entries, [])
+
+ with open('bibtexparser/tests/data/empty.bib') as bibtex_file:
+ bibtex_database_from_file = parser.parse_file(bibtex_file)
+ self.assertEqual(bibtex_database_from_file.entries, [])
+
def test_parse_bom_bytes(self):
parser = BibTexParser()
with open(self.input_bom_file_path, 'rb') as bibtex_file:
@@ -73,7 +82,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..1c875fe 100644
--- a/bibtexparser/tests/test_bibtexwriter.py
+++ b/bibtexparser/tests/test_bibtexwriter.py
@@ -2,7 +2,7 @@
import tempfile
import unittest
import bibtexparser
-from bibtexparser.bwriter import BibTexWriter
+from bibtexparser.bwriter import BibTexWriter, SortingStrategy
from bibtexparser.bibdatabase import BibDatabase
@@ -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,180 @@ class TestBibTexWriter(unittest.TestCase):
title = {Optical fiber fusion slicing},
author = {Yablon, A.D.}
}
+"""
+ self.assertEqual(result, expected)
+
+ def test_align_multiline_values(self):
+ with open('bibtexparser/tests/data/article_multilines.bib') as bibtex_file:
+ bib_database = bibtexparser.load(bibtex_file)
+ writer = BibTexWriter()
+ writer.align_multiline_values = True
+ writer.display_order = ["author", "title", "year", "journal", "abstract", "comments", "keyword"]
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@article{Cesar2013,
+ author = {Jean César},
+ title = {A mutline line title is very amazing. It should be
+ long enough to test multilines... with two lines or should we
+ even test three lines... What an amazing title.},
+ year = {2013},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+ multilines... and with a french érudit word},
+ comments = {A comment},
+ keyword = {keyword1, keyword2,
+ multiline-keyword1, multiline-keyword2}
+}
+"""
+ self.assertEqual(result, expected)
+
+ def test_align_multiline_values_with_align(self):
+ with open('bibtexparser/tests/data/article_multilines.bib') as bibtex_file:
+ bib_database = bibtexparser.load(bibtex_file)
+ writer = BibTexWriter()
+ writer.align_multiline_values = True
+ writer.align_values = True
+ writer.display_order = ["author", "title", "year", "journal", "abstract", "comments", "keyword"]
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@article{Cesar2013,
+ author = {Jean César},
+ title = {A mutline line title is very amazing. It should be
+ long enough to test multilines... with two lines or should we
+ even test three lines... What an amazing title.},
+ year = {2013},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+ multilines... and with a french érudit word},
+ comments = {A comment},
+ keyword = {keyword1, keyword2,
+ multiline-keyword1, multiline-keyword2}
+}
+"""
+ self.assertEqual(result, expected)
+
+ def test_display_order_sorting(self):
+ bib_database = BibDatabase()
+ bib_database.entries = [{'ID': 'abc123',
+ 'ENTRYTYPE': 'book',
+ 'b': 'test2',
+ 'a': 'test1',
+ 'd': 'test4',
+ 'c': 'test3',
+ 'e': 'test5'}]
+ # Only 'a' is not ordered. As it's only one element, strategy should not matter.
+ for strategy in SortingStrategy:
+ writer = BibTexWriter()
+ writer.display_order = ['b', 'c', 'd', 'e', 'a']
+ writer.display_order_sorting = strategy
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@book{abc123,
+ b = {test2},
+ c = {test3},
+ d = {test4},
+ e = {test5},
+ a = {test1}
+}
+"""
+ self.assertEqual(result, expected)
+
+ # Test ALPHABETICAL_ASC strategy
+ writer = BibTexWriter()
+ writer.display_order = ['c']
+ writer.display_order_sorting = SortingStrategy.ALPHABETICAL_ASC
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@book{abc123,
+ c = {test3},
+ a = {test1},
+ b = {test2},
+ d = {test4},
+ e = {test5}
+}
+"""
+ self.assertEqual(result, expected)
+
+ # Test ALPHABETICAL_DESC strategy
+ writer = BibTexWriter()
+ writer.display_order = ['c']
+ writer.display_order_sorting = SortingStrategy.ALPHABETICAL_DESC
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@book{abc123,
+ c = {test3},
+ e = {test5},
+ d = {test4},
+ b = {test2},
+ a = {test1}
+}
+"""
+ self.assertEqual(result, expected)
+
+ # Test PRESERVE strategy
+ writer = BibTexWriter()
+ writer.display_order = ['c']
+ writer.display_order_sorting = SortingStrategy.PRESERVE
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@book{abc123,
+ c = {test3},
+ b = {test2},
+ a = {test1},
+ d = {test4},
+ e = {test5}
+}
+"""
+ self.assertEqual(result, expected)
+ def test_align_multiline_values_with_indent(self):
+ with open('bibtexparser/tests/data/article_multilines.bib') as bibtex_file:
+ bib_database = bibtexparser.load(bibtex_file)
+ writer = BibTexWriter()
+ writer.align_multiline_values = True
+ writer.indent = ' ' * 3
+ writer.display_order = ["author", "title", "year", "journal", "abstract", "comments", "keyword"]
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@article{Cesar2013,
+ author = {Jean César},
+ title = {A mutline line title is very amazing. It should be
+ long enough to test multilines... with two lines or should we
+ even test three lines... What an amazing title.},
+ year = {2013},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+ multilines... and with a french érudit word},
+ comments = {A comment},
+ keyword = {keyword1, keyword2,
+ multiline-keyword1, multiline-keyword2}
+}
+"""
+ self.assertEqual(result, expected)
+
+ def test_align_multiline_values_with_align_with_indent(self):
+ with open('bibtexparser/tests/data/article_multilines.bib') as bibtex_file:
+ bib_database = bibtexparser.load(bibtex_file)
+ writer = BibTexWriter()
+ writer.align_multiline_values = True
+ writer.indent = ' ' * 3
+ writer.align_values = True
+ writer.display_order = ["author", "title", "year", "journal", "abstract", "comments", "keyword"]
+ result = bibtexparser.dumps(bib_database, writer)
+ expected = \
+"""@article{Cesar2013,
+ author = {Jean César},
+ title = {A mutline line title is very amazing. It should be
+ long enough to test multilines... with two lines or should we
+ even test three lines... What an amazing title.},
+ year = {2013},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+ multilines... and with a french érudit word},
+ comments = {A comment},
+ keyword = {keyword1, keyword2,
+ multiline-keyword1, multiline-keyword2}
+}
"""
self.assertEqual(result, expected)
@@ -189,35 +358,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 +402,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):
@@ -257,7 +426,7 @@ class TestEntrySorting(unittest.TestCase):
}
"""
bibdb = bibtexparser.loads(bibtex)
- with tempfile.TemporaryFile(mode='w+') as bibtex_file:
+ with tempfile.TemporaryFile(mode='w+', encoding="utf-8") as bibtex_file:
bibtexparser.dump(bibdb, bibtex_file)
# No exception should be raised
diff --git a/bibtexparser/tests/test_bparser.py b/bibtexparser/tests/test_bparser.py
index 516411a..8452f24 100644
--- a/bibtexparser/tests/test_bparser.py
+++ b/bibtexparser/tests/test_bparser.py
@@ -2,9 +2,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+
+import os
import unittest
import codecs
+import warnings
+import bibtexparser
from bibtexparser.bparser import BibTexParser
from bibtexparser.bibdatabase import (COMMON_STRINGS, BibDataStringExpression)
from bibtexparser.customization import *
@@ -53,6 +57,13 @@ def customizations_latex(record):
class TestBibtexParserList(unittest.TestCase):
+ def test_empty_string(self):
+ bib = BibTexParser("", common_strings=False)
+ self.assertEqual(bib.entries, [])
+ self.assertEqual(bib.comments, [])
+ self.assertEqual(bib.preambles, [])
+ self.assertEqual(bib.strings, {})
+
###########
# ARTICLE
###########
@@ -202,7 +213,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):
@@ -311,7 +322,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_article_no_braces(self):
- with open('bibtexparser/tests/data/article_no_braces.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_no_braces.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read())
res = bib.get_entry_list()
expected = [{'ENTRYTYPE': 'article',
@@ -331,7 +343,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_article_special_characters(self):
- with open('bibtexparser/tests/data/article_with_special_characters.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_with_special_characters.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read())
res = bib.get_entry_list()
expected = [{'ENTRYTYPE': 'article',
@@ -351,7 +364,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_article_protection_braces(self):
- with open('bibtexparser/tests/data/article_with_protection_braces.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_with_protection_braces.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read())
res = bib.get_entry_list()
expected = [{'ENTRYTYPE': 'article',
@@ -391,7 +405,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',
@@ -523,7 +537,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_field_name_with_dash_underscore(self):
- with open('bibtexparser/tests/data/article_field_name_with_underscore.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_field_name_with_underscore.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read())
res = bib.get_entry_list()
expected = [{
@@ -543,7 +558,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_string_definitions(self):
- with open('bibtexparser/tests/data/article_with_strings.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_with_strings.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read(), common_strings=True)
res = dict(bib.strings)
expected = COMMON_STRINGS.copy()
@@ -555,7 +571,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_string_is_interpolated(self):
- with open('bibtexparser/tests/data/article_with_strings.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_with_strings.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read(), common_strings=True,
interpolate_strings=True)
res = bib.get_entry_list()
@@ -575,7 +592,8 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res, expected)
def test_string_is_not_interpolated(self):
- with open('bibtexparser/tests/data/article_with_strings.bib', 'r') as bibfile:
+ with open('bibtexparser/tests/data/article_with_strings.bib', 'r',
+ encoding="utf-8") as bibfile:
bib = BibTexParser(bibfile.read(), common_strings=True,
interpolate_strings=False)
res = bib.get_entry_list()[0]
@@ -612,13 +630,82 @@ class TestBibtexParserList(unittest.TestCase):
self.assertEqual(res_dict, expected_dict)
self.assertEqual(bib.preambles, ["Blah blah"])
+ def test_does_not_fail_on_non_bibtex_with_partial(self):
+ bibraw = '''@misc{this looks,
+ like = a = bibtex file but
+ , is not a real one!
+ '''
+ parser = BibTexParser(common_strings=False)
+ bib = parser.parse(bibraw, partial=False)
+ self.assertEqual(bib.entries, [])
+ self.assertEqual(bib.preambles, [])
+ self.assertEqual(bib.strings, {})
+ self.assertEqual(bib.comments, [
+ '@misc{this looks,\n'
+ ' like = a = bibtex file but\n'
+ ' , is not a real one!'])
+
def test_no_citekey_parsed_as_comment(self):
- bib = BibTexParser('@BOOK{, title = "bla"}')
+ bib = BibTexParser('@BOOK{, title = "bla"}', common_strings=False)
self.assertEqual(bib.entries, [])
self.assertEqual(bib.preambles, [])
self.assertEqual(bib.strings, {})
self.assertEqual(bib.comments, ['@BOOK{, title = "bla"}'])
+ def test_parsing_just_once_not_raising_warnings_with_default_settings(self):
+ parser = BibTexParser()
+
+ with open('bibtexparser/tests/data/article_comma_normal_single.bib',
+ 'r', encoding='utf-8') as bibfile:
+ with warnings.catch_warnings(record=True) as warning:
+ warnings.simplefilter("always")
+ bibtexparser.load(bibfile, parser)
+ assert len(warning) == 0
+
+ def test_parsing_twice_raise_warnings_with_default_settings(self):
+ parser = BibTexParser()
+
+ with open('bibtexparser/tests/data/article_comma_normal_single.bib',
+ 'r', encoding='utf-8') as bibfile_first, \
+ open('bibtexparser/tests/data/article_comma_normal_multiple.bib', 'r', encoding='utf-8') \
+ as bibfile_second:
+ with warnings.catch_warnings(record=True) as warning:
+ warnings.simplefilter("always")
+ bibtexparser.load(bibfile_first, parser)
+ bibtexparser.load(bibfile_second, parser)
+ assert len(warning) != 0
+
+ def test_parsing_three_times_raise_warnings_only_once_with_default_settings(self):
+ parser = BibTexParser()
+
+ with open('bibtexparser/tests/data/article_comma_normal_single.bib',
+ 'r', encoding='utf-8') as bibfile_first, \
+ open('bibtexparser/tests/data/article_comma_normal_multiple.bib', 'r', encoding='utf-8') \
+ as bibfile_second, \
+ open('bibtexparser/tests/data/article.bib', 'r', encoding='utf-8') as bibfile_third:
+ with warnings.catch_warnings(record=True) as warning:
+ warnings.simplefilter("always")
+ bibtexparser.load(bibfile_first, parser)
+ bibtexparser.load(bibfile_second, parser)
+ bibtexparser.load(bibfile_third, parser)
+ assert len(warning) == 1
+
+ def test_parsing_three_times_not_raising_a_warning_if_expect_multiple_parse_is_true(self):
+ parser = BibTexParser()
+ parser.expect_multiple_parse = True
+
+ with open('bibtexparser/tests/data/article_comma_normal_single.bib',
+ 'r', encoding='utf-8') as bibfile_first, \
+ open('bibtexparser/tests/data/article_comma_normal_multiple.bib', 'r', encoding='utf-8') \
+ as bibfile_second, \
+ open('bibtexparser/tests/data/article.bib', 'r', encoding='utf-8') as bibfile_third:
+ with warnings.catch_warnings(record=True) as warning:
+ warnings.simplefilter("always")
+ bibtexparser.load(bibfile_first, parser)
+ bibtexparser.load(bibfile_second, parser)
+ bibtexparser.load(bibfile_third, parser)
+ assert len(warning) == 0
+
if __name__ == '__main__':
unittest.main()
diff --git a/bibtexparser/tests/test_bwriter.py b/bibtexparser/tests/test_bwriter.py
index 688182f..d34b40b 100644
--- a/bibtexparser/tests/test_bwriter.py
+++ b/bibtexparser/tests/test_bwriter.py
@@ -8,7 +8,6 @@ from __future__ import unicode_literals
import unittest
import os
import io
-import sys
from bibtexparser.bparser import BibTexParser
from bibtexparser.bwriter import BibTexWriter, to_bibtex
diff --git a/bibtexparser/tests/test_comments.py b/bibtexparser/tests/test_comments.py
index 55e8f1e..679d869 100644
--- a/bibtexparser/tests/test_comments.py
+++ b/bibtexparser/tests/test_comments.py
@@ -131,7 +131,7 @@ Sunt in culpa qui officia deserunt mollit anim id est laborum.
"This is a comment\n" \
"This is a second comment."
expected = "This is a comment\nThis is a second comment."
- bib = BibTexParser(comment)
+ bib = BibTexParser(comment, common_strings=False)
self.assertEqual(bib.comments, [expected])
self.assertEqual(bib.strings, {'foo': 'bar'})
@@ -141,7 +141,7 @@ Sunt in culpa qui officia deserunt mollit anim id est laborum.
"STRING{Baz = \"This should be interpreted as comment.\"}"
expected = "This is a comment\n" \
"STRING{Baz = \"This should be interpreted as comment.\"}"
- bib = BibTexParser(comment)
+ bib = BibTexParser(comment, common_strings=False)
self.assertEqual(bib.comments, [expected])
self.assertEqual(bib.strings, {'foo': 'bar'})
diff --git a/bibtexparser/tests/test_crossref_resolving.py b/bibtexparser/tests/test_crossref_resolving.py
index eae99f4..d04dfdf 100644
--- a/bibtexparser/tests/test_crossref_resolving.py
+++ b/bibtexparser/tests/test_crossref_resolving.py
@@ -1,4 +1,4 @@
-import unittest2 as unittest
+import unittest
from bibtexparser.bibdatabase import BibDatabase
from bibtexparser.bparser import BibTexParser
diff --git a/bibtexparser/tests/test_customization.py b/bibtexparser/tests/test_customization.py
index 2d97538..77450e7 100644
--- a/bibtexparser/tests/test_customization.py
+++ b/bibtexparser/tests/test_customization.py
@@ -42,7 +42,7 @@ class TestBibtexParserMethod(unittest.TestCase):
@unittest.skip('Bug #9')
def test_getnames_braces(self):
- names = ['A. {Delgado de Molina}', 'M. Vign{\\\'e}']
+ names = ['A. {Delgado de Molina}', r'M. Vign{\'e}']
result = getnames(names)
expected = ['Delgado de Molina, A.', 'Vigné, M.']
self.assertEqual(result, expected)
@@ -78,40 +78,40 @@ class TestBibtexParserMethod(unittest.TestCase):
# convert to unicode
###########
def test_convert_to_unicode(self):
- record = {'toto': '{\`a} \`{a}'}
+ record = {'toto': r'{\`a} \`{a}'}
result = convert_to_unicode(record)
- expected = {'toto': 'à à'}
+ expected = {'toto': r'à à'}
self.assertEqual(result, expected)
- record = {'toto': '{\\"u} \\"{u}'}
+ record = {'toto': r'{\"u} \"{u}'}
result = convert_to_unicode(record)
- expected = {'toto': 'ü ü'}
+ expected = {'toto': r'ü ü'}
self.assertEqual(result, expected)
# From issue 121
- record = {'title': '{Two Gedenk\\"uberlieferung der Angelsachsen}'}
+ record = {'title': r'{Two Gedenk\"uberlieferung der Angelsachsen}'}
result = convert_to_unicode(record)
- expected = {'title': 'Two Gedenküberlieferung der Angelsachsen'}
+ expected = {'title': r'Two Gedenküberlieferung der Angelsachsen'}
self.assertEqual(result, expected)
# From issue 161
record = {'title': r"p\^{a}t\'{e}"}
result = convert_to_unicode(record)
- expected = {'title': "pâté"}
+ expected = {'title': r"pâté"}
self.assertEqual(result, expected)
record = {'title': r"\^{i}le"}
result = convert_to_unicode(record)
- expected = {'title': "île"}
+ expected = {'title': r"île"}
self.assertEqual(result, expected)
record = {'title': r"\texttimes{}{\texttimes}\texttimes"}
result = convert_to_unicode(record)
- expected = {'title': "×××"}
+ expected = {'title': r"×××"}
self.assertEqual(result, expected)
###########
# homogenize
###########
def test_homogenize(self):
- record = {'toto': 'à {\`a} \`{a}'}
+ record = {'toto': r'à {\`a} \`{a}'}
result = homogenize_latex_encoding(record)
- expected = {'toto': '{\`a} {\`a} {\`a}'}
+ expected = {'toto': r'{\`a} {\`a} {\`a}'}
self.assertEqual(result, expected)
###########
@@ -119,7 +119,7 @@ class TestBibtexParserMethod(unittest.TestCase):
###########
def test_add_plaintext_fields(self):
record = {
- 'title': 'On-line {Recognition} of {Handwritten} {Mathematical} {Symbols}',
+ 'title': r'On-line {Recognition} of {Handwritten} {Mathematical} {Symbols}',
'foobar': ['{FFT} {Foobar}', '{foobar}'],
'foobar2': {'item1': '{FFT} {Foobar}', 'item2': '{foobar}'}
}
diff --git a/debian/changelog b/debian/changelog
index 0a8d4fc..c2b05ac 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+bibtexparser (1.4.0+ds-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Tue, 02 May 2023 03:25:44 -0000
+
bibtexparser (1.1.0+ds-5) unstable; urgency=medium
* Set upstream metadata fields: Repository-Browse.
diff --git a/docs/source/demo.py b/docs/source/demo.py
new file mode 100644
index 0000000..10c89b6
--- /dev/null
+++ b/docs/source/demo.py
@@ -0,0 +1,53 @@
+import logging
+import logging.config
+import io
+
+logger = logging.getLogger(__name__)
+
+logging.config.dictConfig({
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'formatters': {
+ 'standard': {
+ 'format': '%(asctime)s [%(levelname)s] %(name)s %(funcName)s:%(lineno)d: %(message)s'
+ },
+ },
+ 'handlers': {
+ 'default': {
+ 'level':'DEBUG',
+ 'formatter': 'standard',
+ 'class':'logging.StreamHandler',
+ },
+ },
+ 'loggers': {
+ '': {
+ 'handlers': ['default'],
+ 'level': 'DEBUG',
+ 'formatter': 'standard',
+ 'propagate': True
+ }
+ }
+})
+
+
+if __name__ == '__main__':
+ bibtex_source = """@ARTICLE{Cesar2013,
+ author = {Jean César},
+ title = {An amazing title},
+ year = {2013},
+ month = {1},
+ volume = {12},
+ pages = {12--23},
+ journal = {Nice Journal},
+ abstract = {This is an abstract. This line should be long enough to test
+ multilines...},
+ comments = {A comment},
+ keywords = {keyword1, keyword2},
+ }
+ """
+
+ from bibtexparser.bparser import BibTexParser
+
+ with io.StringIO(bibtex_source) as f:
+ bp = BibTexParser(f.read())
+ print(bp.get_entry_list())
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 77bd0ab..aa425a6 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -8,16 +8,15 @@ Welcome to BibtexParser's documentation!
:Author: François Boulogne, Olivier Mangin, Lucas Verney, and other contributors.
-:Devel: `github.com project <https://github.com/sciunto-org/python-bibtexparser>`_
-:Mirror: `git.sciunto.org <https://git.sciunto.org/mirror/python-bibtexparser>`_
+:Source Code: `github.com project <https://github.com/sciunto-org/python-bibtexparser>`_
:Bugs: `github.com <https://github.com/sciunto-org/python-bibtexparser/issues>`_
:Generated: |today|
:License: LGPL v3 or BSD
:Version: |release|
BibtexParser is a python library to parse bibtex files. The code relies on `pyparsing <http://pyparsing.wikispaces.com/>`_ and is tested with unittests.
+BibtexParser is used in more than 1300 open-source `repositories <https://github.com/sciunto-org/python-bibtexparser/network/dependents?package_id=UGFja2FnZS01MDI4NzgxNg%3D%3D>`_.
-If you use BibtexParser for your project, feel free to send me an email. I would be happy to hear that and to mention your project in the documentation.
Contents:
@@ -29,7 +28,6 @@ Contents:
bibtexparser.rst
logging.rst
bibtex_conv.rst
- who.rst
Other projects
diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst
index 8c4a8da..5d1eef3 100644
--- a/docs/source/tutorial.rst
+++ b/docs/source/tutorial.rst
@@ -375,7 +375,7 @@ Using the code would yield the following output.
bp = BibTexParser(interpolate_strings=False)
bib_database = bp.parse(bibtex)
bib_database.entries[0]
- as_text(bd.entries[0]['author'])
+ as_text(bib_database.entries[0]['author'])
.. code-block:: python
diff --git a/docs/source/who.rst b/docs/source/who.rst
deleted file mode 100644
index 8068f1f..0000000
--- a/docs/source/who.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Who uses BibtexParser?
-======================
-
-If your project uses BibtexParser, you can ask for the addition of a link in this list.
-
-* http://timotheepoisot.fr/2013/11/10/shared-bibtex-file-markdown/
-* https://github.com/Phyks/BMC
-* http://aurelien.naldi.info/research/publications.html
-* http://robot.kut.ac.kr/publications
-* https://git.atelo.org/etlapale/bibgen
-* https://onmenwhostareongraphs.wordpress.com/2015/06/09/graph-display-software-for-author-relationships-with-bibtex-files/
-* https://github.com/vitorfs/parsifal
diff --git a/requirements.txt b/requirements.txt
index 50c1129..e7c21d1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1 @@
-future>=0.16.0
pyparsing>=2.0.3
-unittest2>=1.1.0
diff --git a/setup.py b/setup.py
index 5e1e1e7..b0b465e 100644
--- a/setup.py
+++ b/setup.py
@@ -12,16 +12,22 @@ with open('bibtexparser/__init__.py') as fh:
version = line.strip().split()[-1][1:-1]
break
+
+def load_readme():
+ with open("README.rst") as f:
+ return f.read()
+
+
setup(
- name = 'bibtexparser',
- version = version,
- url = "https://github.com/sciunto-org/python-bibtexparser",
- author = "Francois Boulogne and other contributors",
- license = "LGPLv3 or BSD",
- 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'}
+ name='bibtexparser',
+ version=version,
+ url="https://github.com/sciunto-org/python-bibtexparser",
+ author="Francois Boulogne and other contributors",
+ license="LGPLv3 or BSD",
+ author_email="code@mweiss.ch",
+ description="Bibtex parser for python 3",
+ long_description_content_type="text/x-rst",
+ long_description=load_readme(),
+ packages=['bibtexparser'],
+ install_requires=['pyparsing>=2.0.3'],
)