chore: Fix for additional linting rules
This commit is contained in:
parent
96cd4929c9
commit
f5455b6946
7 changed files with 58 additions and 40 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 ""
|
||||||
|
|
|
||||||
|
|
@ -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 []
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue