From 7bb4d2d4bf3ddbec0205075e83c7e9e8e5c6110d Mon Sep 17 00:00:00 2001 From: s-heppner Date: Thu, 8 Oct 2026 21:26:09 +0200 Subject: [PATCH] Simplify rendering of the linter output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the output listed the violations of each entry below an extra `❌ Invariant Violations:` line, and every violation message repeated the name of the entry, although the header line already names it. This made the output more verbose than necessary. With this change, each violation is printed directly below the header with one indentation level less. The messages of all helper functions in `verification.py` and of the `ieeetr` and `IEEEtran` rulesets no longer contain the entry name. The tests and the `README.md` are updated accordingly. Fixes #14 --- README.md | 7 +++---- bibtex_linter/ieeetr_rules.py | 12 ++++++------ bibtex_linter/ieeetran_rules.py | 18 +++++++++--------- bibtex_linter/main.py | 3 +-- bibtex_linter/verification.py | 10 +++++----- test/test_verification.py | 8 ++++---- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7066c7c..1943e29 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,8 @@ A Python tool to parse BibTeX entries and run (custom) checks on them. > bibtex_linter refs.bib Entry 'SomeBook' of type 'BOOK' failed verification: - ❌ Invariant Violations: - - Entry 'SomeBook' misses the following required fields: [publisher] - - Entry 'SomeBook' has fields present that would be omitted in the compiled document: [url]. This could lead to a loss of information. + ❌ Misses the following required fields: [publisher] + ❌ Has fields present that would be omitted in the compiled document: [url]. This could lead to a loss of information. Found 2 invariant violations in 17 entries. ``` @@ -83,7 +82,7 @@ def check_article(entry: BibTeXEntry) -> List[str]: :return: A list of string descriptions of rule violations for this entry. """ if not entry.fields.get("author"): - return [f"Entry '{entry.name}' misses the required field author!"] + return ["Misses the required field [author]."] return [] ``` diff --git a/bibtex_linter/ieeetr_rules.py b/bibtex_linter/ieeetr_rules.py index cc974f7..7eca698 100644 --- a/bibtex_linter/ieeetr_rules.py +++ b/bibtex_linter/ieeetr_rules.py @@ -87,7 +87,7 @@ def check_online(entry: BibTeXEntry) -> List[str]: "url", } if "note" not in entry.fields.keys(): - invariant_violations.append(f"Entry '{entry.name}' is of type 'online' and needs a field 'note' with the URL.") + invariant_violations.append("Is of type 'online' and needs a field 'note' with the URL.") # Todo: In the future, we could actually check that it contains an URL invariant_violations.extend(check_required_fields(entry, required_fields)) invariant_violations.extend(check_omitted_fields(entry, omitted_fields)) @@ -145,8 +145,8 @@ def check_in_book(entry: BibTeXEntry) -> List[str]: "url", } if "chapter" not in entry.fields.keys() or "pages" not in entry.fields.keys(): - invariant_violations.append(f"Entry {entry.name} needs to contain one of the " - f"following fields: [chapter, pages].") + invariant_violations.append("Needs to contain one of the " + "following fields: [chapter, pages].") invariant_violations.extend(check_required_fields(entry, required_fields)) invariant_violations.extend(check_omitted_fields(entry, omitted_fields)) return invariant_violations @@ -233,9 +233,9 @@ def check_tech_report(entry: BibTeXEntry) -> List[str]: "url", } if "howpublished" in entry.fields.keys(): - invariant_violations.append(f"Entry {entry.name} is of type 'techreport', which does not render field " - f"'howpublished'. Either use field 'institution' instead, or switch to a different " - f"entry type completely.") + invariant_violations.append("Is of type 'techreport', which does not render field " + "'howpublished'. Either use field 'institution' instead, or switch to a different " + "entry type completely.") invariant_violations.extend(check_required_fields(entry, required_fields)) invariant_violations.extend(check_omitted_fields(entry, omitted_fields)) return invariant_violations diff --git a/bibtex_linter/ieeetran_rules.py b/bibtex_linter/ieeetran_rules.py index dbf129d..c9512cd 100644 --- a/bibtex_linter/ieeetran_rules.py +++ b/bibtex_linter/ieeetran_rules.py @@ -28,8 +28,8 @@ def check_url_field(entry: BibTeXEntry) -> List[str]: invariant_violations: List[str] = [] if "url" in entry.fields.keys(): invariant_violations.append( - f"Entry '{entry.name}' contains the non-allowed field: [url]. " - f"Move the content of the field into the [note] field." + "Contains the non-allowed field: [url]. " + "Move the content of the field into the [note] field." ) if "note" in entry.fields.keys(): note_content: str = entry.fields["note"] @@ -37,7 +37,7 @@ def check_url_field(entry: BibTeXEntry) -> List[str]: match = re.match(pattern, note_content) if not match: invariant_violations.append( - f"Entry '{entry.name}' contains a malformed field [note]. " + "Contains a malformed field [note]. " "Make sure the [note] field follows the following pattern: '[ONLINE]. Available: \\url{...}, " "Accessed: YYYY-mmm-dd'" ) @@ -103,7 +103,7 @@ def check_conference(entry: BibTeXEntry) -> List[str]: )) if entry.fields.get("organization") == entry.fields.get("publisher"): invariant_violations.append( - f"Entry '{entry.name}' fields [organization] and [publisher] are the same. Remove field [organization]." + "Fields [organization] and [publisher] are the same. Remove field [organization]." ) return invariant_violations @@ -134,7 +134,7 @@ def check_online(entry: BibTeXEntry) -> List[str]: )) if entry.fields.get("organization") == entry.fields.get("author"): invariant_violations.append( - f"Entry '{entry.name}' fields [organization] and [author] are the same. Remove field [organization]." + "Fields [organization] and [author] are the same. Remove field [organization]." ) return invariant_violations @@ -160,7 +160,7 @@ def check_book(entry: BibTeXEntry) -> List[str]: )) if entry.fields.get("publisher") == entry.fields.get("editor"): invariant_violations.append( - f"Entry '{entry.name}' fields [publisher] and [editor] are the same. Remove field [editor]." + "Fields [publisher] and [editor] are the same. Remove field [editor]." ) return invariant_violations @@ -225,7 +225,7 @@ def check_in_collection(entry: BibTeXEntry) -> List[str]: )) if entry.fields.get("editor") == entry.fields.get("publisher"): invariant_violations.append( - f"Entry '{entry.name}' fields [editor] and [publisher] are the same. Remove field [editor]." + "Fields [editor] and [publisher] are the same. Remove field [editor]." ) return invariant_violations @@ -263,7 +263,7 @@ def check_standard(entry: BibTeXEntry) -> List[str]: )) if entry.fields.get("author") == entry.fields.get("organization"): invariant_violations.append( - f"Entry '{entry.name}' fields [author] and [organization] are the same. Remove field [author]." + "Fields [author] and [organization] are the same. Remove field [author]." ) return invariant_violations @@ -276,7 +276,7 @@ def check_tech_report(entry: BibTeXEntry) -> List[str]: :param entry: The BibTeXEntry :return: A list of string descriptions of rule violations for this entry. """ - return [f"Entry '{entry.name}' is of type 'TECHREPORT'. Please use a different entry type, such as 'STANDARD'."] + return ["Is of type 'TECHREPORT'. Please use a different entry type, such as 'STANDARD'."] @linter_rule(entry_type="misc") diff --git a/bibtex_linter/main.py b/bibtex_linter/main.py index 09a29c6..de7f541 100644 --- a/bibtex_linter/main.py +++ b/bibtex_linter/main.py @@ -61,9 +61,8 @@ def main() -> None: if violations: had_violations = True print(f"\nEntry '{entry.name}' of type '{entry.entry_type}' failed verification:") - print(" ❌ Invariant Violations:") for issue in violations: - print(f" - {issue}") + print(f" ❌ {issue}") print(f"\n\nFound {total_number_of_violations} invariant violation(s) in {len(entries)} entries.") diff --git a/bibtex_linter/verification.py b/bibtex_linter/verification.py index 140e527..e4f4e5d 100644 --- a/bibtex_linter/verification.py +++ b/bibtex_linter/verification.py @@ -38,7 +38,7 @@ def check_required_fields(entry: BibTeXEntry, fields: Set[str]) -> List[str]: existing_fields: Set[str] = set(entry.fields.keys()) if not fields.issubset(entry.fields): missing = fields - existing_fields - return [f"Entry '{entry.name}' misses the following required fields: [{', '.join(sorted(missing))}]"] + return [f"Misses the following required fields: [{', '.join(sorted(missing))}]"] return [] @@ -49,7 +49,7 @@ def check_required_field(entry: BibTeXEntry, field: str, explanation: str) -> Li out the required field. """ if field not in entry.fields.keys(): - return [f"Entry '{entry.name}' misses required field [{field}]. {explanation}"] + return [f"Misses required field [{field}]. {explanation}"] return [] @@ -61,7 +61,7 @@ def check_omitted_fields(entry: BibTeXEntry, fields: Set[str]) -> List[str]: omitted_fields_present = fields & existing_fields if omitted_fields_present: - return [f"Entry '{entry.name}' has fields present that would be omitted in the compiled document: " + return ["Has fields present that would be omitted in the compiled document: " f"[{', '.join(sorted(omitted_fields_present))}]. This could lead to a loss of information."] return [] @@ -74,7 +74,7 @@ def check_disallowed_fields(entry: BibTeXEntry, fields: Set[str]) -> List[str]: disallowed_fields_present = fields & existing_fields if disallowed_fields_present: - return [f"Entry '{entry.name}' has fields present that would be omitted in the compiled document: " + return ["Has fields present that would be omitted in the compiled document: " f"[{', '.join(sorted(disallowed_fields_present))}]."] return [] @@ -86,7 +86,7 @@ def check_disallowed_field(entry: BibTeXEntry, field: str, explanation: str) -> why it is disallowed. """ if field in entry.fields.keys(): - return [f"Entry '{entry.name}' contains disallowed field [{field}]. {explanation}"] + return [f"Contains disallowed field [{field}]. {explanation}"] return [] diff --git a/test/test_verification.py b/test/test_verification.py index 1534867..9b3e733 100644 --- a/test/test_verification.py +++ b/test/test_verification.py @@ -22,7 +22,7 @@ def test_check_required_fields_missing(self) -> None: name="missing_fields", fields={"author": "Jane"} ) - expected = ["Entry 'missing_fields' misses the following required fields: [howpublished, title, year]"] + expected = ["Misses the following required fields: [howpublished, title, year]"] actual = check_required_fields(entry, {"author", "title", "howpublished", "year"}) self.assertEqual(expected, actual) @@ -41,7 +41,7 @@ def test_check_omitted_fields_present(self) -> None: fields={"author": "Jane", "language": "en", "url": "example.com"} ) expected = [ - "Entry 'omit_test' has fields present that would be omitted in the compiled document: " + "Has fields present that would be omitted in the compiled document: " "[language, url]. This could lead to a loss of information." ] actual = check_omitted_fields(entry, {"language", "organization", "address", "pages", "url"}) @@ -62,8 +62,8 @@ def test_verify_combined_rule(self) -> None: fields={"author": "Jane", "url": "http://example.org"} ) expected = [ - "Entry 'bad_entry' misses the following required fields: [howpublished, title, year]", - "Entry 'bad_entry' has fields present that would be omitted in the compiled document: [url]. " + "Misses the following required fields: [howpublished, title, year]", + "Has fields present that would be omitted in the compiled document: [url]. " "This could lead to a loss of information." ] actual = verify(entry)