diff --git a/.docstr.yaml b/.docstr.yaml index 34ed99e9..1de73876 100644 --- a/.docstr.yaml +++ b/.docstr.yaml @@ -1,6 +1,6 @@ -skip_magic: True -skip_file_doc: True -skip_init: True -skip_private: True -ignore_setter: True -fail-under: 100 \ No newline at end of file +skip_magic: true +skip_file_doc: true +skip_init: true +skip_private: true +ignore_setter: true +fail-under: 100 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 13251d8f..952e1105 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,12 +13,12 @@ name: "CodeQL" on: push: - branches: [ "main", "v1" ] + branches: ["main", "v1"] pull_request: # The branches below must be a subset of the branches above - branches: [ "main", "v1" ] + branches: ["main", "v1"] schedule: - - cron: '25 22 * * 1' + - cron: '25 22 * * 1' jobs: analyze: @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'python' ] + 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 @@ -48,11 +48,11 @@ jobs: # 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 @@ -61,7 +61,7 @@ jobs: # ℹ️ 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. + # 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: | diff --git a/.github/workflows/linting_and_unittests.yml b/.github/workflows/linting_and_unittests.yml index 59ddc017..86e3c925 100644 --- a/.github/workflows/linting_and_unittests.yml +++ b/.github/workflows/linting_and_unittests.yml @@ -1,48 +1,30 @@ -name: "Linting and unittests" +name: "unittests" on: push: paths: - - '**.py' + - '**.py' pull_request: branches: ["main"] jobs: - lint: - runs-on: ubuntu-latest - container: - image: python:3.8 - - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[lint] - - name: Lint format with `black` - run: black bibtexparser tests setup.py --check - - name: Lint imports with `isort` - run: isort bibtexparser tests setup.py --check-only --profile black - - name: Check docstr-coverage with `docstr-coverage` - run: docstr-coverage bibtexparser build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.8, 3.9, '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[test] - - name: Test with pytest - run: | - pytest tests - + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[test] + - name: Test with pytest + run: | + pytest tests diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000..b8a74b8a --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,17 @@ +name: pre-commit + +on: + workflow_dispatch: + pull_request: + push: + branches: [main] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: actions/setup-python@main + with: + python-version: '<3.12' # https://github.com/HunterMcGushion/docstr_coverage/issues/137 + - uses: pre-commit/action@main diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 35a522b0..e8b640b2 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 - + - name: Set up Python uses: actions/setup-python@v4 with: @@ -37,7 +37,7 @@ jobs: with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository-url: https://test.pypi.org/legacy/ - + - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..19bdedd5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,61 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-toml + - id: debug-statements +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.12.0 + hooks: + - id: pretty-format-yaml + args: [--preserve-quotes, --autofix, --indent, '2'] + - id: pretty-format-toml + args: [--autofix, --indent, '4', --trailing-commas] +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: python-check-blanket-noqa + - id: python-check-blanket-type-ignore + - id: python-no-log-warn + - id: python-use-type-annotations + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal +- repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + args: [--safe, --quiet, --line-length=100] +- repo: https://github.com/PyCQA/autoflake + rev: v2.3.0 + hooks: + - id: autoflake + args: [--in-place, --remove-unused-variable] +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort + args: ["--force-single-line", "--line-length=100", "--profile=black"] +- repo: https://github.com/asottile/pyupgrade + rev: v3.15.1 + hooks: + - id: pyupgrade + args: [--py36-plus] +- repo: https://github.com/PyCQA/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + args: ["--ignore=E722,W503,E203", --max-line-length=110, "--per-file-ignores=*/__init__.py:F401"] +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.5.0 + hooks: + - id: setup-cfg-fmt +- repo: https://github.com/HunterMcGushion/docstr_coverage + rev: v2.3.0 + hooks: + - id: docstr-coverage + args: ["bibtexparser"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99f67523..14441b23 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -## Contributing +## Contributing Thanks heaps for being interested in contributing to python-bibtexparser. @@ -8,15 +8,16 @@ We are always looking for people to improve the library. Contributions include, 2. Providing bugfixing PRs 3. Implementing any of the issues or continuing any of the PRs labelled with `needs help` or `good first issue`. -### Some guidelines: +### Some guidelines 1. Be nice! Were all doing this in our free time; no one is obligated to do anything. -2. Add sufficient tests to your PRs +2. Add sufficient tests to your PRs. 3. Document your code. 4. Don't hesitate to ask questions. ### Version 1 vs version 2 -Also note that there are currently two independent "default" branches: + +Also note that there are currently two independent "default" branches: First, `main`, where we maintain the `v2` of bibtexparser, which is a complete re-write and currently still in beta and not feature complete. Second, `v1` where we maintain the stable `v1` version of bibtexparser. Note that on `v1` we accept only small, non-breaking changes and are planning to stop support as soon as `v2` reaches reasonable stability. The two branches are never going to be merged anymore, thus if you want to change something for both versions, you will have to open two PRs. @@ -25,8 +26,8 @@ Issues are labelled `v1` and `v2`, correspondingly. ### Dev-Dependencies, testing and linting on v2. -To install the dev dependencies, run `pip install -e .[test,lint,docs]` from within the cloned repository. Then: +To install the dev dependencies, run `pip install -e .[test,docs]` from within the cloned repository. Then: - To test your code, run `pytest .` -- To lint your code (required for CI/CD to pass), run: `black bibtexparser tests docs && isort bibtexparser tests docs --profile black` +- To lint your code (enforces code style), run: `pre-commit run --all-files` (if you need to install pre-commit, run `pip install pre-commit`). - To build and preview the docs, navigate into `docs` and run `make html`. Then open the `index.html` file in the `docs/build/html` folder. diff --git a/LICENSE b/LICENSE index 1e10333b..4d30978e 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 2781ae67..1999044a 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ If instead, you want to use v1, install it using: pip install bibtexparser~=1.0 ``` -Note that all development and maintenance effort is focussed on v2. +Note that all development and maintenance effort is focussed on v2. Small PRs for v1 are still accepted, but only as long as they are backwards compatible and don't introduce much additional technical debt. -Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1). +Development of version one happens on the dedicated [v1 branch](https://github.com/sciunto-org/python-bibtexparser/tree/v1). -The remainder of this README is specific to v2. +The remainder of this README is specific to v2. ## Documentation Go check out our documentation on [https://bibtexparser.readthedocs.io/en/main/](https://bibtexparser.readthedocs.io/en/main/). @@ -66,7 +66,7 @@ new_bibtex_string = bibtexparser.write_string(bib_database, ) ``` -These examples really only show the bare minimum. +These examples really only show the bare minimum. Consult the documentation for a list of available middleware, parsing options and write-formatting options. ## V2 Architecture and Terminology @@ -76,21 +76,21 @@ Consult the documentation for a list of available middleware, parsing options an The architecture consists of the following components: #### Library -Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order). +Reflects the contents of a parsed bibtex files, including all comments, entries, strings, preamples and their metadata (e.g. order). #### A Splitter Splits a bibtex string into basic blocks (Entry, String, Preamble, ...), with correspondingly split content (e.g. fields on Entry, key-value on String, ...). -The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (`@....`) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock". +The splitter aims to be forgiving when facing invalid bibtex: A line starting with a block definition (``@....``) ends the previous block, even if not yet every bracket is closed, failing the parsing of the previous block. Correspondingly, one block type is "ParsingFailedBlock". #### Middleware -Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resoling crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, no not mutate their input, but return a modified copy. +Middleware layers transform a library and its blocks, for example by decoding latex special characters, interpolating string references, resoling crossreferences or re-ordering blocks. Thus, the choice of middleware allows to customize parsing and writing to ones specific usecase. Note: Middlewares, by default, no not mutate their input, but return a modified copy. #### Writer -Writes the content of a bibtex library to a `.bib` file. Optional formatting parameters can be passed using a corresponding dedicated data structure. +Writes the content of a bibtex library to a ``.bib`` file. Optional formatting parameters can be passed using a corresponding dedicated data structure. ## About -Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer. +Since 2022, `bibtexparser` is primarily written and maintained by Michael Weiss ([@MiWeiss](https://github.com/MiWeiss/)). In 2024, Tom de Geus ([@tdegeus](https://github.com/tdegeus)) joined as co-maintainer. Credits and thanks to the many contributors who helped creating this library, including François Boulogne ([@sciunto](https://github.com/sciunto/), creator of the first version) and Olivier Mangin ([@omangin](https://github.com/omangin/), long-term contributor). diff --git a/RELEASE b/RELEASE index 655bbb31..58deacd1 100644 --- a/RELEASE +++ b/RELEASE @@ -1,4 +1,4 @@ -Tagged version are automatically released to pypi using github actions. +Tagged version are automatically released to pypi using github actions. Oder/Manual release instructions: @@ -17,4 +17,3 @@ Oder/Manual release instructions: twine upload dist/* * Create release on Github * Verify docs are automatically updated - diff --git a/bibtexparser/__init__.py b/bibtexparser/__init__.py index 127902a5..8239d3d2 100644 --- a/bibtexparser/__init__.py +++ b/bibtexparser/__init__.py @@ -1,7 +1,10 @@ import bibtexparser.exceptions import bibtexparser.middlewares import bibtexparser.model -from bibtexparser.entrypoint import parse_file, parse_string, write_file, write_string +from bibtexparser.entrypoint import parse_file +from bibtexparser.entrypoint import parse_string +from bibtexparser.entrypoint import write_file +from bibtexparser.entrypoint import write_string from bibtexparser.library import Library from bibtexparser.writer import BibtexFormat diff --git a/bibtexparser/entrypoint.py b/bibtexparser/entrypoint.py index 0d77607f..f1bad67d 100644 --- a/bibtexparser/entrypoint.py +++ b/bibtexparser/entrypoint.py @@ -1,11 +1,17 @@ import warnings -from typing import Iterable, List, Optional, TextIO, Union +from typing import Iterable +from typing import List +from typing import Optional +from typing import TextIO +from typing import Union from .library import Library from .middlewares.middleware import Middleware -from .middlewares.parsestack import default_parse_stack, default_unparse_stack +from .middlewares.parsestack import default_parse_stack +from .middlewares.parsestack import default_unparse_stack from .splitter import Splitter -from .writer import BibtexFormat, write +from .writer import BibtexFormat +from .writer import write def _build_parse_stack( @@ -27,7 +33,7 @@ def _build_parse_stack( return list(parse_stack) parse_stack_types = [type(m) for m in parse_stack] - append_stack_types = set([type(m) for m in append_middleware]) + append_stack_types = {type(m) for m in append_middleware} stack_types_intersect = set(parse_stack_types).intersection(append_stack_types) if len(stack_types_intersect) > 0: warnings.warn( @@ -57,7 +63,7 @@ def _build_unparse_stack( return list(unparse_stack) parse_stack_types = [type(m) for m in unparse_stack] - append_stack_types = set([type(m) for m in prepend_middleware]) + append_stack_types = {type(m) for m in prepend_middleware} stack_types_intersect = set(parse_stack_types).intersection(append_stack_types) if len(stack_types_intersect) > 0: warnings.warn( diff --git a/bibtexparser/exceptions.py b/bibtexparser/exceptions.py index 161c78d0..f0107689 100644 --- a/bibtexparser/exceptions.py +++ b/bibtexparser/exceptions.py @@ -1,4 +1,5 @@ -from typing import List, Optional +from typing import List +from typing import Optional class ParsingException(Exception): diff --git a/bibtexparser/library.py b/bibtexparser/library.py index 2f6a4252..fecd1bcb 100644 --- a/bibtexparser/library.py +++ b/bibtexparser/library.py @@ -1,15 +1,15 @@ -from typing import Dict, List, Union - -from .model import ( - Block, - DuplicateBlockKeyBlock, - Entry, - ExplicitComment, - ImplicitComment, - ParsingFailedBlock, - Preamble, - String, -) +from typing import Dict +from typing import List +from typing import Union + +from .model import Block +from .model import DuplicateBlockKeyBlock +from .model import Entry +from .model import ExplicitComment +from .model import ImplicitComment +from .model import ParsingFailedBlock +from .model import Preamble +from .model import String # TODO Use functools.lru_cache for library properties (which create lists when called) @@ -24,9 +24,7 @@ def __init__(self, blocks: Union[List[Block], None] = None): if blocks is not None: self.add(blocks) - def add( - self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = False - ): + def add(self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = False): """Add blocks to library. The adding is key-safe, i.e., it is made sure that no duplicate keys are added. @@ -34,7 +32,8 @@ def add( a DuplicateKeyBlock. :param blocks: Block or list of blocks to add. - :param fail_on_duplicate_key: If True, raises ValueError if a block was replaced with a DuplicateKeyBlock. + :param fail_on_duplicate_key: + If True, raises ValueError if a block was replaced with a DuplicateKeyBlock. """ if isinstance(blocks, Block): blocks = [blocks] @@ -49,7 +48,7 @@ def add( if fail_on_duplicate_key: duplicate_keys = [] for original, added in zip(blocks, _added_blocks): - if not original is added and isinstance(added, DuplicateBlockKeyBlock): + if original is not added and isinstance(added, DuplicateBlockKeyBlock): duplicate_keys.append(added.key) if len(duplicate_keys) > 0: @@ -74,9 +73,7 @@ def remove(self, blocks: Union[List[Block], Block]): elif isinstance(block, String): del self._strings_by_key[block.key] - def replace( - self, old_block: Block, new_block: Block, fail_on_duplicate_key: bool = True - ): + def replace(self, old_block: Block, new_block: Block, fail_on_duplicate_key: bool = True): """Replace a block with another block, at the same position. :param old_block: Block to replace. @@ -196,7 +193,5 @@ def preambles(self) -> List[Preamble]: def comments(self) -> List[Union[ExplicitComment, ImplicitComment]]: """All comment blocks in the library, preserving order of insertion.""" return [ - block - for block in self._blocks - if isinstance(block, (ExplicitComment, ImplicitComment)) + block for block in self._blocks if isinstance(block, (ExplicitComment, ImplicitComment)) ] diff --git a/bibtexparser/middlewares/__init__.py b/bibtexparser/middlewares/__init__.py index 1f5a9382..89b0af1e 100644 --- a/bibtexparser/middlewares/__init__.py +++ b/bibtexparser/middlewares/__init__.py @@ -1,29 +1,21 @@ -from bibtexparser.middlewares.enclosing import ( - AddEnclosingMiddleware, - RemoveEnclosingMiddleware, -) +from bibtexparser.middlewares.enclosing import AddEnclosingMiddleware +from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware from bibtexparser.middlewares.interpolate import ResolveStringReferencesMiddleware -from bibtexparser.middlewares.latex_encoding import ( - LatexDecodingMiddleware, - LatexEncodingMiddleware, -) -from bibtexparser.middlewares.middleware import BlockMiddleware, LibraryMiddleware -from bibtexparser.middlewares.month import ( - MonthAbbreviationMiddleware, - MonthIntMiddleware, - MonthLongStringMiddleware, -) -from bibtexparser.middlewares.names import ( - MergeCoAuthors, - MergeNameParts, - NameParts, - SeparateCoAuthors, - SplitNameParts, -) +from bibtexparser.middlewares.latex_encoding import LatexDecodingMiddleware +from bibtexparser.middlewares.latex_encoding import LatexEncodingMiddleware +from bibtexparser.middlewares.middleware import BlockMiddleware +from bibtexparser.middlewares.middleware import LibraryMiddleware +from bibtexparser.middlewares.month import MonthAbbreviationMiddleware +from bibtexparser.middlewares.month import MonthIntMiddleware +from bibtexparser.middlewares.month import MonthLongStringMiddleware +from bibtexparser.middlewares.names import MergeCoAuthors +from bibtexparser.middlewares.names import MergeNameParts +from bibtexparser.middlewares.names import NameParts +from bibtexparser.middlewares.names import SeparateCoAuthors +from bibtexparser.middlewares.names import SplitNameParts from bibtexparser.middlewares.sorting_blocks import SortBlocksByTypeAndKeyMiddleware -from bibtexparser.middlewares.sorting_entry_fields import ( - SortFieldsAlphabeticallyMiddleware, - SortFieldsCustomMiddleware, -) +from bibtexparser.middlewares.sorting_entry_fields import SortFieldsAlphabeticallyMiddleware +from bibtexparser.middlewares.sorting_entry_fields import SortFieldsCustomMiddleware -from .parsestack import default_parse_stack, default_unparse_stack +from .parsestack import default_parse_stack +from .parsestack import default_unparse_stack diff --git a/bibtexparser/middlewares/enclosing.py b/bibtexparser/middlewares/enclosing.py index 8aa0c327..08835f39 100644 --- a/bibtexparser/middlewares/enclosing.py +++ b/bibtexparser/middlewares/enclosing.py @@ -1,6 +1,10 @@ -from typing import Tuple, Union +from typing import Tuple +from typing import Union -from bibtexparser.model import Entry, Field, String +from bibtexparser.library import Library +from bibtexparser.model import Entry +from bibtexparser.model import Field +from bibtexparser.model import String from .middleware import BlockMiddleware @@ -51,7 +55,7 @@ def _strip_enclosing(value: str) -> Tuple[str, Union[str, None]]: return value, "no-enclosing" # docstr-coverage: inherited - def transform_entry(self, entry: Entry, library: "Library") -> Entry: + def transform_entry(self, entry: Entry, library: Library) -> Entry: field: Field metadata = dict() for field in entry.fields: @@ -62,7 +66,7 @@ def transform_entry(self, entry: Entry, library: "Library") -> Entry: return entry # docstr-coverage: inherited - def transform_string(self, string: String, library: "Library") -> String: + def transform_string(self, string: String, library: Library) -> String: stripped, enclosing = self._strip_enclosing(string.value) string.value = stripped string.parser_metadata[self.metadata_key()] = enclosing @@ -101,8 +105,7 @@ def __init__( if default_enclosing not in ("{", '"'): raise ValueError( - "default_enclosing must be either '{' or '\"'" - f"not '{default_enclosing}'" + "default_enclosing must be either '{' or '\"'" f"not '{default_enclosing}'" ) self._default_enclosing = default_enclosing self._reuse_previous_enclosing = reuse_previous_enclosing @@ -113,9 +116,7 @@ def __init__( def metadata_key(cls) -> str: return "remove_enclosing" - def _enclose( - self, value: str, metadata_enclosing: str, apply_int_rule: bool - ) -> str: + def _enclose(self, value: str, metadata_enclosing: str, apply_int_rule: bool) -> str: enclosing = self._default_enclosing if self._reuse_previous_enclosing and metadata_enclosing is not None: enclosing = metadata_enclosing @@ -129,8 +130,7 @@ def _enclose( if enclosing == "no-enclosing": return value raise ValueError( - f"enclosing must be either '{{' or '\"' or 'no-enclosing', " - f"not '{enclosing}'" + f"enclosing must be either '{{' or '\"' or 'no-enclosing', " f"not '{enclosing}'" ) # docstr-coverage: inherited @@ -142,13 +142,9 @@ def transform_entry(self, entry: Entry, *args, **kwargs) -> Entry: for field in entry.fields: apply_int_rule = field.key in ENTRY_POTENTIALLY_INT_FIELDS prev_encoding = ( - metadata_enclosing.get(field.key, None) - if metadata_enclosing is not None - else None - ) - field.value = self._enclose( - field.value, prev_encoding, apply_int_rule=apply_int_rule + metadata_enclosing.get(field.key, None) if metadata_enclosing is not None else None ) + field.value = self._enclose(field.value, prev_encoding, apply_int_rule=apply_int_rule) return entry # docstr-coverage: inherited diff --git a/bibtexparser/middlewares/interpolate.py b/bibtexparser/middlewares/interpolate.py index d9e10766..baf0a636 100644 --- a/bibtexparser/middlewares/interpolate.py +++ b/bibtexparser/middlewares/interpolate.py @@ -3,7 +3,8 @@ from typing import Any from bibtexparser.library import Library -from bibtexparser.model import Entry, Field +from bibtexparser.model import Entry +from bibtexparser.model import Field from .enclosing import REMOVED_ENCLOSING_KEY from .middleware import LibraryMiddleware @@ -41,10 +42,7 @@ def transform(self, library: Library) -> Library: raised_enclosing_warning = False for entry in library.entries: resolved_fields = list() - if ( - not raised_enclosing_warning - and REMOVED_ENCLOSING_KEY in entry.parser_metadata - ): + if not raised_enclosing_warning and REMOVED_ENCLOSING_KEY in entry.parser_metadata: raised_enclosing_warning = True warnings.warn( ( diff --git a/bibtexparser/middlewares/latex_encoding.py b/bibtexparser/middlewares/latex_encoding.py index fdb559ac..bb11b09f 100644 --- a/bibtexparser/middlewares/latex_encoding.py +++ b/bibtexparser/middlewares/latex_encoding.py @@ -1,19 +1,23 @@ import abc import logging import re -from typing import List, Optional, Tuple +from typing import List +from typing import Optional +from typing import Tuple import pylatexenc -from pylatexenc.latex2text import LatexNodes2Text, MacroTextSpec -from pylatexenc.latexencode import ( - RULE_REGEX, - UnicodeToLatexConversionRule, - UnicodeToLatexEncoder, -) +from pylatexenc.latex2text import LatexNodes2Text +from pylatexenc.latex2text import MacroTextSpec +from pylatexenc.latexencode import RULE_REGEX +from pylatexenc.latexencode import UnicodeToLatexConversionRule +from pylatexenc.latexencode import UnicodeToLatexEncoder from bibtexparser.exceptions import PartialMiddlewareException from bibtexparser.library import Library -from bibtexparser.model import Block, Entry, MiddlewareErrorBlock, String +from bibtexparser.model import Block +from bibtexparser.model import Entry +from bibtexparser.model import MiddlewareErrorBlock +from bibtexparser.model import String from .middleware import BlockMiddleware from .names import NameParts @@ -33,9 +37,7 @@ def _transform_python_value_string(self, python_string: str) -> Tuple[str, str]: raise NotImplementedError("called abstract method") # docstr-coverage: inherited - def _transform_all_strings( - self, list_of_strings: List[str], errors: List[str] - ) -> List[str]: + def _transform_all_strings(self, list_of_strings: List[str], errors: List[str]) -> List[str]: """Called for every python (value, not key) string found on Entry and String blocks""" res = [] for s in list_of_strings: @@ -52,9 +54,7 @@ def transform_entry(self, entry: Entry, library: Library) -> Block: field.value, e = self._transform_python_value_string(field.value) errors.append(e) elif isinstance(field.value, NameParts): - field.value.first = self._transform_all_strings( - field.value.first, errors - ) + field.value.first = self._transform_all_strings(field.value.first, errors) field.value.last = self._transform_all_strings(field.value.last, errors) field.value.von = self._transform_all_strings(field.value.von, errors) field.value.jr = self._transform_all_strings(field.value.jr, errors) @@ -162,9 +162,7 @@ def __init__( allow_parallel_execution=True, ) - if decoder is not None and ( - keep_braced_groups is not None or keep_math_mode is not None - ): + if decoder is not None and (keep_braced_groups is not None or keep_math_mode is not None): raise ValueError( "Cannot specify both encoder and one of " "`keep_braced_groups` or `keep_braced_groups`." @@ -174,9 +172,7 @@ def __init__( # Defaults (not specified as defaults in args, # to make sure we can identify if they were specified) - keep_braced_groups = ( - keep_braced_groups if keep_braced_groups is not None else False - ) + keep_braced_groups = keep_braced_groups if keep_braced_groups is not None else False keep_math_mode = keep_math_mode if keep_math_mode is not None else True if decoder is None: diff --git a/bibtexparser/middlewares/middleware.py b/bibtexparser/middlewares/middleware.py index 0ddd1525..9514d810 100644 --- a/bibtexparser/middlewares/middleware.py +++ b/bibtexparser/middlewares/middleware.py @@ -1,17 +1,16 @@ import abc import logging from copy import deepcopy -from typing import Collection, Union +from typing import Collection +from typing import Union from bibtexparser.library import Library -from bibtexparser.model import ( - Block, - Entry, - ExplicitComment, - ImplicitComment, - Preamble, - String, -) +from bibtexparser.model import Block +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble +from bibtexparser.model import String class Middleware(abc.ABC): @@ -94,9 +93,7 @@ def transform(self, library: "Library") -> "Library": blocks.extend(transformed) # Case 4: Something else. Error. else: - raise TypeError( - f"Illegal output type from transform_block: {type(transformed)}" - ) + raise TypeError(f"Illegal output type from transform_block: {type(transformed)}") return Library(blocks=blocks) def transform_block( diff --git a/bibtexparser/middlewares/month.py b/bibtexparser/middlewares/month.py index 31c9ec20..73a8f5f9 100644 --- a/bibtexparser/middlewares/month.py +++ b/bibtexparser/middlewares/month.py @@ -1,9 +1,12 @@ import abc from collections import OrderedDict -from typing import Tuple, Union +from typing import Tuple +from typing import Union from bibtexparser.library import Library -from bibtexparser.model import Block, Entry, Field +from bibtexparser.model import Block +from bibtexparser.model import Entry +from bibtexparser.model import Field from .middleware import BlockMiddleware @@ -31,9 +34,7 @@ def transform_entry(self, entry: Entry, library: "Library") -> Block: return entry @abc.abstractmethod - def resolve_month_field_val( - self, month_field: Field - ) -> Tuple[Union[str, int], str]: + def resolve_month_field_val(self, month_field: Field) -> Tuple[Union[str, int], str]: """Transform the month field. Args: diff --git a/bibtexparser/middlewares/names.py b/bibtexparser/middlewares/names.py index 7152870a..d067222d 100644 --- a/bibtexparser/middlewares/names.py +++ b/bibtexparser/middlewares/names.py @@ -3,11 +3,17 @@ Much of the code is taken from Blair Bonnetts never merged v0 pull request (https://github.com/sciunto-org/python-bibtexparser/pull/140). """ + import abc import dataclasses -from typing import List, Literal, Tuple +from typing import List +from typing import Literal +from typing import Tuple -from bibtexparser.model import Block, Entry, Field, MiddlewareErrorBlock +from bibtexparser.model import Block +from bibtexparser.model import Entry +from bibtexparser.model import Field +from bibtexparser.model import MiddlewareErrorBlock from .middleware import BlockMiddleware @@ -139,9 +145,7 @@ def escape_last_slash(string: str) -> str: jr = " ".join(self.jr) if self.jr else None von_last = " ".join(name for name in [von, last] if name) - return ", ".join( - escape_last_slash(name) for name in [von_last, jr, first] if name - ) + return ", ".join(escape_last_slash(name) for name in [von_last, jr, first] if name) class SplitNameParts(_NameTransformerMiddleware): @@ -193,16 +197,14 @@ def metadata_key(cls) -> str: def _transform_field_value(self, name) -> List[str]: if not isinstance(name, list) and all(isinstance(n, NameParts) for n in name): - raise ValueError("Expected a list of NameParts, got {}. ".format(name)) + raise ValueError(f"Expected a list of NameParts, got {name}. ") if self.style == "last": return [n.merge_last_name_first for n in name] elif self.style == "first": return [n.merge_first_name_first for n in name] else: - raise ValueError( - """Expected "first" or "last" style, got {}. """.format(self.style) - ) + raise ValueError(f"""Expected "first" or "last" style, got {self.style}. """) def parse_single_name_into_parts(name, strict=True): @@ -478,10 +480,10 @@ def parse_single_name_into_parts(name, strict=True): else: lcases = cases[0] - def rindex(l, x, default): - """Returns the index of the rightmost occurence of x in l.""" - for i in range(len(l) - 1, -1, -1): - if l[i] == x: + def rindex(k, x, default): + """Returns the index of the rightmost occurrence of x in k.""" + for i in range(len(k) - 1, -1, -1): + if k[i] == x: return i return default diff --git a/bibtexparser/middlewares/parsestack.py b/bibtexparser/middlewares/parsestack.py index b700e800..227fccc4 100644 --- a/bibtexparser/middlewares/parsestack.py +++ b/bibtexparser/middlewares/parsestack.py @@ -1,10 +1,8 @@ from typing import List from bibtexparser.middlewares import ResolveStringReferencesMiddleware -from bibtexparser.middlewares.enclosing import ( - AddEnclosingMiddleware, - RemoveEnclosingMiddleware, -) +from bibtexparser.middlewares.enclosing import AddEnclosingMiddleware +from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware from .middleware import Middleware @@ -12,12 +10,8 @@ def default_parse_stack(allow_inplace_modification: bool = True) -> List[Middleware]: """The default parse stack to be applied after splitting, if not specified otherwise.""" return [ - ResolveStringReferencesMiddleware( - allow_inplace_modification=allow_inplace_modification - ), - RemoveEnclosingMiddleware( - allow_inplace_modification=allow_inplace_modification - ), + ResolveStringReferencesMiddleware(allow_inplace_modification=allow_inplace_modification), + RemoveEnclosingMiddleware(allow_inplace_modification=allow_inplace_modification), ] diff --git a/bibtexparser/middlewares/sorting_blocks.py b/bibtexparser/middlewares/sorting_blocks.py index b69326c0..5ff5f135 100644 --- a/bibtexparser/middlewares/sorting_blocks.py +++ b/bibtexparser/middlewares/sorting_blocks.py @@ -1,16 +1,17 @@ from copy import deepcopy -from dataclasses import dataclass, field -from typing import List, Tuple, Type +from dataclasses import dataclass +from dataclasses import field +from typing import List +from typing import Tuple +from typing import Type from bibtexparser.library import Library -from bibtexparser.model import ( - Block, - Entry, - ExplicitComment, - ImplicitComment, - Preamble, - String, -) +from bibtexparser.model import Block +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble +from bibtexparser.model import String from .middleware import LibraryMiddleware @@ -57,8 +58,7 @@ def _verify_all_types_are_block_types(sort_order): for t in sort_order: if not issubclass(t, Block): raise ValueError( - "Sort order must only contain Block subclasses, " - f"but got {str(t)}" + "Sort order must only contain Block subclasses, " f"but got {str(t)}" ) @staticmethod @@ -74,9 +74,7 @@ def _block_junks(blocks: List[Block]) -> List[_BlockJunk]: # (this happens for comments, preambles and parsing-failed blocks, for example) pass - if not ( - isinstance(block, ExplicitComment) or isinstance(block, ImplicitComment) - ): + if not (isinstance(block, ExplicitComment) or isinstance(block, ImplicitComment)): # We added a non-comment block, hence we finish the junk and # start a new one block_junks.append(current_junk) @@ -107,9 +105,7 @@ def _sort_key(block_junk): block_junks.sort(key=_sort_key) return Library( - blocks=[ - block for block_junk in block_junks for block in block_junk.blocks - ] + blocks=[block for block_junk in block_junks for block in block_junk.blocks] ) else: diff --git a/bibtexparser/middlewares/sorting_entry_fields.py b/bibtexparser/middlewares/sorting_entry_fields.py index 600924a1..5f19ffa6 100644 --- a/bibtexparser/middlewares/sorting_entry_fields.py +++ b/bibtexparser/middlewares/sorting_entry_fields.py @@ -1,7 +1,8 @@ from typing import Tuple from bibtexparser.library import Library -from bibtexparser.model import Block, Entry +from bibtexparser.model import Block +from bibtexparser.model import Entry from .middleware import BlockMiddleware @@ -49,7 +50,7 @@ def __init__( self._order = order if len(self._order) != len(set(self._order)): - duplicate_keys = set([x for x in self._order if self._order.count(x) > 1]) + duplicate_keys = {x for x in self._order if self._order.count(x) > 1} raise ValueError( "Order list must not contain duplicates. " "The following keys are duplicated: " diff --git a/bibtexparser/model.py b/bibtexparser/model.py index 71283dd4..4558efaf 100644 --- a/bibtexparser/model.py +++ b/bibtexparser/model.py @@ -1,5 +1,9 @@ import abc -from typing import Any, Dict, List, Optional, Set +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Set class Block(abc.ABC): @@ -117,9 +121,7 @@ def __repr__(self): class Preamble(Block): """Bibtex Blocks of the ``@preamble`` type, e.g. ``@preamble{This is a preamble}``.""" - def __init__( - self, value: str, start_line: Optional[int] = None, raw: Optional[str] = None - ): + def __init__(self, value: str, start_line: Optional[int] = None, raw: Optional[str] = None): super().__init__(start_line, raw) self._value = value @@ -136,18 +138,13 @@ def __str__(self): return f"Preamble (line: {self.start_line}): `{self.value}`" def __repr__(self): - return ( - f"Preamble(value=`{self.value}`, " - f"start_line={self.start_line}, raw=`{self.raw}`)" - ) + return f"Preamble(value=`{self.value}`, " f"start_line={self.start_line}, raw=`{self.raw}`)" class ExplicitComment(Block): """Bibtex Blocks of the ``@comment`` type, e.g. ``@comment{This is a comment}``.""" - def __init__( - self, comment: str, start_line: Optional[int] = None, raw: Optional[str] = None - ): + def __init__(self, comment: str, start_line: Optional[int] = None, raw: Optional[str] = None): super().__init__(start_line, raw) self._comment = comment @@ -173,9 +170,7 @@ def __repr__(self): class ImplicitComment(Block): """Bibtex outside of an ``@{...}`` block, which is treated as a comment.""" - def __init__( - self, comment: str, start_line: Optional[int] = None, raw: Optional[str] = None - ): + def __init__(self, comment: str, start_line: Optional[int] = None, raw: Optional[str] = None): super().__init__(start_line, raw) self._comment = comment @@ -241,10 +236,7 @@ def __str__(self): return f"Field (line: {self.start_line}, key: `{self.key}`): `{self.value}`" def __repr__(self): - return ( - f"Field(key=`{self.key}`, value=`{self.value}`, " - f"start_line={self.start_line})" - ) + return f"Field(key=`{self.key}`, value=`{self.value}`, " f"start_line={self.start_line})" class Entry(Block): @@ -370,9 +362,7 @@ def items(self): ] + [(f.key, f.value) for f in self.fields] def __str__(self): - lines = [ - f"Entry (line: {self.start_line}, type: `{self.entry_type}`, key: `{self.key}`):" - ] + lines = [f"Entry (line: {self.start_line}, type: `{self.entry_type}`, key: `{self.key}`):"] lines.extend([f"\t`{f.key}` = `{f.value}`" for f in self.fields]) return "\n".join(lines) diff --git a/bibtexparser/splitter.py b/bibtexparser/splitter.py index ac19f776..93d88ec7 100644 --- a/bibtexparser/splitter.py +++ b/bibtexparser/splitter.py @@ -1,23 +1,23 @@ import logging import re -from typing import List, Optional, Set, Tuple, Union - -from .exceptions import ( - BlockAbortedException, - ParserStateException, - RegexMismatchException, -) +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import Union + +from .exceptions import BlockAbortedException +from .exceptions import ParserStateException +from .exceptions import RegexMismatchException from .library import Library -from .model import ( - DuplicateFieldKeyBlock, - Entry, - ExplicitComment, - Field, - ImplicitComment, - ParsingFailedBlock, - Preamble, - String, -) +from .model import DuplicateFieldKeyBlock +from .model import Entry +from .model import ExplicitComment +from .model import Field +from .model import ImplicitComment +from .model import ParsingFailedBlock +from .model import Preamble +from .model import String class Splitter: @@ -154,11 +154,7 @@ def _is_escaped(): elif next_mark.group(0) == "{" and not currently_quote_escaped: num_open_curls += 1 continue - elif ( - next_mark.group(0) == "}" - and not currently_quote_escaped - and num_open_curls > 0 - ): + elif next_mark.group(0) == "}" and not currently_quote_escaped and num_open_curls > 0: num_open_curls -= 1 continue @@ -188,9 +184,7 @@ def _is_escaped(): end_index=next_mark.start() - 1, ) - def _move_to_end_of_entry( - self, first_key_start: int - ) -> Tuple[List[Field], int, Set[str]]: + def _move_to_end_of_entry(self, first_key_start: int) -> Tuple[List[Field], int, Set[str]]: """Move to the end of the entry and return the fields and the end index.""" result = [] keys = set() @@ -290,7 +284,8 @@ def split(self, library: Optional[Library] = None) -> Library: except BlockAbortedException as e: logging.warning( - f"Parsing of `{m_val}` block (line {start_line}) aborted on line {self._current_line} " + f"Parsing of `{m_val}` block (line {start_line}) " + f"aborted on line {self._current_line} " f"due to syntactical error in bibtex:\n {e.abort_reason}" ) logging.info( @@ -308,8 +303,7 @@ def split(self, library: Optional[Library] = None) -> Library: except ParserStateException as e: # This is a bug in the parser, not in the bibtex. We should not continue. logging.error( - "python-bibtexparser detected an invalid state. " - "Please report this bug." + "python-bibtexparser detected an invalid state. Please report this bug." ) logging.error(e.message) raise e @@ -321,9 +315,7 @@ def split(self, library: Optional[Library] = None) -> Library: ) raise e - self._reset_block_status( - current_char_index=self._current_char_index + 1 - ) + self._reset_block_status(current_char_index=self._current_char_index + 1) else: # Part of implicit comment continue @@ -379,16 +371,13 @@ def _handle_entry(self, m, m_val) -> Union[Entry, ParsingFailedBlock]: elif comma_mark.group(0) != ",": self._unaccepted_mark = comma_mark raise BlockAbortedException( - abort_reason="Expected comma after entry key," - f" but found {comma_mark.group(0)}", + abort_reason=f"Expected comma after entry key, but found {comma_mark.group(0)}", end_index=comma_mark.end(), ) else: self._open_brackets += 1 key = self.bibstr[m.end() + 1 : comma_mark.start()].strip() - fields, end_index, duplicate_keys = self._move_to_end_of_entry( - comma_mark.end() - ) + fields, end_index, duplicate_keys = self._move_to_end_of_entry(comma_mark.end()) entry = Entry( start_line=start_line, diff --git a/bibtexparser/writer.py b/bibtexparser/writer.py index bd7f0d2a..a3d7af7c 100644 --- a/bibtexparser/writer.py +++ b/bibtexparser/writer.py @@ -1,16 +1,16 @@ from copy import deepcopy -from typing import List, Optional, Union +from typing import List +from typing import Optional +from typing import Union from .library import Library -from .model import ( - Entry, - ExplicitComment, - Field, - ImplicitComment, - ParsingFailedBlock, - Preamble, - String, -) +from .model import Entry +from .model import ExplicitComment +from .model import Field +from .model import ImplicitComment +from .model import ParsingFailedBlock +from .model import Preamble +from .model import String VAL_SEP = " = " PARSING_FAILED_COMMENT = "% WARNING Parsing failed for the following {n} lines." @@ -52,22 +52,16 @@ def _treat_preamble(block: Preamble, bibtex_format: "BibtexFormat") -> List[str] return [f"@preamble{{{block.value}}}\n"] -def _treat_impl_comment( - block: ImplicitComment, bibtex_format: "BibtexFormat" -) -> List[str]: +def _treat_impl_comment(block: ImplicitComment, bibtex_format: "BibtexFormat") -> List[str]: # Note: No explicit escaping is done here - that should be done in middleware return [block.comment, "\n"] -def _treat_expl_comment( - block: ExplicitComment, bibtex_format: "BibtexFormat" -) -> List[str]: +def _treat_expl_comment(block: ExplicitComment, bibtex_format: "BibtexFormat") -> List[str]: return ["@comment{", block.comment, "}\n"] -def _treat_failed_block( - block: ParsingFailedBlock, bibtex_format: "BibtexFormat" -) -> List[str]: +def _treat_failed_block(block: ParsingFailedBlock, bibtex_format: "BibtexFormat") -> List[str]: lines = len(block.raw.splitlines()) parsing_failed_comment = PARSING_FAILED_COMMENT.format(n=lines) return [parsing_failed_comment, "\n", block.raw, "\n"] diff --git a/dev-utilities/bibtex-nameparsing/parsename.py b/dev-utilities/bibtex-nameparsing/parsename.py index 0c80e898..71c0b7a7 100644 --- a/dev-utilities/bibtex-nameparsing/parsename.py +++ b/dev-utilities/bibtex-nameparsing/parsename.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import os.path import shutil @@ -13,7 +12,7 @@ except ImportError: from tempfile import mkdtemp - class TemporaryDirectory(object): + class TemporaryDirectory: def __init__(self): self.name = mkdtemp() @@ -45,7 +44,7 @@ def evaluate_bibtex_parsename(tempdir, names): # Write entries for each string in the list. with open(os.path.join(tempdir, "parsename.bib"), "w") as bibfile: for i, name in enumerate(names): - bibfile.write("@parsename{{case{0:d}, author={{{1:s}}}}}\n".format(i, name)) + bibfile.write(f"@parsename{{case{i:d}, author={{{name:s}}}}}\n") # Run BibTeX. proc = subprocess.Popen( @@ -64,7 +63,7 @@ def evaluate_bibtex_parsename(tempdir, names): return False # No error. - with open(os.path.join(tempdir, "parsename.bbl"), "r") as bblfile: + with open(os.path.join(tempdir, "parsename.bbl")) as bblfile: print(bblfile.read()) return True diff --git a/dev-utilities/bibtex-nameparsing/splitnames.py b/dev-utilities/bibtex-nameparsing/splitnames.py index ea645345..2c6041c2 100644 --- a/dev-utilities/bibtex-nameparsing/splitnames.py +++ b/dev-utilities/bibtex-nameparsing/splitnames.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import os.path import shutil @@ -13,7 +12,7 @@ except ImportError: from tempfile import mkdtemp - class TemporaryDirectory(object): + class TemporaryDirectory: def __init__(self): self.name = mkdtemp() @@ -45,9 +44,7 @@ def evaluate_bibtex_splitnames(tempdir, names): # Write entries for each string in the list. with open(os.path.join(tempdir, "splitnames.bib"), "w") as bibfile: for i, name in enumerate(names): - bibfile.write( - "@splitnames{{case{0:d}, author={{{1:s}}}}}\n".format(i, name) - ) + bibfile.write(f"@splitnames{{case{i:d}, author={{{name:s}}}}}\n") # Run BibTeX. proc = subprocess.Popen( @@ -66,7 +63,7 @@ def evaluate_bibtex_splitnames(tempdir, names): return False # No error. - with open(os.path.join(tempdir, "splitnames.bbl"), "r") as bblfile: + with open(os.path.join(tempdir, "splitnames.bbl")) as bblfile: print(bblfile.read()) return True diff --git a/docs/source/biber.rst b/docs/source/biber.rst index 349543ba..0d9391d5 100644 --- a/docs/source/biber.rst +++ b/docs/source/biber.rst @@ -7,4 +7,4 @@ Due to its simpel and high-level nature, this library should not only support Bi with ease, as they all share the same general syntax. That said, we did not explicitely check against all of biber and biblatex features. Should you detect anything which is not supported, -please open an issue or send a pull request. \ No newline at end of file +please open an issue or send a pull request. diff --git a/docs/source/bibtex_conv.rst b/docs/source/bibtex_conv.rst index ed219c0d..a783ba39 100644 --- a/docs/source/bibtex_conv.rst +++ b/docs/source/bibtex_conv.rst @@ -33,4 +33,3 @@ Here are some interesting projects using .bib-based libraries (but not necessari | https://github.com/FlamingTempura/bibtex-tidy (github repo) * | Display your bibliography in html pages: | http://www.monperrus.net/martin/bibtexbrowser/ - diff --git a/docs/source/bibtexparser.rst b/docs/source/bibtexparser.rst index 627cd029..4b8db4b8 100644 --- a/docs/source/bibtexparser.rst +++ b/docs/source/bibtexparser.rst @@ -37,4 +37,4 @@ Full API ------------------------------------------------------------------ .. autoclass:: bibtexparser.BibtexFormat - :members: \ No newline at end of file + :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index bf51d9d8..10270d1f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # BibtexParser documentation build configuration file, created by # sphinx-quickstart on Thu Aug 1 13:30:23 2013. @@ -176,14 +175,7 @@ # -- Options for LaTeX output -------------------------------------------------- -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - #'preamble': '', -} +latex_elements = {} # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). @@ -222,9 +214,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ("index", "bibtexparser", "BibtexParser Documentation", ["F. Boulogne"], 1) -] +man_pages = [("index", "bibtexparser", "BibtexParser Documentation", ["F. Boulogne"], 1)] # If true, show URL addresses after external links. # man_show_urls = False diff --git a/docs/source/customize.rst b/docs/source/customize.rst index 76d5328c..c7b2c3df 100644 --- a/docs/source/customize.rst +++ b/docs/source/customize.rst @@ -195,8 +195,10 @@ If you want to change these defaults, specify them in the call to the super cons Community-Provided Middleware ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + We encourage users to provide their own middleware layers and share them with the community. We are happy to provide a list of community-provided middleware layers here, so please let us know if you have written one! +See ``CONTRIBUTING.md`` for suggestions how to contribute. Metadata Fields ^^^^^^^^^^^^^^^ diff --git a/docs/source/index.rst b/docs/source/index.rst index a1f48e47..a67252e8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,7 +1,7 @@ .. BibtexParser documentation master file, created by sphinx-quickstart on Thu Aug 1 13:30:23 2013. You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. + contain the root ``toctree`` directive. Welcome to BibtexParser's documentation! ======================================== @@ -37,4 +37,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/docs/source/install.rst b/docs/source/install.rst index ffda408d..7b6c8ba8 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -2,7 +2,6 @@ Installation ============ - Requirements ------------ @@ -31,7 +30,7 @@ To install the latest release candidate (currently required to use v2) using pip pip install --pre bibtexparser -without the ``--pre`` option, you will get the latest `v1` version. +without the ``--pre`` option, you will get the latest v1 version. It has a different API and is not directly compatible with v2. @@ -50,5 +49,3 @@ Or, if you want to install dev dependencies: .. code-block:: sh pip install .[test,lint,docs] - - diff --git a/docs/source/migrate.rst b/docs/source/migrate.rst index df0b72db..6bd3c313 100644 --- a/docs/source/migrate.rst +++ b/docs/source/migrate.rst @@ -5,21 +5,21 @@ Migrating: v1 -> v2 Before you start migrating, we recommend you read the docs regarding the terminology and architecture of bibtexparser v2, and have a quick look at the tutorial to get a feeling for the new API. -Status of `v2` --------------- +Status of v2 +------------ -The `v2` branch is well tested and reasonably stable, but it is not yet widely adopted - as an early adopter, +The v2 branch is well tested and reasonably stable, but it is not yet widely adopted - as an early adopter, you may encounter some bugs. If you do, please report them on the issue tracker. Also, note that some interfaces may change sightly before we release v2.0.0 as stable. -Some customizations from `v1` are not implemented in `v2`, as we doubt they are widely used. If you need one of +Some customizations from v1 are not implemented in v2, as we doubt they are widely used. If you need one of these features, please let us know on the issue tracker. Differences between v1 and v2 ----------------------------- -From a user perspective `v2` has the following advantages over `v1`: +From a user perspective v2 has the following advantages over v1: * Order of magnitudes faster * Easily customizable parsing and writing @@ -28,21 +28,21 @@ From a user perspective `v2` has the following advantages over `v1`: * Robuster handling of de- and encoding (special chars, ...). * Permissive MIT license -Implementation-wise, the main difference of `v2` is that it does not depend on `pyparsing` anymore. +Implementation-wise, the main difference of v2 is that it does not depend on ``pyparsing`` anymore. Also, it does not implement any en-/decoding of special characters, but relies on external libraries for this. To implement these changes, we had to make some breaking changes to the API. Amongst others, be aware that: * The used vocabulary has slightly changed. [:ref:`docs `] * The primary entrypoints have changed. [:ref:`docs `] -* The module `bibtexparser.customizations` been replaced by the module `bibtexparser.middleware` [:ref:`docs `] +* The module ``bibtexparser.customizations`` been replaced by the module ``bibtexparser.middleware`` [:ref:`docs `] Minimal Migration Guide (without customizations) ------------------------------------------------ -The following code snippets show how to migrate from `v1` to `v2` for the most common use cases. -It aims to provide the quickest way to get `v1` code running with `v2`. -As such, it makes reduced use of the new features of `v2` and makes use of backwards compatibility APIs where possible. +The following code snippets show how to migrate from v1 to v2 for the most common use cases. +It aims to provide the quickest way to get v1 code running with v2. +As such, it makes reduced use of the new features of v2 and makes use of backwards compatibility APIs where possible. .. warning:: This migration guide is not complete. It covers the parts which are presumably the trickiest ones to migrate. Further migration steps should be needed, but should either be trivial or very specific to your use case @@ -63,7 +63,7 @@ To make sure that users dont "migrate by accident" to bibtex v2, we changed the # v2 import bibtexparser library = bibtexparser.parse_file(bibtex_file) - + For most usecases, these default settings should be sufficient, even though there are differences in the default configurations between v1 and v2 and thus the outcome you will see. @@ -73,7 +73,7 @@ Read the :ref:`customization docs ` for instruction on how to custo Accessing the library ~~~~~~~~~~~~~~~~~~~~~ -While in v1 entries were represented as dicts, in v2 they are represented as `Entry` objects. +While in v1 entries were represented as dicts, in v2 they are represented as ``Entry`` objects. .. code-block:: python @@ -92,14 +92,15 @@ While in v1 entries were represented as dicts, in v2 they are represented as `En Similarly, other block types (comments, strings, ...) are now also represented as dedicated :ref:`object types `, but for them, the migration is straight forward and we will not go into detail here. -.. note:: Working with the actual field instances (`entry.fields` or `entry.fields_dict`) and not the shorthand notation - (`entry[field_key]`) makes additional information (e.g. raw bibtex or start line of the field in the parsed file) available. - We recommend you check out the new data types and their attributes. +.. note:: + Working with the actual field instances (``entry.fields`` or ``entry.fields_dict``) and not the shorthand notation + (``entry[field_key]``) makes additional information (e.g. raw bibtex or start line of the field in the parsed file) available. + We recommend you check out the new data types and their attributes. Writing a bibtex file (possibly with customizations) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The way to write a bibtex file has changed fundamentally in `v2`, and is now handeled in a fashion very similar to the parsing. +The way to write a bibtex file has changed fundamentally in v2, and is now handeled in a fashion very similar to the parsing. See the :ref:`writing quickstart ` and :ref:`writing formatting ` for more information. diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 611fc158..d2d09978 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -10,18 +10,18 @@ For more detailed information, please refer to the corresponding sections of the Prerequisite: Vocabulary ======================== -* An **entry** refers to a citable item, e.g. `@book{...}`, `@article{...}`, etc. -* A **preamble** is a `@preamble{...}` block. -* A **string** is `@string{...}`. -* An **explicit comment** is written as `@comment{...}`. -* An **implicit comment** is any text not within any `@...{...}` block. +* An **entry** refers to a citable item, e.g. ``@book{...}``, ``@article{...}``, etc. +* A **preamble** is a ``@preamble{...}`` block. +* A **string** is ``@string{...}``. +* An **explicit comment** is written as ``@comment{...}``. +* An **implicit comment** is any text not within any ``@...{...}`` block. * Each of the above is called a **block**, i.e., any .bib file is a collection of blocks of the above types. In an entry, you can find -* an **entry type** like `article`, `book`, etc. -* and **entry key**, e.g. `Cesar2013` in `@article{Cesar2013, ...}`. -* and **fields**, which are the key-value pairs in the entry, e.g. `author = {Jean César}`. +* an **entry type** like ``article``, ``book``, etc. +* and **entry key**, e.g. ``Cesar2013`` in ``@article{Cesar2013, ...}``. +* and **fields**, which are the key-value pairs in the entry, e.g. ``author = {Jean César}``. * each field has a **field key** and a **field value**. @@ -59,7 +59,7 @@ Let's attempt to parse this string using the default bibtexparser configuration: library = bibtexparser.parse_string(bibtex_str) # or bibtexparser.parse_file("my_file.bib") -The returned `library` object provides access to the parsed blocks, i.e., parsed high-level segments of the bibtex such as entries, comments, strings and preambles. +The returned ``library`` object provides access to the parsed blocks, i.e., parsed high-level segments of the bibtex such as entries, comments, strings and preambles. You can access them by type, or iterate over all blocks, as shown below: .. code-block:: python @@ -102,7 +102,7 @@ Example of exposed attributes: first_field.key # The field key, e.g. "author" first_field.value # The field value, e.g. "Albert Einstein and Boris Johnson" -For a list of all available attributes, see the documentation of the `bibtexparser.model` module. +For a list of all available attributes, see the documentation of the ``bibtexparser.model`` module. Step 2: Error Checking @@ -123,7 +123,7 @@ and you should check for their presence to make sure mistakes are not going unde Obviously, in your code, you may want to go beyond simply printing a statement when faced with failed_blocks. -Here, the actual failed blocks provided in `library.failed_blocks` +Here, the actual failed blocks provided in ``library.failed_blocks`` will provide you some more information (exceeding this tutorial, see the corresponding section of the docs for more detail). @@ -155,4 +155,4 @@ This can be quickly achieved using the following: # } As you can see, the content (besides some white-spacing and other layout) is identical to the original string. -Naturally, the writer can be configured to your needs. For more information on that, see :ref:`the customization documentation `. \ No newline at end of file +Naturally, the writer can be configured to your needs. For more information on that, see :ref:`the customization documentation `. diff --git a/setup.py b/setup.py index 4565454a..343fd130 100644 --- a/setup.py +++ b/setup.py @@ -47,11 +47,6 @@ def load_readme(): "pytest-cov", # Code coverage "jupyter", # For runnable examples ], - "lint": [ - "black==23.3.0", - "isort==5.12.0", - "docstr-coverage==2.2.0", - ], "docs": [ "sphinx", ], diff --git a/tests/e2e_example.py b/tests/e2e_example.py index d7fbe7c1..4ebea307 100644 --- a/tests/e2e_example.py +++ b/tests/e2e_example.py @@ -1,12 +1,10 @@ from textwrap import dedent import bibtexparser -from bibtexparser.middlewares.names import ( - MergeCoAuthors, - MergeNameParts, - SeparateCoAuthors, - SplitNameParts, -) +from bibtexparser.middlewares.names import MergeCoAuthors +from bibtexparser.middlewares.names import MergeNameParts +from bibtexparser.middlewares.names import SeparateCoAuthors +from bibtexparser.middlewares.names import SplitNameParts bibtex_string = dedent( """\ @@ -54,11 +52,11 @@ def test_example(): \tjournal = {Nature Reviews Materials}, \tyear = {2019} } - - + + @comment{This is a comment.} - - + + @preamble{e = mc^2}""" ).strip() ) diff --git a/tests/middleware_tests/middleware_test_util.py b/tests/middleware_tests/middleware_test_util.py index df5b332b..1d22b2a6 100644 --- a/tests/middleware_tests/middleware_test_util.py +++ b/tests/middleware_tests/middleware_test_util.py @@ -3,7 +3,10 @@ from bibtexparser.library import Library from bibtexparser.middlewares.middleware import Middleware -from bibtexparser.model import Block, ExplicitComment, ImplicitComment, Preamble +from bibtexparser.model import Block +from bibtexparser.model import ExplicitComment +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble def assert_block_does_not_change( @@ -12,15 +15,11 @@ def assert_block_does_not_change( """Utility to make sure blocks of some types are not changed by middleware.""" block_type = block_type.lower() if block_type == "preamble": - block = Preamble( - start_line=5, raw="@Preamble{a_x + b_x^2}", value="a_x + b_x^2" - ) + block = Preamble(start_line=5, raw="@Preamble{a_x + b_x^2}", value="a_x + b_x^2") elif block_type == "implicit_comment": block = ImplicitComment(start_line=5, raw="# MyComment", comment="MyComment") elif block_type == "explicit_comment": - block = ExplicitComment( - start_line=5, raw="@Comment{MyComment}", comment="MyComment" - ) + block = ExplicitComment(start_line=5, raw="@Comment{MyComment}", comment="MyComment") else: raise ValueError("block type not yet supported in test utility") @@ -37,9 +36,7 @@ def assert_block_does_not_change( assert transformed_library.blocks[0] is not block -def assert_inplace_is_respected( - inplace: bool, input_block: Block, transformed_block: Block -): +def assert_inplace_is_respected(inplace: bool, input_block: Block, transformed_block: Block): """Make sure input instance is reused if and only if `inplace` is True.""" if inplace: # Note that this is not a strict requirement, diff --git a/tests/middleware_tests/test_block_middleware.py b/tests/middleware_tests/test_block_middleware.py index 9cfa3ed2..11e4f244 100644 --- a/tests/middleware_tests/test_block_middleware.py +++ b/tests/middleware_tests/test_block_middleware.py @@ -2,7 +2,11 @@ from bibtexparser import Library from bibtexparser.middlewares.middleware import BlockMiddleware -from bibtexparser.model import Entry, ExplicitComment, ImplicitComment, Preamble, String +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble +from bibtexparser.model import String BLOCKS = [ ExplicitComment("explicit_comment_a"), @@ -59,9 +63,7 @@ def test_successful_transform(middleware, expected): def test_returning_list_adds_all(): library = Library(blocks=BLOCKS) - library = LambdaBlockMiddleware( - lambda b: [ImplicitComment("% Block"), b] - ).transform(library) + library = LambdaBlockMiddleware(lambda b: [ImplicitComment("% Block"), b]).transform(library) expected = [ ImplicitComment("% Block"), @@ -98,9 +100,7 @@ def test_returning_list_adds_all(): [ ConstantBlockMiddleware(True), ConstantBlockMiddleware([True]), - LambdaBlockMiddleware( - lambda block: (b for b in [block]) - ), # generators are not collections + LambdaBlockMiddleware(lambda block: (b for b in [block])), # generators are not collections ], ) def test_returning_invalid_raises_error(middleware): diff --git a/tests/middleware_tests/test_custom_middleware_smoke.py b/tests/middleware_tests/test_custom_middleware_smoke.py index adf46a1f..c71234da 100644 --- a/tests/middleware_tests/test_custom_middleware_smoke.py +++ b/tests/middleware_tests/test_custom_middleware_smoke.py @@ -27,7 +27,5 @@ def transform_entry(self, entry, *args, **kwargs): def test_custom_middleware_smoke(): """Test that the very simple custom middleware above works.""" - library = bibtexparser.parse_string( - bibtex_str, append_middleware=[JournalAbbreviate()] - ) + library = bibtexparser.parse_string(bibtex_str, append_middleware=[JournalAbbreviate()]) assert library.entries[0]["journal"] == "NJ" diff --git a/tests/middleware_tests/test_enclosing.py b/tests/middleware_tests/test_enclosing.py index a40173ea..b2f96b81 100644 --- a/tests/middleware_tests/test_enclosing.py +++ b/tests/middleware_tests/test_enclosing.py @@ -4,17 +4,16 @@ import pytest from bibtexparser.library import Library -from bibtexparser.middlewares.enclosing import ( - AddEnclosingMiddleware, - RemoveEnclosingMiddleware, -) -from bibtexparser.model import Block, Entry, Field, String -from tests.middleware_tests.middleware_test_util import ( - assert_block_does_not_change, - assert_inplace_is_respected, - assert_nonfield_entry_attributes_unchanged, -) -from tests.resources import EDGE_CASE_VALUES, ENCLOSINGS +from bibtexparser.middlewares.enclosing import AddEnclosingMiddleware +from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware +from bibtexparser.model import Entry +from bibtexparser.model import Field +from bibtexparser.model import String +from tests.middleware_tests.middleware_test_util import assert_block_does_not_change +from tests.middleware_tests.middleware_test_util import assert_inplace_is_respected +from tests.middleware_tests.middleware_test_util import assert_nonfield_entry_attributes_unchanged +from tests.resources import EDGE_CASE_VALUES +from tests.resources import ENCLOSINGS def _skip_pseudo_enclosing_value(value: str): @@ -24,9 +23,7 @@ def _skip_pseudo_enclosing_value(value: str): pytest.skip("No enclosing to remove") -@pytest.mark.parametrize( - "enclosing", ENCLOSINGS + [pytest.param("{0}", id="no_enclosing")] -) +@pytest.mark.parametrize("enclosing", ENCLOSINGS + [pytest.param("{0}", id="no_enclosing")]) @pytest.mark.parametrize("value", EDGE_CASE_VALUES) @pytest.mark.parametrize("inplace", [True, False], ids=["inplace", "not_inplace"]) def test_removal_of_enclosing_on_string(enclosing, value, inplace): @@ -40,12 +37,10 @@ def test_removal_of_enclosing_on_string(enclosing, value, inplace): # Create test string key = "someKey" - raw = f"<--- does not matter for this unit test -->" + raw = "<--- does not matter for this unit test -->" start_line = 5 - original = String( - start_line=start_line, key=key, raw=raw, value=enclosing.format(value) - ) + original = String(start_line=start_line, key=key, raw=raw, value=enclosing.format(value)) middleware = RemoveEnclosingMiddleware(allow_inplace_modification=inplace) @@ -57,9 +52,7 @@ def test_removal_of_enclosing_on_string(enclosing, value, inplace): # Assert correct removal of enclosing transformed = transformed_library.strings[0] assert transformed.value == value - expected_enclosing = ( - enclosing.format("")[0] if enclosing != "{0}" else "no-enclosing" - ) + expected_enclosing = enclosing.format("")[0] if enclosing != "{0}" else "no-enclosing" assert transformed.parser_metadata["removed_enclosing"] == expected_enclosing # Assert remaining fields are unchanged assert transformed.start_line == start_line @@ -105,9 +98,7 @@ def test_removal_of_enclosing_on_entry(enclosing: str, inplace: bool): assert transformed_fields["month"].value == "1" # Assert remaining fields are unchanged - assert_nonfield_entry_attributes_unchanged( - input_entry, transformed_library.entries[0] - ) + assert_nonfield_entry_attributes_unchanged(input_entry, transformed_library.entries[0]) # Assert `allow_inplace_modification` is respected assert_inplace_is_respected(inplace, input_entry, transformed_library.entries[0]) @@ -125,12 +116,8 @@ def test_no_removal_blocktypes(block: str, inplace: bool): @pytest.mark.parametrize("metadata_enclosing", ["{", '"', "no-enclosing", None]) @pytest.mark.parametrize("default_enclosing", ["{", '"']) -@pytest.mark.parametrize( - "enclose_ints", [True, False], ids=["enclose_ints", "no_enclose_ints"] -) -@pytest.mark.parametrize( - "reuse_previous_enclosing", [True, False], ids=["reuse", "no_reuse"] -) +@pytest.mark.parametrize("enclose_ints", [True, False], ids=["enclose_ints", "no_enclose_ints"]) +@pytest.mark.parametrize("reuse_previous_enclosing", [True, False], ids=["reuse", "no_reuse"]) @pytest.mark.parametrize("value", EDGE_CASE_VALUES + ["1990"]) @pytest.mark.parametrize("inplace", [True, False], ids=["inplace", "not_inplace"]) def test_addition_of_enclosing_on_entry( @@ -212,12 +199,8 @@ def _figure_out_added_enclosing(changed_value, value): @pytest.mark.parametrize("metadata_enclosing", ["{", '"', None]) @pytest.mark.parametrize("default_enclosing", ["{", '"']) -@pytest.mark.parametrize( - "enclose_ints", [True, False], ids=["enclose_ints", "no_enclose_ints"] -) -@pytest.mark.parametrize( - "reuse_previous_enclosing", [True, False], ids=["reuse", "no_reuse"] -) +@pytest.mark.parametrize("enclose_ints", [True, False], ids=["enclose_ints", "no_enclose_ints"]) +@pytest.mark.parametrize("reuse_previous_enclosing", [True, False], ids=["reuse", "no_reuse"]) @pytest.mark.parametrize("inplace", [True, False], ids=["inplace", "not_inplace"]) def test_addition_of_enclosing_on_string( metadata_enclosing: str, @@ -276,9 +259,7 @@ def test_addition_of_enclosing_on_string( @pytest.mark.parametrize("block", ["preamble", "implicit_comment", "explicit_comment"]) @pytest.mark.parametrize("reuse_encoding", [True, False], ids=["reuse", "no_reuse"]) -@pytest.mark.parametrize( - "enclose_int", [True, False], ids=["enclose_int", "no_enclose_int"] -) +@pytest.mark.parametrize("enclose_int", [True, False], ids=["enclose_int", "no_enclose_int"]) @pytest.mark.parametrize("default_enc", ["{", '"']) @pytest.mark.parametrize("inplace", [True, False], ids=["inplace", "not_inplace"]) def test_no_addition_block_types( diff --git a/tests/middleware_tests/test_interpolate.py b/tests/middleware_tests/test_interpolate.py index 77a370cf..1262fa30 100644 --- a/tests/middleware_tests/test_interpolate.py +++ b/tests/middleware_tests/test_interpolate.py @@ -19,20 +19,16 @@ def test_string_interpolation_middleware_interpolates_string(): - original_library = Splitter(bibtex_string).split() + original_lib = Splitter(bibtex_string).split() # Prerequisite - assert ( - original_library.entries_dict["test_article"].fields_dict["note"].value - == "test_note" - ) + assert original_lib.entries_dict["test_article"].fields_dict["note"].value == "test_note" # Apply middleware - changed_library = ResolveStringReferencesMiddleware( - allow_inplace_modification=False - ).transform(original_library) + m = ResolveStringReferencesMiddleware(allow_inplace_modification=False) + changed_library = m.transform(original_lib) - assert original_library is not changed_library + assert original_lib is not changed_library assert ( changed_library.entries_dict["test_article"].fields_dict["note"].value == '"This is a test note."' @@ -40,15 +36,13 @@ def test_string_interpolation_middleware_interpolates_string(): def test_warning_is_raised_if_enclosings_are_removed(): - original_library = Splitter(bibtex_string).split() - no_enclosing_library = RemoveEnclosingMiddleware( - allow_inplace_modification=False - ).transform(original_library) + original_lib = Splitter(bibtex_string).split() + m = RemoveEnclosingMiddleware(allow_inplace_modification=False) + no_enclosing_library = m.transform(original_lib) with pytest.warns(UserWarning) as record: - ResolveStringReferencesMiddleware(allow_inplace_modification=False).transform( - no_enclosing_library - ) + m = ResolveStringReferencesMiddleware(allow_inplace_modification=False) + m.transform(no_enclosing_library) assert len(record) == 1 assert "RemoveEnclosing" in record[0].message.args[0] diff --git a/tests/middleware_tests/test_latex_encoding.py b/tests/middleware_tests/test_latex_encoding.py index cd649fc6..4a0c1a88 100644 --- a/tests/middleware_tests/test_latex_encoding.py +++ b/tests/middleware_tests/test_latex_encoding.py @@ -2,33 +2,26 @@ Note: All encoding/decoding is done using the pylatexenc library. Thus, we merely test that the middleware is correctly configured.""" + from copy import deepcopy -from textwrap import dedent import pytest from bibtexparser import Library -from bibtexparser.middlewares.latex_encoding import ( - LatexDecodingMiddleware, - LatexEncodingMiddleware, -) -from bibtexparser.model import Entry, Field -from tests.middleware_tests.middleware_test_util import ( - assert_inplace_is_respected, - assert_nonfield_entry_attributes_unchanged, -) +from bibtexparser.middlewares.latex_encoding import LatexDecodingMiddleware +from bibtexparser.middlewares.latex_encoding import LatexEncodingMiddleware +from bibtexparser.model import Entry +from bibtexparser.model import Field +from tests.middleware_tests.middleware_test_util import assert_inplace_is_respected +from tests.middleware_tests.middleware_test_util import assert_nonfield_entry_attributes_unchanged @pytest.mark.parametrize( "latex_string,expected_decoded_string", [ pytest.param(r"some \textbf{bold} text", "some bold text", id=r"\textbf"), - pytest.param( - r"Kristoffer H\o{}gsbro Rose", "Kristoffer Høgsbro Rose", id=r"\o{}" - ), - pytest.param( - r"Einstein $ e=m_c^2 $", "Einstein $ e=m_c^2 $", id=r"Keep math mode" - ), + pytest.param(r"Kristoffer H\o{}gsbro Rose", "Kristoffer Høgsbro Rose", id=r"\o{}"), + pytest.param(r"Einstein $ e=m_c^2 $", "Einstein $ e=m_c^2 $", id=r"Keep math mode"), pytest.param(r"I payed \$10", "I payed $10", id=r"Keep \$"), pytest.param( r"{Walther Andreas} Muller", @@ -74,12 +67,8 @@ def test_latex_special_chars_decoding(latex_string, expected_decoded_string): @pytest.mark.parametrize( "human_string ,expected_latex_string", [ - pytest.param( - "Kristoffer Høgsbro Rose", r"Kristoffer H{\o}gsbro Rose", id=r"\o{}" - ), - pytest.param( - r"Einstein $ e=m_c^2 $", r"Einstein $ e=m_c^2 $", id=r"Keep math mode" - ), + pytest.param("Kristoffer Høgsbro Rose", r"Kristoffer H{\o}gsbro Rose", id=r"\o{}"), + pytest.param(r"Einstein $ e=m_c^2 $", r"Einstein $ e=m_c^2 $", id=r"Keep math mode"), pytest.param(r"I payed $10", r"I payed \$10", id=r"Escape $"), pytest.param( r"See https://mweiss.ch", @@ -91,9 +80,7 @@ def test_latex_special_chars_decoding(latex_string, expected_decoded_string): r"See \url{http://mweiss.ch}", id=r"\url{...} for http", ), - pytest.param( - r"See www.mweiss.ch", r"See \url{www.mweiss.ch}", id=r"\url{...} for www." - ), + pytest.param(r"See www.mweiss.ch", r"See \url{www.mweiss.ch}", id=r"\url{...} for www."), pytest.param( r"See https://www.mweiss.ch", r"See \url{https://www.mweiss.ch}", @@ -125,14 +112,11 @@ def test_latex_special_chars_encoding(human_string, expected_latex_string): @pytest.mark.parametrize("inplace", [True, False]) -@pytest.mark.parametrize( - "middleware_class", [LatexEncodingMiddleware, LatexDecodingMiddleware] -) +@pytest.mark.parametrize("middleware_class", [LatexEncodingMiddleware, LatexDecodingMiddleware]) def test_inplace(inplace: bool, middleware_class): """Make sure that inplace conversion is done iff inplace is True""" input_entry = _entry_with_latex_string("Some string") library = Library([input_entry]) - original_copy = deepcopy(input_entry) middleware = middleware_class(allow_inplace_modification=inplace) transformed_library = middleware.transform(library) diff --git a/tests/middleware_tests/test_month.py b/tests/middleware_tests/test_month.py index 65031a9f..c67c6541 100644 --- a/tests/middleware_tests/test_month.py +++ b/tests/middleware_tests/test_month.py @@ -1,9 +1,7 @@ from bibtexparser.middlewares.enclosing import RemoveEnclosingMiddleware -from bibtexparser.middlewares.month import ( - MonthAbbreviationMiddleware, - MonthIntMiddleware, - MonthLongStringMiddleware, -) +from bibtexparser.middlewares.month import MonthAbbreviationMiddleware +from bibtexparser.middlewares.month import MonthIntMiddleware +from bibtexparser.middlewares.month import MonthLongStringMiddleware from bibtexparser.splitter import Splitter test_bibtex_string = """ @@ -58,12 +56,10 @@ def test_long_string_months(): ), "enclosed values should not be not changed" # Test the same after enclosing is removed - no_enclosing_library = RemoveEnclosingMiddleware( - allow_inplace_modification=False - ).transform(original_library) - new_library = MonthLongStringMiddleware(allow_inplace_modification=False).transform( - no_enclosing_library - ) + m = RemoveEnclosingMiddleware(allow_inplace_modification=False) + no_enclosing_library = m.transform(original_library) + m = MonthLongStringMiddleware(allow_inplace_modification=False) + new_library = m.transform(no_enclosing_library) assert new_library.entries_dict["smith2022"]["month"] == "January" assert new_library.entries_dict["doe2021"]["month"] == "April" @@ -74,9 +70,8 @@ def test_long_string_months(): def test_short_string_months(): original_library = Splitter(test_bibtex_string).split() - new_library = MonthAbbreviationMiddleware( - allow_inplace_modification=False - ).transform(original_library) + m = MonthAbbreviationMiddleware(allow_inplace_modification=False) + new_library = m.transform(original_library) assert ( new_library.entries_dict["smith2022"]["month"] == '"jan"' @@ -88,12 +83,10 @@ def test_short_string_months(): ), "enclosed values should not be not changed" # Test the same after enclosing is removed - no_enclosing_library = RemoveEnclosingMiddleware( - allow_inplace_modification=False - ).transform(original_library) - new_library = MonthAbbreviationMiddleware( - allow_inplace_modification=False - ).transform(no_enclosing_library) + m = RemoveEnclosingMiddleware(allow_inplace_modification=False) + no_enclosing_library = m.transform(original_library) + m = MonthAbbreviationMiddleware(allow_inplace_modification=False) + new_library = m.transform(no_enclosing_library) assert new_library.entries_dict["smith2022"]["month"] == "jan" assert new_library.entries_dict["doe2021"]["month"] == "apr" @@ -104,9 +97,8 @@ def test_short_string_months(): def test_int_months(): original_library = Splitter(test_bibtex_string).split() - new_library = MonthIntMiddleware(allow_inplace_modification=False).transform( - original_library - ) + m = MonthIntMiddleware(allow_inplace_modification=False) + new_library = m.transform(original_library) assert ( new_library.entries_dict["smith2022"]["month"] == '"jan"' @@ -118,12 +110,10 @@ def test_int_months(): ), "enclosed values should not be not changed" # Test the same after enclosing is removed - no_enclosing_library = RemoveEnclosingMiddleware( - allow_inplace_modification=False - ).transform(original_library) - new_library = MonthIntMiddleware(allow_inplace_modification=False).transform( - no_enclosing_library - ) + m = RemoveEnclosingMiddleware(allow_inplace_modification=False) + no_enclosing_library = m.transform(original_library) + m = MonthIntMiddleware(allow_inplace_modification=False) + new_library = m.transform(no_enclosing_library) assert new_library.entries_dict["smith2022"]["month"] == 1 assert new_library.entries_dict["doe2021"]["month"] == 4 diff --git a/tests/middleware_tests/test_names.py b/tests/middleware_tests/test_names.py index d2f3ff13..5da4d81d 100644 --- a/tests/middleware_tests/test_names.py +++ b/tests/middleware_tests/test_names.py @@ -1,24 +1,22 @@ from copy import deepcopy -from typing import Dict, List +from typing import Dict +from typing import List import pytest as pytest from bibtexparser.library import Library -from bibtexparser.middlewares.names import ( - InvalidNameError, - MergeCoAuthors, - MergeNameParts, - NameParts, - SeparateCoAuthors, - SplitNameParts, - parse_single_name_into_parts, - split_multiple_persons_names, -) -from bibtexparser.model import Entry, Field -from tests.middleware_tests.middleware_test_util import ( - assert_inplace_is_respected, - assert_nonfield_entry_attributes_unchanged, -) +from bibtexparser.middlewares.names import InvalidNameError +from bibtexparser.middlewares.names import MergeCoAuthors +from bibtexparser.middlewares.names import MergeNameParts +from bibtexparser.middlewares.names import NameParts +from bibtexparser.middlewares.names import SeparateCoAuthors +from bibtexparser.middlewares.names import SplitNameParts +from bibtexparser.middlewares.names import parse_single_name_into_parts +from bibtexparser.middlewares.names import split_multiple_persons_names +from bibtexparser.model import Entry +from bibtexparser.model import Field +from tests.middleware_tests.middleware_test_util import assert_inplace_is_respected +from tests.middleware_tests.middleware_test_util import assert_nonfield_entry_attributes_unchanged @pytest.mark.parametrize( @@ -213,9 +211,7 @@ def test_name_splitting_no_strict_mode(name: str, expected: Dict[str, List[str]] def test_name_splitting_commas_at_higher_brace_level(strict: bool): """Test that commas are only considered at higher brace levels""" result = parse_single_name_into_parts("CC, dd, {AA, BB}", strict=strict) - expected = _dict_to_nameparts( - {"first": ["{AA, BB}"], "von": [], "last": ["CC"], "jr": ["dd"]} - ) + expected = _dict_to_nameparts({"first": ["{AA, BB}"], "von": [], "last": ["CC"], "jr": ["dd"]}) assert result == expected @@ -865,9 +861,7 @@ def test_name_splitting_commas_at_higher_brace_level(strict: bool): ) -@pytest.mark.parametrize( - "name, expected_as_dict", REGULAR_NAME_PARTS_PARSING_TEST_CASES -) +@pytest.mark.parametrize("name, expected_as_dict", REGULAR_NAME_PARTS_PARSING_TEST_CASES) @pytest.mark.parametrize("strict", [True, False], ids=["strict", "non-strict"]) def test_split_name_into_parts(name, expected_as_dict, strict): # As all inputs are valid, strict/no-strict should have no influence @@ -876,9 +870,7 @@ def test_split_name_into_parts(name, expected_as_dict, strict): assert result == expected -@pytest.mark.parametrize( - "name, expected_as_dict", REGULAR_NAME_PARTS_PARSING_TEST_CASES -) +@pytest.mark.parametrize("name, expected_as_dict", REGULAR_NAME_PARTS_PARSING_TEST_CASES) @pytest.mark.parametrize("strict", [True, False], ids=["strict", "non-strict"]) def test_merge_last_name_first_inverse(name, expected_as_dict, strict): """Tests that merging name parts using the last-name-first method diff --git a/tests/middleware_tests/test_sorting_blocks.py b/tests/middleware_tests/test_sorting_blocks.py index e7a5d3eb..da0deca6 100644 --- a/tests/middleware_tests/test_sorting_blocks.py +++ b/tests/middleware_tests/test_sorting_blocks.py @@ -1,6 +1,10 @@ from bibtexparser import Library from bibtexparser.middlewares.sorting_blocks import SortBlocksByTypeAndKeyMiddleware -from bibtexparser.model import Entry, ExplicitComment, ImplicitComment, Preamble, String +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble +from bibtexparser.model import String BLOCKS = [ ExplicitComment("explicit_comment_a"), @@ -47,9 +51,7 @@ def test_sorting_blocks_preserving_comments_default_type_order(): def test_sorting_blocks_preserving_comments_custom_type_order(): type_order = (Preamble, String, Entry) library = Library(blocks=BLOCKS) - library = SortBlocksByTypeAndKeyMiddleware(block_type_order=type_order).transform( - library - ) + library = SortBlocksByTypeAndKeyMiddleware(block_type_order=type_order).transform(library) ordered_blocks = library.blocks assert ordered_blocks[0] == Preamble("preamble_a") diff --git a/tests/middleware_tests/test_sorting_entry_fields.py b/tests/middleware_tests/test_sorting_entry_fields.py index 262d0c99..b60ede9d 100644 --- a/tests/middleware_tests/test_sorting_entry_fields.py +++ b/tests/middleware_tests/test_sorting_entry_fields.py @@ -1,11 +1,10 @@ import pytest from bibtexparser import Library -from bibtexparser.middlewares.sorting_entry_fields import ( - SortFieldsAlphabeticallyMiddleware, - SortFieldsCustomMiddleware, -) -from bibtexparser.model import Entry, Field +from bibtexparser.middlewares.sorting_entry_fields import SortFieldsAlphabeticallyMiddleware +from bibtexparser.middlewares.sorting_entry_fields import SortFieldsCustomMiddleware +from bibtexparser.model import Entry +from bibtexparser.model import Field TEST_ENTRY = Entry( "article", @@ -23,9 +22,8 @@ @pytest.mark.parametrize("inplace", [True, False]) def test_sort_alphabetically(inplace: bool): library = Library(blocks=[TEST_ENTRY]) - transformed = SortFieldsAlphabeticallyMiddleware( - allow_inplace_modification=inplace - ).transform(library) + m = SortFieldsAlphabeticallyMiddleware(allow_inplace_modification=inplace) + transformed = m.transform(library) entry = transformed.entries[0] assert entry.fields[0].key == "author" assert entry.fields[1].key == "journal" @@ -47,9 +45,8 @@ def test_sort_custom_order_case_insensitive(inplace: bool): library = Library(blocks=[TEST_ENTRY]) # case insensitivity is default - transformed = SortFieldsCustomMiddleware( - order=custom_order, allow_inplace_modification=inplace - ).transform(library) + m = SortFieldsCustomMiddleware(order=custom_order, allow_inplace_modification=inplace) + transformed = m.transform(library) entry = transformed.entries[0] assert entry.fields[0].key == "author" assert entry.fields[1].key == "year" @@ -69,18 +66,19 @@ def test_sort_order_custom_case_sensitive(): custom_order = ("author", "yEar", "title", "Journal") # assess case sensitive library = Library(blocks=[TEST_ENTRY]) - transformed = SortFieldsCustomMiddleware( - order=custom_order, case_sensitive=True - ).transform(library) + m = SortFieldsCustomMiddleware(order=custom_order, case_sensitive=True) + transformed = m.transform(library) entry = transformed.entries[0] assert entry.fields[0].key == "author" assert entry.fields[1].key == "title" - assert ( - entry.fields[2].key == "year" - ) # Unspecified fields (due to case sensitivity) are appended - assert ( - entry.fields[3].key == "journal" - ) # Unspecified fields (due to case sensitivity) are appended - assert entry.fields[4].key == "note" # Unspecified fields are appended + + # Unspecified fields (due to case sensitivity) are appended + assert entry.fields[2].key == "year" + + # Unspecified fields (due to case sensitivity) are appended + assert entry.fields[3].key == "journal" + + # Unspecified fields are appended + assert entry.fields[4].key == "note" assert len(transformed.blocks) == 1 diff --git a/tests/resources.py b/tests/resources.py index 138114b1..e0646950 100644 --- a/tests/resources.py +++ b/tests/resources.py @@ -24,23 +24,23 @@ VALID_BIBTEX_SNIPPETS: List[str] = [ # A small, regular article dedent( - f"""\ - @article{{test, + """\ + @article{test, author = "John Doe", title = "Some title", - }}""" + }""" ), # A string definition - dedent(f"""@string{{someString = "some value"}}"""), + dedent("""@string{someString = "some value"}"""), # A string definition with a comment dedent( - f"""\ - @string{{someString = "some value"}} - + """\ + @string{someString = "some value"} + % This is a comment""" ), # A preamble - dedent(f"""@preamble{{some preamble}}"""), + dedent("""@preamble{some preamble}"""), # A an empty line "\n", # A comment diff --git a/tests/splitter_tests/test_splitter_basic.py b/tests/splitter_tests/test_splitter_basic.py index a969ef8d..0a7d1716 100644 --- a/tests/splitter_tests/test_splitter_basic.py +++ b/tests/splitter_tests/test_splitter_basic.py @@ -2,13 +2,16 @@ These tests are not exhaustive, but they should cover the most common cases. More exhaustive and atomic tests are provided in separate modules.""" -from typing import Any, Dict + +from typing import Any +from typing import Dict import pytest as pytest from bibtexparser.library import Library from bibtexparser.splitter import Splitter +# flake8: noqa example_bibstr = """ @string{goossens = "Goossens, Michel"} @@ -259,10 +262,7 @@ def test_failed_block(): expected_raw = "\n".join(example_bibstr.splitlines()[27:40]) assert failed_block.raw.strip() == expected_raw.strip() assert failed_block.start_line == 27 - assert ( - 'Was still looking for field-value closing `"`' - in failed_block.error.abort_reason - ) + assert 'Was still looking for field-value closing `"`' in failed_block.error.abort_reason duplicate_bibtex_entry_keys = """ @@ -316,9 +316,7 @@ def test_handles_duplicate_strings(): assert isinstance(lib.failed_blocks[0], bibtexparser.model.DuplicateBlockKeyBlock) assert lib.failed_blocks[0].previous_block.value == "Duplicate string 1" assert lib.failed_blocks[0].ignore_error_block.value == '"Duplicate string 2"' - assert isinstance( - lib.failed_blocks[0].ignore_error_block, bibtexparser.model.String - ) + assert isinstance(lib.failed_blocks[0].ignore_error_block, bibtexparser.model.String) blocks_not_starting_on_new_lines = """\ diff --git a/tests/splitter_tests/test_splitter_entry.py b/tests/splitter_tests/test_splitter_entry.py index ca67439c..d9718e8e 100644 --- a/tests/splitter_tests/test_splitter_entry.py +++ b/tests/splitter_tests/test_splitter_entry.py @@ -1,12 +1,15 @@ """Tests the parsing of entries, e.g. `@article{...}` blocks.""" + from textwrap import dedent import pytest as pytest from bibtexparser.library import Library -from bibtexparser.model import DuplicateFieldKeyBlock, Field +from bibtexparser.model import DuplicateFieldKeyBlock +from bibtexparser.model import Field from bibtexparser.splitter import Splitter -from tests.resources import EDGE_CASE_VALUES, ENCLOSINGS +from tests.resources import EDGE_CASE_VALUES +from tests.resources import ENCLOSINGS @pytest.mark.parametrize( @@ -115,7 +118,7 @@ def test_trailing_comma(enclosing: str): firstfield = {{some value}}, fieldBeforeTrailingComma = {value_before_trailing_comma}, }} - + @string{{someString = "some value"}}""" ) library: Library = Splitter(bibtex_str).split() @@ -261,12 +264,8 @@ def test_entry_with_concatenated_field(entry, expected): "entry", [ # common in revtex, see issue #384 - pytest.param( - "@Article {articleTestKey, title = {Some title}}", id="single whitespace" - ), - pytest.param( - "@Article {articleTestKey, title = {Some title}}", id="double whitespace" - ), + pytest.param("@Article {articleTestKey, title = {Some title}}", id="single whitespace"), + pytest.param("@Article {articleTestKey, title = {Some title}}", id="double whitespace"), pytest.param("@Article\t{articleTestKey, title = {Some title}}", id="tab"), pytest.param( "@Article \t {articleTestKey, title = {Some title}}", @@ -276,7 +275,9 @@ def test_entry_with_concatenated_field(entry, expected): ) def test_entry_with_space_before_bracket(entry: str): """For motivation why we need this, please see issue #391""" - some_previous_entry = "@article{normal_entry, title = {The first title}, author = {The first author} }" + some_previous_entry = ( + "@article{normal_entry, title = {The first title}, author = {The first author} }" + ) full_bibtex = f"{some_previous_entry}\n\n{entry}\n\n" library: Library = Splitter(full_bibtex).split() diff --git a/tests/splitter_tests/test_splitter_explicit_comment.py b/tests/splitter_tests/test_splitter_explicit_comment.py index 0e402833..9e7d6bf1 100644 --- a/tests/splitter_tests/test_splitter_explicit_comment.py +++ b/tests/splitter_tests/test_splitter_explicit_comment.py @@ -2,15 +2,14 @@ from bibtexparser.model import ExplicitComment from bibtexparser.splitter import Splitter -from tests.resources import EDGE_CASE_VALUES, VALID_BIBTEX_SNIPPETS +from tests.resources import EDGE_CASE_VALUES +from tests.resources import VALID_BIBTEX_SNIPPETS @pytest.mark.parametrize("bibtex_before", VALID_BIBTEX_SNIPPETS) @pytest.mark.parametrize("bibtex_after", VALID_BIBTEX_SNIPPETS) @pytest.mark.parametrize("comment_content", EDGE_CASE_VALUES) -def test_explicit_comment_parsing( - bibtex_before: str, bibtex_after: str, comment_content: str -): +def test_explicit_comment_parsing(bibtex_before: str, bibtex_after: str, comment_content: str): num_before_comments = bibtex_before.lower().count("@comment{") num_after_comments = bibtex_after.lower().count("@comment{") diff --git a/tests/splitter_tests/test_splitter_implicit_comments.py b/tests/splitter_tests/test_splitter_implicit_comments.py index 77230b76..826e7b91 100644 --- a/tests/splitter_tests/test_splitter_implicit_comments.py +++ b/tests/splitter_tests/test_splitter_implicit_comments.py @@ -1,4 +1,5 @@ """Tests the parsing of implicit comments, i.e., anything outside @{...} blocks.""" + from textwrap import dedent import pytest @@ -13,22 +14,16 @@ def test_implicit_comment_eof(): bibtex_str = dedent( """\ @article{article1, title={title1}} - + % This is an implicit comment at the end of the file.""" ) library = Splitter(bibtex_str).split() assert len(library.comments) == 1 - assert ( - library.comments[0].comment - == "% This is an implicit comment at the end of the file." - ) + assert library.comments[0].comment == "% This is an implicit comment at the end of the file." # Before applying the middleware, `comment` and `raw` are the same. - assert ( - library.comments[0].raw - == "% This is an implicit comment at the end of the file." - ) + assert library.comments[0].raw == "% This is an implicit comment at the end of the file." assert library.comments[0].start_line == 2 @@ -43,15 +38,9 @@ def test_implicit_comment_start_of_file(): library = Splitter(bibtex_str).split() assert len(library.comments) == 1 - assert ( - library.comments[0].comment - == "This is an implicit comment at the start of the file." - ) + assert library.comments[0].comment == "This is an implicit comment at the start of the file." # Before applying the middleware, `comment` and `raw` are the same. - assert ( - library.comments[0].raw - == "This is an implicit comment at the start of the file." - ) + assert library.comments[0].raw == "This is an implicit comment at the start of the file." assert library.comments[0].start_line == 0 @@ -82,12 +71,12 @@ def test_multiline_implicit_comment(): bibtex_str = dedent( """\ @article{article1, title={title1}} - + % This is an implicit comment % spanning multiple lines - + with an empty line in between. - + @article{article2, title={title2}}""" ) @@ -99,7 +88,7 @@ def test_multiline_implicit_comment(): """\ % This is an implicit comment % spanning multiple lines - + with an empty line in between.""" ) assert library.comments[0].comment == expected_str diff --git a/tests/splitter_tests/test_splitter_parse_failures.py b/tests/splitter_tests/test_splitter_parse_failures.py index b97768e9..79fb9f7d 100644 --- a/tests/splitter_tests/test_splitter_parse_failures.py +++ b/tests/splitter_tests/test_splitter_parse_failures.py @@ -8,16 +8,12 @@ @pytest.mark.parametrize( "faulty_block", [ - pytest.param( - """@article{article1, title={title1}""", id="entry_without_closing_brace" - ), + pytest.param("""@article{article1, title={title1}""", id="entry_without_closing_brace"), pytest.param( """@article{article1, \n title={title1}""", id="multiline_entry_without_closing_brace", ), - pytest.param( - """@article{article1,""", id="entry_without_field_and_closing_brace" - ), + pytest.param("""@article{article1,""", id="entry_without_field_and_closing_brace"), pytest.param("""@article{article1""", id="entry_only_with_key_no_comma"), pytest.param( """@article{article1, title={title1} author={author1}""", @@ -41,9 +37,7 @@ [False, True], ids=["no_block_afterwards", "block_afterwards"], ) -def test_faulty_block_parsing( - bibtex_before: str, faulty_block: str, use_block_afterwards: bool -): +def test_faulty_block_parsing(bibtex_before: str, faulty_block: str, use_block_afterwards: bool): """Test that an unexpected block end raises a ParseError.""" bibtex_str = f"{bibtex_before}\n{faulty_block}" if use_block_afterwards: @@ -58,9 +52,6 @@ def test_faulty_block_parsing( # Check that the following block (if present) is again parsed correctly. if use_block_afterwards: assert len(library.comments) >= 1 - assert ( - library.comments[-1].raw - == "@comment{This is a comment after a faulty block}" - ) + assert library.comments[-1].raw == "@comment{This is a comment after a faulty block}" assert library.comments[-1].comment == "This is a comment after a faulty block" assert library.comments[-1].start_line == len(bibtex_str.splitlines()) - 1 diff --git a/tests/splitter_tests/test_splitter_preamble.py b/tests/splitter_tests/test_splitter_preamble.py index 1cc7fdf5..a41360fd 100644 --- a/tests/splitter_tests/test_splitter_preamble.py +++ b/tests/splitter_tests/test_splitter_preamble.py @@ -2,7 +2,8 @@ from bibtexparser.model import Preamble from bibtexparser.splitter import Splitter -from tests.resources import PREAMBLES, VALID_BIBTEX_SNIPPETS +from tests.resources import PREAMBLES +from tests.resources import VALID_BIBTEX_SNIPPETS @pytest.mark.parametrize("bibtex_before", VALID_BIBTEX_SNIPPETS) diff --git a/tests/splitter_tests/test_splitter_string.py b/tests/splitter_tests/test_splitter_string.py index 19fce310..f43f8cbb 100644 --- a/tests/splitter_tests/test_splitter_string.py +++ b/tests/splitter_tests/test_splitter_string.py @@ -2,7 +2,8 @@ from bibtexparser.library import Library from bibtexparser.splitter import Splitter -from tests.resources import EDGE_CASE_VALUES, ENCLOSINGS +from tests.resources import EDGE_CASE_VALUES +from tests.resources import ENCLOSINGS @pytest.mark.parametrize( diff --git a/tests/test_entrypoint.py b/tests/test_entrypoint.py index 9f270159..95fac0a2 100644 --- a/tests/test_entrypoint.py +++ b/tests/test_entrypoint.py @@ -1,8 +1,6 @@ """Testing the parse_file function.""" -import pytest - -from bibtexparser import parse_file, writer +from bibtexparser import parse_file def test_gbk(): diff --git a/tests/test_library.py b/tests/test_library.py index e02597ed..8142a06e 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -1,9 +1,8 @@ -from copy import deepcopy - import pytest from bibtexparser import Library -from bibtexparser.model import Entry, Field +from bibtexparser.model import Entry +from bibtexparser.model import Field def get_dummy_entry(): @@ -29,9 +28,7 @@ def test_replace_with_duplicates(): replacement_entry = get_dummy_entry() replacement_entry.fields_dict["title"].value = "A new title" - library.replace( - library.failed_blocks[0], replacement_entry, fail_on_duplicate_key=False - ) + library.replace(library.failed_blocks[0], replacement_entry, fail_on_duplicate_key=False) assert len(library.blocks) == 2 assert len(library.failed_blocks) == 1 assert library.failed_blocks[0].ignore_error_block["title"] == "A new title" @@ -39,9 +36,7 @@ def test_replace_with_duplicates(): replacement_entry_2 = get_dummy_entry() replacement_entry_2.fields_dict["title"].value = "Another new title" - library.replace( - library.entries[0], replacement_entry_2, fail_on_duplicate_key=False - ) + library.replace(library.entries[0], replacement_entry_2, fail_on_duplicate_key=False) assert len(library.blocks) == 2 assert len(library.failed_blocks) == 1 # The new block replaces the previous "non-duplicate" and should thus not become a duplicate itself @@ -56,9 +51,7 @@ def test_replace_fail_on_duplicate(): library.add([replaceable_entry, future_duplicate_entry]) with pytest.raises(ValueError): - library.replace( - replaceable_entry, get_dummy_entry(), fail_on_duplicate_key=True - ) + library.replace(replaceable_entry, get_dummy_entry(), fail_on_duplicate_key=True) assert len(library.blocks) == 2 assert len(library.failed_blocks) == 0 diff --git a/tests/test_model.py b/tests/test_model.py index 198e8a06..e584b33f 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,16 +1,15 @@ -from copy import copy, deepcopy +from copy import copy +from copy import deepcopy from textwrap import dedent import pytest -from bibtexparser.model import ( - Entry, - ExplicitComment, - Field, - ImplicitComment, - Preamble, - String, -) +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import Field +from bibtexparser.model import ImplicitComment +from bibtexparser.model import Preamble +from bibtexparser.model import String def test_entry_equality(): @@ -78,21 +77,15 @@ def test_entry_deepcopy(): def test_entry_get(): - entry1 = Entry( - "article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw" - ) - entry2 = Entry( - "article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw" - ) + entry1 = Entry("article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw") + entry2 = Entry("article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw") assert entry1.get("other", "default") == "default" assert entry1.get("foo") == Field("foo", "bar", 2) assert entry1 == entry2 def test_entry_pop(): - entry1 = Entry( - "article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw" - ) + entry1 = Entry("article", "key", [Field("field", "value", 1), Field("foo", "bar", 2)], 1, "raw") entry2 = Entry("article", "key", [Field("field", "value", 1)], 1, "raw") assert entry1.pop("other", "default") == "default" assert entry1.pop("foo") == Field("foo", "bar", 2) diff --git a/tests/test_writer.py b/tests/test_writer.py index 5baa4ced..4ee4391c 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -2,16 +2,16 @@ import pytest -from bibtexparser import BibtexFormat, Library, writer -from bibtexparser.model import ( - Entry, - ExplicitComment, - Field, - ImplicitComment, - ParsingFailedBlock, - Preamble, - String, -) +from bibtexparser import BibtexFormat +from bibtexparser import Library +from bibtexparser import writer +from bibtexparser.model import Entry +from bibtexparser.model import ExplicitComment +from bibtexparser.model import Field +from bibtexparser.model import ImplicitComment +from bibtexparser.model import ParsingFailedBlock +from bibtexparser.model import Preamble +from bibtexparser.model import String _DUMMY_STRING = String(key="myKey", value='"myValue"') _DUMMY_PREAMBLE = Preamble(value='"myValue"') @@ -132,9 +132,7 @@ def test_block_separator(block_separator): def test_write_failed_block(): raw = "@article{irrelevant-for-this-test,\nexcept = {that-there-need-to-be},\nother = {multiple-lines}\n}" - block = ParsingFailedBlock( - error=ValueError("Some error"), raw=raw, ignore_error_block=None - ) + block = ParsingFailedBlock(error=ValueError("Some error"), raw=raw, ignore_error_block=None) library = Library(blocks=[block]) string = writer.write(library) lines = string.splitlines()