Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -25,35 +25,31 @@ repos:
- id: rst-directive-colons
- id: rst-inline-touching-normal
- repo: https://github.com/psf/black
rev: 24.2.0
rev: 26.5.1
hooks:
- id: black
args: [--safe, --quiet, --line-length=100]
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.0
rev: v2.3.3
hooks:
- id: autoflake
args: [--in-place, --remove-unused-variable]
- repo: https://github.com/pycqa/isort
rev: 5.13.2
rev: 8.0.1
hooks:
- id: isort
name: isort
args: ["--force-single-line", "--line-length=100", "--profile=black"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.1
rev: v3.21.2
hooks:
- id: pyupgrade
args: [--py36-plus]
args: [--py310-plus]
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
rev: 7.3.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.2
hooks:
Expand Down
45 changes: 21 additions & 24 deletions bibtexparser/entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import codecs
import warnings
from typing import Iterable
from typing import List
from collections.abc import Iterable
from typing import Optional
from typing import TextIO
from typing import Tuple
from typing import Union

from .library import Library
from .middlewares.middleware import Middleware
Expand All @@ -17,9 +14,9 @@


def _build_parse_stack(
parse_stack: Optional[Iterable[Middleware]],
append_middleware: Optional[Iterable[Middleware]],
) -> List[Middleware]:
parse_stack: Iterable[Middleware] | None,
append_middleware: Iterable[Middleware] | None,
) -> list[Middleware]:
if parse_stack is not None and append_middleware is not None:
raise ValueError(
"Provided both parse_stack and append_middleware. "
Expand Down Expand Up @@ -47,9 +44,9 @@ def _build_parse_stack(


def _build_unparse_stack(
unparse_stack: Optional[Iterable[Middleware]],
prepend_middleware: Optional[Iterable[Middleware]],
) -> List[Middleware]:
unparse_stack: Iterable[Middleware] | None,
prepend_middleware: Iterable[Middleware] | None,
) -> list[Middleware]:
if unparse_stack is not None and prepend_middleware is not None:
raise ValueError(
"Provided both unparse_stack and prepend_middleware. "
Expand Down Expand Up @@ -77,11 +74,11 @@ def _build_unparse_stack(


def _handle_deprecated_write_params(
unparse_stack: Optional[Iterable[Middleware]],
prepend_middleware: Optional[Iterable[Middleware]],
unparse_stack: Iterable[Middleware] | None,
prepend_middleware: Iterable[Middleware] | None,
kwargs: dict,
function_name: str,
) -> Tuple[Optional[Iterable[Middleware]], Optional[Iterable[Middleware]]]:
) -> tuple[Iterable[Middleware] | None, Iterable[Middleware] | None]:
"""Handle deprecated parameter names for write functions.

:param unparse_stack: Current unparse_stack value
Expand Down Expand Up @@ -124,9 +121,9 @@ def _handle_deprecated_write_params(

def parse_string(
bibtex_str: str,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
library: Optional[Library] = None,
parse_stack: Iterable[Middleware] | None = None,
append_middleware: Iterable[Middleware] | None = None,
library: Library | None = None,
) -> Library:
"""Parse a BibTeX string.

Expand Down Expand Up @@ -156,8 +153,8 @@ def parse_string(

def parse_file(
path: str,
parse_stack: Optional[Iterable[Middleware]] = None,
append_middleware: Optional[Iterable[Middleware]] = None,
parse_stack: Iterable[Middleware] | None = None,
append_middleware: Iterable[Middleware] | None = None,
encoding: str = "UTF-8",
) -> Library:
"""Parse a BibTeX file
Expand Down Expand Up @@ -188,11 +185,11 @@ def parse_file(


def write_file(
file: Union[str, TextIO],
file: str | TextIO,
library: Library,
unparse_stack: Optional[Iterable[Middleware]] = None,
prepend_middleware: Optional[Iterable[Middleware]] = None,
bibtex_format: Optional[BibtexFormat] = None,
unparse_stack: Iterable[Middleware] | None = None,
prepend_middleware: Iterable[Middleware] | None = None,
bibtex_format: BibtexFormat | None = None,
encoding: str = "UTF-8",
**kwargs,
) -> None:
Expand Down Expand Up @@ -230,8 +227,8 @@ def write_file(

def write_string(
library: Library,
unparse_stack: Optional[Iterable[Middleware]] = None,
prepend_middleware: Optional[Iterable[Middleware]] = None,
unparse_stack: Iterable[Middleware] | None = None,
prepend_middleware: Iterable[Middleware] | None = None,
bibtex_format: Optional["BibtexFormat"] = None,
**kwargs,
) -> str:
Expand Down
8 changes: 2 additions & 6 deletions bibtexparser/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
from typing import List
from typing import Optional


class ParsingException(Exception):
"""Generic Exception for parsing errors"""

Expand All @@ -25,7 +21,7 @@ def __init__(
self,
abort_reason: str,
# Not provided if end of file is reached
end_index: Optional[int] = None,
end_index: int | None = None,
):
self.abort_reason = abort_reason
self.end_index = end_index
Expand Down Expand Up @@ -60,6 +56,6 @@ def __init__(self, first_match, expected_match, second_match):
class PartialMiddlewareException(ParsingException):
"""Exception raised when a middleware could not be fully applied."""

def __init__(self, reasons: List[str]):
def __init__(self, reasons: list[str]):
reasons_string = "\n\n=====\n\n".join(reasons)
super().__init__(f"Middleware could not be fully applied: {reasons_string}")
32 changes: 13 additions & 19 deletions bibtexparser/library.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
from typing import Dict
from typing import List
from typing import Union

from .model import Block
from .model import DuplicateBlockKeyBlock
from .model import Entry
Expand All @@ -19,7 +15,7 @@ class Library:

def __init__(
self,
blocks: Union[List[Block], None] = None,
blocks: list[Block] | None = None,
fail_on_duplicate_key: bool = True,
):
self._blocks = []
Expand All @@ -28,7 +24,7 @@ def __init__(
if blocks is not None:
self.add(blocks, fail_on_duplicate_key=fail_on_duplicate_key)

def add(self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = True):
def add(self, blocks: list[Block] | Block, fail_on_duplicate_key: bool = True):
"""Add blocks to library.

The adding is key-safe, i.e., it is made sure that no duplicate keys are added
Expand Down Expand Up @@ -64,7 +60,7 @@ def add(self, blocks: Union[List[Block], Block], fail_on_duplicate_key: bool = T
block = self._add_to_dicts(block)
self._blocks.append(block)

def _find_duplicate_keys(self, blocks: List[Block]) -> List[str]:
def _find_duplicate_keys(self, blocks: list[Block]) -> list[str]:
"""Keys of blocks that would become duplicates when added to the library."""
duplicate_keys = []
seen_entry_keys = set(self._entries_by_key)
Expand All @@ -91,7 +87,7 @@ def _block_index(self, block: Block) -> int:
# No identity match; fall back to equality (raises ValueError if not found).
return self._blocks.index(block)

def remove(self, blocks: Union[List[Block], Block]):
def remove(self, blocks: list[Block] | Block):
"""Remove blocks from library.

If equal duplicate blocks exist in the library, the exact (identical)
Expand Down Expand Up @@ -143,9 +139,7 @@ def replace(self, old_block: Block, new_block: Block, fail_on_duplicate_key: boo
raise ValueError("Duplicate key found.")

@staticmethod
def _cast_to_duplicate(
prev_block_with_same_key: Union[Entry, String], duplicate: Union[Entry, String]
):
def _cast_to_duplicate(prev_block_with_same_key: Entry | String, duplicate: Entry | String):
if not (
isinstance(prev_block_with_same_key, type(duplicate))
or isinstance(duplicate, type(prev_block_with_same_key))
Expand Down Expand Up @@ -196,44 +190,44 @@ def _add_to_dicts(self, block):
return block

@property
def blocks(self) -> List[Block]:
def blocks(self) -> list[Block]:
"""All blocks in the library, preserving order of insertion."""
return self._blocks

@property
def failed_blocks(self) -> List[ParsingFailedBlock]:
def failed_blocks(self) -> list[ParsingFailedBlock]:
"""All blocks that could not be parsed, preserving order of insertion."""
return [b for b in self._blocks if isinstance(b, ParsingFailedBlock)]

@property
def strings(self) -> List[String]:
def strings(self) -> list[String]:
"""All @string blocks in the library, preserving order of insertion."""
return list(self._strings_by_key.values())

@property
def strings_dict(self) -> Dict[str, String]:
def strings_dict(self) -> dict[str, String]:
"""Dict representation of all @string blocks in the library."""
return self._strings_by_key.copy()

@property
def entries(self) -> List[Entry]:
def entries(self) -> list[Entry]:
"""All entry (@article, ...) blocks in the library, preserving order of insertion."""
# Note: Taking this from the entries dict would be faster, but does not preserve order
# e.g. in cases where `replace` has been called.
return [b for b in self._blocks if isinstance(b, Entry)]

@property
def entries_dict(self) -> Dict[str, Entry]:
def entries_dict(self) -> dict[str, Entry]:
"""Dict representation of all entry blocks in the library."""
return self._entries_by_key.copy()

@property
def preambles(self) -> List[Preamble]:
def preambles(self) -> list[Preamble]:
"""All @preamble blocks in the library, preserving order of insertion."""
return [block for block in self._blocks if isinstance(block, Preamble)]

@property
def comments(self) -> List[Union[ExplicitComment, ImplicitComment]]:
def comments(self) -> list[ExplicitComment | ImplicitComment]:
"""All comment blocks in the library, preserving order of insertion."""
return [
block for block in self._blocks if isinstance(block, (ExplicitComment, ImplicitComment))
Expand Down
10 changes: 3 additions & 7 deletions bibtexparser/middlewares/enclosing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
from typing import Optional
from typing import Tuple
from typing import Union

from bibtexparser.library import Library
from bibtexparser.model import Entry
from bibtexparser.model import Field
Expand Down Expand Up @@ -53,7 +49,7 @@ def metadata_key(cls) -> str:
return REMOVED_ENCLOSING_KEY

@staticmethod
def _strip_enclosing(value: str) -> Tuple[str, Union[str, None]]:
def _strip_enclosing(value: str) -> tuple[str, str | None]:
value = value.strip()
if value.startswith("{") and value.endswith("}"):
return value[1:-1], "{"
Expand Down Expand Up @@ -144,9 +140,9 @@ def metadata_key(cls) -> str:
def _enclose(
self,
value: str,
metadata_enclosing: Optional[str],
metadata_enclosing: str | None,
apply_int_rule: bool,
demanded_enclosing: Optional[str] = None,
demanded_enclosing: str | None = None,
) -> str:
enclosing = self._default_enclosing
if demanded_enclosing is not None:
Expand Down
9 changes: 3 additions & 6 deletions bibtexparser/middlewares/fieldkeys.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import logging
from typing import Dict
from typing import List
from typing import Set

from bibtexparser.library import Library
from bibtexparser.model import Entry
Expand Down Expand Up @@ -29,8 +26,8 @@ def __init__(self, allow_inplace_modification: bool = True):

# docstr-coverage: inherited
def transform_entry(self, entry: Entry, library: "Library") -> Entry:
seen_normalized_keys: Set[str] = set()
new_fields_dict: Dict[str, Field] = {}
seen_normalized_keys: set[str] = set()
new_fields_dict: dict[str, Field] = {}
for field in entry.fields:
normalized_key: str = field.key.lower()
# if the normalized key is already present, apply "last one wins"
Expand All @@ -48,7 +45,7 @@ def transform_entry(self, entry: Entry, library: "Library") -> Entry:
field.key = normalized_key
new_fields_dict[normalized_key] = field

new_fields: List[Field] = list(new_fields_dict.values())
new_fields: list[Field] = list(new_fields_dict.values())
entry.fields = new_fields

return entry
Loading
Loading