Skip to content

✨ Add SortBlocksMiddleware for custom block sorting#525

Merged
MiWeiss merged 1 commit into
mainfrom
worktree-sort-blocks-middleware
Jun 11, 2026
Merged

✨ Add SortBlocksMiddleware for custom block sorting#525
MiWeiss merged 1 commit into
mainfrom
worktree-sort-blocks-middleware

Conversation

@MiWeiss

@MiWeiss MiWeiss commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Closes #480 (see also #478, where this need came up).

What this does

Adds a generic SortBlocksMiddleware that sorts the blocks of a library by a user-provided sort-key function, working like the key argument of Python's built-in sorted:

from bibtexparser.middlewares import SortBlocksMiddleware
from bibtexparser.model import Entry

def by_year(block):
    # Tuple keys make mixed block types comparable:
    # non-entries and year-less entries on top, the rest sorted by year
    if isinstance(block, Entry) and year in block:
        return (1, int(block[year]))
    return (0, 0)

middleware = SortBlocksMiddleware(key=by_year)

reverse=True and preserve_comments_on_top are supported; comment-preservation reuses the existing _BlockJunk grouping, so custom sorts keep comments attached to their entry for free.

Design deviation from the issue body

#480 proposed an abstract class with an unimplemented compare(block1, block2) -> int. Following the later discussion in the issue (thanks @mirhahn), this PR implements a key-function API instead:

  • key= is the native Python 3 sorting idiom (cmp was removed from list.sort in Python 3); writing a correct, transitive comparator is harder than lambda e: (e[year], e.key).
  • Comparator users get a free escape hatch via functools.cmp_to_key (documented in the class docstring); there is no conversion in the other direction.
  • The existing middlewares (SortBlocksByTypeAndKeyMiddleware, both field-sorting middlewares) already sort via key functions internally — a comparator-based abstract class would have required converting working key functions into comparators.

Also, the class is concrete (key passed as constructor argument, mirroring SortFieldsCustomMiddleware) rather than abstract, so one-off sorts need no subclass; subclassing for reusable named middlewares still works.

Backwards compatibility

SortBlocksByTypeAndKeyMiddleware is now a thin subclass of SortBlocksMiddleware supplying its (type_index, block.key) tuple key. Behavior is unchanged; the pre-existing tests pass unmodified.

Review aid

The new unit tests each parse a small literal BibTeX string and assert the resulting order, so the functionality is directly visible: sort by year, reverse, hierarchical (year, author) keys, sort stability, mixed block types, and both comment-handling modes. Docs (customize.rst) gained an MWE under the Sorting section plus a note pointing custom-sorting users to the new class.

🤖 Generated with Claude Code

Adds a generic block-sorting middleware taking a sort-key function
(as the `key` argument of Python's built-in `sorted`), allowing users
to sort blocks by arbitrary criteria, e.g. by field values such as year.

The comment-preservation logic is moved from
SortBlocksByTypeAndKeyMiddleware into the new class;
SortBlocksByTypeAndKeyMiddleware is now a thin subclass of it
(fully backwards compatible, existing tests unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MiWeiss

MiWeiss commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

reviewed

@MiWeiss MiWeiss merged commit f312ac0 into main Jun 11, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Allow simple way to create custom block sorting

1 participant