chore: Fix for additional linting rules

This commit is contained in:
Marty Oehme 2025-09-11 13:38:47 +02:00
parent 96cd4929c9
commit f5455b6946
Signed by: Marty
GPG key ID: 4E535BC19C61886E
7 changed files with 58 additions and 40 deletions

View file

@ -1,4 +1,9 @@
import re import re
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from papis_extract.annotation import Annotation
from papis_extract.exporter import Exporter
import click import click
import papis.cli import papis.cli
@ -9,8 +14,6 @@ import papis.strings
from papis.document import Document from papis.document import Document
from papis_extract import extraction from papis_extract import extraction
from papis_extract.annotation import Annotation
from papis_extract.exporter import Exporter
from papis_extract.exporters import all_exporters from papis_extract.exporters import all_exporters
from papis_extract.extractors import all_extractors from papis_extract.extractors import all_extractors
from papis_extract.formatter import Formatter, formatters from papis_extract.formatter import Formatter, formatters

View file

@ -1,6 +1,6 @@
import math import math
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Optional, cast from typing import Any, cast
import chevron import chevron
import papis.config import papis.config
@ -117,7 +117,7 @@ class Annotation:
return color_mapping.get(colorname, "") return color_mapping.get(colorname, "")
# mimics the functions in papis.config.{getlist,getint,getfloat} etc. # mimics the functions in papis.config.{getlist,getint,getfloat} etc.
def _getdict(self, key: str, section: Optional[str] = None) -> dict[str, str]: def _getdict(self, key: str, section: str | None = None) -> dict[str, str]:
"""Dict getter """Dict getter
:returns: A python dict :returns: A python dict
@ -126,19 +126,17 @@ class Annotation:
""" """
rawvalue: Any = papis.config.general_get(key, section=section) rawvalue: Any = papis.config.general_get(key, section=section)
if isinstance(rawvalue, dict): if isinstance(rawvalue, dict):
return cast(dict[str, str], rawvalue) return cast("dict[str, str]", rawvalue)
try: try:
rawvalue = eval(rawvalue) rawvalue = eval(rawvalue)
except Exception: except Exception:
raise SyntaxError( raise SyntaxError(
"The key '{}' must be a valid Python object: {}".format(key, rawvalue) f"The configuration key '{key}' must be a valid Python dict: {rawvalue}"
) )
else: else:
if not isinstance(rawvalue, dict): if not isinstance(rawvalue, dict):
raise SyntaxError( raise SyntaxError(
"The key '{}' must be a valid Python dict. Got: {} (type {!r})".format( f"The configuration key '{key}' must be a valid Python dict. Got: {rawvalue} (type {type(rawvalue).__name__})"
key, rawvalue, type(rawvalue).__name__
)
) )
return cast(dict[str, str], rawvalue) return cast("dict[str, str]", rawvalue)

View file

@ -1,4 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path
import Levenshtein import Levenshtein
import papis.commands.edit import papis.commands.edit
@ -69,11 +70,11 @@ class NotesExporter:
:type duplicates: bool, optional :type duplicates: bool, optional
""" """
logger.debug("Adding annotations to note...") logger.debug("Adding annotations to note...")
notes_path = papis.notes.notes_path_ensured(document) notes_path = Path(papis.notes.notes_path_ensured(document))
existing: list[str] = [] existing: list[str] = []
with open(notes_path, "r") as file_read: with notes_path.open("r") as fr:
existing = file_read.readlines() existing = fr.readlines()
new_annotations: list[str] = formatted_annotations new_annotations: list[str] = formatted_annotations
if not duplicates: if not duplicates:
@ -81,15 +82,16 @@ class NotesExporter:
formatted_annotations, existing formatted_annotations, existing
) )
if not new_annotations: if not new_annotations:
logger.debug("No new annotations to be added.")
return return
with open(notes_path, "a") as f: with notes_path.open("a") as fa:
# add newline if theres no empty space at file end # add newline if theres no empty space at file end
if len(existing) > 0 and existing[-1].strip() != "": if len(existing) > 0 and existing[-1].strip() != "":
f.write("\n") fa.write("\n")
# We filter out any empty lines from the annotations # We filter out any empty lines from the annotations
filtered_annotations = [annot for annot in new_annotations if annot != ""] filtered_annotations = [annot for annot in new_annotations if annot != ""]
f.write("\n\n".join(filtered_annotations)) fa.write("\n\n".join(filtered_annotations))
logger.info( logger.info(
f"Wrote {len(filtered_annotations)} " f"Wrote {len(filtered_annotations)} "
f"{'line' if len(filtered_annotations) == 1 else 'lines'} " f"{'line' if len(filtered_annotations) == 1 else 'lines'} "
@ -97,11 +99,11 @@ class NotesExporter:
) )
if git: if git:
msg = "Update notes for '{0}'".format(papis.document.describe(document)) msg = f"Update annotations for '{papis.document.describe(document)}'"
folder = document.get_main_folder() folder = document.get_main_folder()
if folder: if folder:
papis.git.add_and_commit_resources( papis.git.add_and_commit_resources(
folder, [notes_path, document.get_info_file()], msg folder, [str(notes_path), document.get_info_file()], msg
) )
def _drop_existing_annotations( def _drop_existing_annotations(

View file

@ -19,9 +19,7 @@ class PdfExtractor:
if not filename.is_file(): if not filename.is_file():
logger.error(f"File {str(filename)} not readable.") logger.error(f"File {str(filename)} not readable.")
return False return False
if not self._is_pdf(filename): return self._is_pdf(filename)
return False
return True
def run(self, filename: Path) -> list[Annotation]: def run(self, filename: Path) -> list[Annotation]:
"""Extract annotations from a file. """Extract annotations from a file.
@ -39,15 +37,15 @@ class PdfExtractor:
if not quote and not note: if not quote and not note:
continue continue
color: tuple[float, float, float] = cast( color: tuple[float, float, float] = cast(
tuple[float, float, float], "tuple[float, float, float]",
( (
annot.colors.get("fill") annot.colors.get("fill")
or annot.colors.get("stroke") or annot.colors.get("stroke")
or (0.0, 0.0, 0.0) or (0.0, 0.0, 0.0)
), ),
) )
page_nr: int = cast(int, page.number or 0) page_nr: int = cast("int", page.number or 0)
highlight_type: str = cast(str, annot.type[1] or "") highlight_type: str = cast("str", annot.type[1] or "")
a = Annotation( a = Annotation(
file=str(filename), file=str(filename),
content=quote or "", content=quote or "",
@ -83,7 +81,7 @@ class PdfExtractor:
should both be included or are the same, using should both be included or are the same, using
Levenshtein distance. Levenshtein distance.
""" """
content = cast(str, annotation.info["content"].replace("\n", " ")) content = cast("str", annotation.info["content"].replace("\n", " "))
written = page.get_textbox(annotation.rect).replace("\n", " ") written = page.get_textbox(annotation.rect).replace("\n", " ")
# highlight with selection in note # highlight with selection in note
@ -94,13 +92,13 @@ class PdfExtractor:
if Levenshtein.ratio(content, written) > minimum_similarity: if Levenshtein.ratio(content, written) > minimum_similarity:
return (content, None) return (content, None)
# both a highlight and a note # both a highlight and a note
elif content and written: if content and written:
return (written, content) return (written, content)
# an independent note, not a highlight # an independent note, not a highlight
elif content: if content:
return (None, content) return (None, content)
# highlight with selection not in note # highlight with selection not in note
elif written: if written:
return (written, None) return (written, None)
# just a highlight without any text # just a highlight without any text
return (None, None) return (None, None)

View file

@ -2,7 +2,6 @@
from pathlib import Path from pathlib import Path
import magic import magic
import papis.config
import papis.logging import papis.logging
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@ -13,7 +12,7 @@ logger = papis.logging.get_logger(__name__)
class PocketBookExtractor: class PocketBookExtractor:
def can_process(self, filename: Path) -> bool: def can_process(self, filename: Path) -> bool:
if not magic.from_file(filename, mime=True) == "text/xml": if magic.from_file(filename, mime=True) != "text/xml":
return False return False
content = self._read_file(filename) content = self._read_file(filename)
@ -21,11 +20,11 @@ class PocketBookExtractor:
return False return False
html = BeautifulSoup(content, features="xml") html = BeautifulSoup(content, features="xml")
if not html.find( return bool(
"meta", {"name": "generator", "content": "PocketBook Bookmarks Export"} html.find(
): "meta", {"name": "generator", "content": "PocketBook Bookmarks Export"}
return False )
return True )
def run(self, filename: Path) -> list[Annotation]: def run(self, filename: Path) -> list[Annotation]:
"""Extract annotations from pocketbook html file. """Extract annotations from pocketbook html file.
@ -78,8 +77,8 @@ class PocketBookExtractor:
def _read_file(self, filename: Path) -> str: def _read_file(self, filename: Path) -> str:
try: try:
with open(filename) as f: with filename.open("r") as fr:
return f.read() return fr.read()
except FileNotFoundError: except FileNotFoundError:
logger.error(f"Could not open file {filename} for extraction.") logger.error(f"Could not open file {filename} for extraction.")
return "" return ""

View file

@ -17,7 +17,7 @@ class ReadEraExtractor:
""" """
def can_process(self, filename: Path) -> bool: def can_process(self, filename: Path) -> bool:
if not magic.from_file(filename, mime=True) == "text/plain": if magic.from_file(filename, mime=True) != "text/plain":
return False return False
content = self._read_file(filename) content = self._read_file(filename)
@ -83,8 +83,8 @@ class ReadEraExtractor:
def _read_file(self, filename: Path) -> list[str]: def _read_file(self, filename: Path) -> list[str]:
try: try:
with open(filename, encoding="utf-8") as f: with filename.open("r") as fr:
return f.readlines() return fr.readlines()
except FileNotFoundError: except FileNotFoundError:
logger.error(f"Could not open file {filename} for extraction.") logger.error(f"Could not open file {filename} for extraction.")
return [] return []

View file

@ -39,6 +39,24 @@ pocketbook = ["beautifulsoup4<5.0.0,>=4.12.3"]
[tool.uv] [tool.uv]
dev-dependencies = ["pytest<9.0.0,>=8.0.0", "pytest-cov<7.0.0,>=6.0.0"] dev-dependencies = ["pytest<9.0.0,>=8.0.0", "pytest-cov<7.0.0,>=6.0.0"]
[tool.ruff.lint]
extend-select = [
"C4", # Catch incorrect use of comprehensions, dict, list, etc
"F", # Pyflakes rules
"FA", # Enforce from __future__ import annotations
"I", # Sort imports properly
"ICN", # Use common import conventions
"ISC", # Good use of string concatenation
"NPY", # Some numpy-specific things
"PTH", # Use pathlib instead of os.path
"RET", # Good return practices
"SIM", # Common simplification rules
"TC", # Enforce importing certain types in a TYPE_CHECKING block
"TID", # Some good import practices
"UP", # Warn if certain things can changed due to newer Python versions
"W", # PyCodeStyle warnings
]
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"