From 804b6b82cad5af6db744cd347def2d63f683959a Mon Sep 17 00:00:00 2001 From: Martin Bernstorff Date: Fri, 27 Oct 2023 19:08:08 +0000 Subject: [PATCH] ruff: format --- application/main.py | 4 +++- makefile | 2 +- pyproject.toml | 1 + src/personal_mnemonic_medium/card_pipeline.py | 4 +++- .../exporters/anki/card_types/base.py | 14 ++++++------- .../exporters/anki/globals.py | 4 +++- .../exporters/anki/package_generator.py | 16 ++++++++++---- .../exporters/anki/sync.py | 21 ++++++++++++++----- .../markdown_to_html/html_compiler.py | 9 ++++++-- .../note_factories/note.py | 4 +++- .../prompt_extractors/cloze_extractor.py | 3 ++- .../prompt_extractors/qa_extractor.py | 8 +++++-- src/personal_mnemonic_medium/utils/hasher.py | 4 +++- tests/exporters/anki/test_card_converter.py | 16 ++++++++------ .../test_qa_prompt_extractor.py | 14 ++++++++++--- 15 files changed, 88 insertions(+), 36 deletions(-) diff --git a/application/main.py b/application/main.py index c79e27b7..54b858d9 100644 --- a/application/main.py +++ b/application/main.py @@ -14,7 +14,9 @@ from personal_mnemonic_medium.prompt_extractors.cloze_extractor import ( ClozePromptExtractor, ) -from personal_mnemonic_medium.prompt_extractors.qa_extractor import QAPromptExtractor +from personal_mnemonic_medium.prompt_extractors.qa_extractor import ( + QAPromptExtractor, +) from wasabi import Printer msg = Printer(timestamp=True) diff --git a/makefile b/makefile index ff8876d9..0f59df4b 100644 --- a/makefile +++ b/makefile @@ -15,8 +15,8 @@ test: ## Run tests pytest $(SRC_PATH) lint: ## Format code - ruff . --fix --extend-select F401 ruff format . + ruff . --fix --extend-select F401 type-check: ## Type-check code pyright $(SRC_PATH) diff --git a/pyproject.toml b/pyproject.toml index 4aec449c..f426daaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ reportMissingTypeStubs = false [tool.ruff] # Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. +line-length = 80 select = [ "A", "ANN", diff --git a/src/personal_mnemonic_medium/card_pipeline.py b/src/personal_mnemonic_medium/card_pipeline.py index 6740c71a..f42e5111 100644 --- a/src/personal_mnemonic_medium/card_pipeline.py +++ b/src/personal_mnemonic_medium/card_pipeline.py @@ -26,7 +26,9 @@ def run( ) -> list[AnkiCard]: notes: list[Document] = [] if input_path.is_dir(): - notes += list(self.document_factory.get_notes_from_dir(dir_path=input_path)) + notes += list( + self.document_factory.get_notes_from_dir(dir_path=input_path) + ) if not input_path.is_dir(): note_from_file = self.document_factory.get_note_from_file( diff --git a/src/personal_mnemonic_medium/exporters/anki/card_types/base.py b/src/personal_mnemonic_medium/exporters/anki/card_types/base.py index 06d53322..25041df1 100644 --- a/src/personal_mnemonic_medium/exporters/anki/card_types/base.py +++ b/src/personal_mnemonic_medium/exporters/anki/card_types/base.py @@ -106,13 +106,13 @@ def get_source_button(self) -> str: def to_genanki_note(self) -> genanki.Note: """Produce a genanki. Note with the specified guid.""" - if len(self.html_fields) > len(self.genanki_model.fields): # type: ignore + if len(self.html_fields) > len(self.genanki_model.fields): # type: ignore raise ValueError( - f"Too many fields for model {self.genanki_model.name}: {self.html_fields}", # type: ignore + f"Too many fields for model {self.genanki_model.name}: {self.html_fields}", # type: ignore ) - if len(self.html_fields) < len(self.genanki_model.fields): # type: ignore - while len(self.html_fields) < len(self.genanki_model.fields): # type: ignore + if len(self.html_fields) < len(self.genanki_model.fields): # type: ignore + while len(self.html_fields) < len(self.genanki_model.fields): # type: ignore before_extras_field = len(self.html_fields) == 2 if before_extras_field: self.add_field(self.get_source_button()) @@ -156,12 +156,12 @@ def determine_media_references(self) -> Iterator[tuple[Path, Path]]: results = [] def process_match(m) -> str: # noqa # type: ignore - initial_contents = m.group(1) # type: ignore - abspath, newpath = self.make_ref_pair(initial_contents) # type: ignore + initial_contents = m.group(1) # type: ignore + abspath, newpath = self.make_ref_pair(initial_contents) # type: ignore results.append((abspath, newpath)) # noqa # type: ignore return r'src="' + newpath + '"' - current_stage = re.sub(regex, process_match, current_stage) # type: ignore + current_stage = re.sub(regex, process_match, current_stage) # type: ignore yield from results diff --git a/src/personal_mnemonic_medium/exporters/anki/globals.py b/src/personal_mnemonic_medium/exporters/anki/globals.py index 4323af3a..037b3bd6 100644 --- a/src/personal_mnemonic_medium/exporters/anki/globals.py +++ b/src/personal_mnemonic_medium/exporters/anki/globals.py @@ -4,7 +4,9 @@ from personal_mnemonic_medium.exporters.anki.anki_css import CARD_MODEL_CSS -ANKICONNECT_URL = "http://host.docker.internal:8765" # On host machine, port is 8765 +ANKICONNECT_URL = ( + "http://host.docker.internal:8765" +) # On host machine, port is 8765 CARD_MATHJAX_CONTENT = textwrap.dedent( """\ diff --git a/src/personal_mnemonic_medium/exporters/anki/package_generator.py b/src/personal_mnemonic_medium/exporters/anki/package_generator.py index 01cef39f..ea110507 100644 --- a/src/personal_mnemonic_medium/exporters/anki/package_generator.py +++ b/src/personal_mnemonic_medium/exporters/anki/package_generator.py @@ -15,7 +15,9 @@ from personal_mnemonic_medium.exporters.anki.card_types.cloze import AnkiCloze from personal_mnemonic_medium.exporters.anki.card_types.qa import AnkiQA from personal_mnemonic_medium.exporters.base import CardExporter -from personal_mnemonic_medium.prompt_extractors.cloze_extractor import ClozePrompt +from personal_mnemonic_medium.prompt_extractors.cloze_extractor import ( + ClozePrompt, +) from personal_mnemonic_medium.prompt_extractors.prompt import Prompt from personal_mnemonic_medium.prompt_extractors.qa_extractor import QAPrompt from personal_mnemonic_medium.utils.hasher import simple_hash @@ -35,7 +37,9 @@ class DeckBundle: media: set[str] def get_package(self) -> genanki.Package: - return genanki.Package(deck_or_decks=self.deck, media_files=list(self.media)) + return genanki.Package( + deck_or_decks=self.deck, media_files=list(self.media) + ) def save_deck_to_file(self, output_path: Path) -> Path: package = self.get_package() @@ -85,7 +89,9 @@ def cards_to_deck( try: deck.add_note(card.to_genanki_note()) except IndexError as e: - log.debug(f"Could not add card {card} to deck {deck_name}, {e}.") + log.debug( + f"Could not add card {card} to deck {deck_name}, {e}." + ) return deck, media @@ -112,7 +118,9 @@ def prompts_to_cards( source_prompt=prompt, ) else: - raise NotImplementedError(f"Prompt type {type(prompt)} not supported.") + raise NotImplementedError( + f"Prompt type {type(prompt)} not supported." + ) cards += [card] diff --git a/src/personal_mnemonic_medium/exporters/anki/sync.py b/src/personal_mnemonic_medium/exporters/anki/sync.py index 75096b57..e457746f 100644 --- a/src/personal_mnemonic_medium/exporters/anki/sync.py +++ b/src/personal_mnemonic_medium/exporters/anki/sync.py @@ -31,7 +31,9 @@ def invoke(action: Any, **params: Any) -> Any: """ requestJson = json.dumps(request(action, **params)).encode("utf-8") response = json.load( - urllib.request.urlopen(urllib.request.Request(ANKICONNECT_URL, requestJson)), + urllib.request.urlopen( + urllib.request.Request(ANKICONNECT_URL, requestJson) + ), ) if len(response) != 2: raise Exception("response has an unexpected number of fields") @@ -102,7 +104,9 @@ def sync_deck( msg.info("\tNotes removed: ") msg.info(f"\t\t{removed_note_guids}") - package_path = deck_bundle.save_deck_to_file(save_dir_path / "deck.apkg") + package_path = deck_bundle.save_deck_to_file( + save_dir_path / "deck.apkg" + ) try: sync_path = str(sync_dir_path / "deck.apkg") invoke("importPackage", path=sync_path) @@ -124,7 +128,9 @@ def sync_deck( msg.good(f"Deleted {len(guids_to_delete)} notes") except Exception: - msg.fail(f"Unable to delete cards in {deck_bundle.deck.name}") + msg.fail( + f"Unable to delete cards in {deck_bundle.deck.name}" + ) # Print full stack trace traceback.print_exc() except Exception as e: @@ -144,7 +150,9 @@ def get_md_note_infos(deck_bundle: DeckBundle) -> set[str]: return md_note_guids -def get_anki_note_infos(deck_bundle: DeckBundle) -> tuple[dict[str, Any], set[str]]: +def get_anki_note_infos( + deck_bundle: DeckBundle +) -> tuple[dict[str, Any], set[str]]: anki_card_ids: list[int] = invoke( "findCards", query=f'"deck:{deck_bundle.deck.name}"', @@ -158,7 +166,10 @@ def get_anki_note_infos(deck_bundle: DeckBundle) -> tuple[dict[str, Any], set[st # convert the note info into a dictionary of guid to note info anki_note_info_by_guid = { - n["fields"]["UUID"]["value"].replace("

", "").replace("

", "").strip(): n + n["fields"]["UUID"]["value"] + .replace("

", "") + .replace("

", "") + .strip(): n for n in anki_notes_info } diff --git a/src/personal_mnemonic_medium/exporters/markdown_to_html/html_compiler.py b/src/personal_mnemonic_medium/exporters/markdown_to_html/html_compiler.py index d2732b52..b88f3268 100644 --- a/src/personal_mnemonic_medium/exporters/markdown_to_html/html_compiler.py +++ b/src/personal_mnemonic_medium/exporters/markdown_to_html/html_compiler.py @@ -12,7 +12,10 @@ def field_to_html(field: Any) -> str: If math is separated with dollar sign it is converted to brackets. """ if CONFIG["dollar"]: - for sep, (op, cl) in [("$$", (r"\\[", r"\\]")), ("$", (r"\\(", r"\\)"))]: + for sep, (op, cl) in [ + ("$$", (r"\\[", r"\\]")), + ("$", (r"\\(", r"\\)")), + ]: escaped_sep = sep.replace(r"$", r"\$") # ignore escaped dollar signs when splitting the field field = re.split(rf"(? str: token_instances = re.findall(pattern, field) for instance in token_instances: - field = field.replace(instance, replacement + instance[1:-1] + replacement) # type: ignore + field = field.replace( + instance, replacement + instance[1:-1] + replacement + ) # type: ignore # Make sure every \n converts into a newline field = field.replace("\n", " \n") diff --git a/src/personal_mnemonic_medium/note_factories/note.py b/src/personal_mnemonic_medium/note_factories/note.py index 93693398..4b35706d 100644 --- a/src/personal_mnemonic_medium/note_factories/note.py +++ b/src/personal_mnemonic_medium/note_factories/note.py @@ -18,7 +18,9 @@ def __init__( import_time_formatted = datetime.datetime.now().strftime("%Y-%m-%d") - self.tags = self.get_tags(self.content, import_time=import_time_formatted) + self.tags = self.get_tags( + self.content, import_time=import_time_formatted + ) @staticmethod def replace_alias_wiki_links(text: str) -> str: diff --git a/src/personal_mnemonic_medium/prompt_extractors/cloze_extractor.py b/src/personal_mnemonic_medium/prompt_extractors/cloze_extractor.py index 53de7a74..d1fb4054 100644 --- a/src/personal_mnemonic_medium/prompt_extractors/cloze_extractor.py +++ b/src/personal_mnemonic_medium/prompt_extractors/cloze_extractor.py @@ -56,7 +56,8 @@ def _replace_cloze_id_with_unique( for cloze in selected_clozes: output_hash = ( - int(hashlib.sha256(cloze.encode("utf-8")).hexdigest(), 16) % 10**3 + int(hashlib.sha256(cloze.encode("utf-8")).hexdigest(), 16) + % 10**3 ) new_cloze = f"{{{{c{output_hash}::{cloze[1:-1]}}}}}" diff --git a/src/personal_mnemonic_medium/prompt_extractors/qa_extractor.py b/src/personal_mnemonic_medium/prompt_extractors/qa_extractor.py index 36b899cc..701527de 100644 --- a/src/personal_mnemonic_medium/prompt_extractors/qa_extractor.py +++ b/src/personal_mnemonic_medium/prompt_extractors/qa_extractor.py @@ -17,14 +17,18 @@ class QAPrompt(Prompt): - def __init__(self, question: str, answer: str, *args: Any, **kwargs: Any) -> None: + def __init__( + self, question: str, answer: str, *args: Any, **kwargs: Any + ) -> None: super().__init__(*args, **kwargs) self.question = question self.answer = answer class QAPromptExtractor(PromptExtractor): - def __init__(self, question_prefix: str = "Q.", answer_prefix: str = "A.") -> None: + def __init__( + self, question_prefix: str = "Q.", answer_prefix: str = "A." + ) -> None: self.question_prefix = question_prefix self.answer_prefix = answer_prefix diff --git a/src/personal_mnemonic_medium/utils/hasher.py b/src/personal_mnemonic_medium/utils/hasher.py index 7130ad51..d36db008 100644 --- a/src/personal_mnemonic_medium/utils/hasher.py +++ b/src/personal_mnemonic_medium/utils/hasher.py @@ -3,6 +3,8 @@ def simple_hash(text: str) -> int: """MD5 of text, mod 2^63. Probably not a great hash function.""" - comp_hash = int(hashlib.sha256(text.encode("utf-8")).hexdigest(), 16) % 10**10 + comp_hash = ( + int(hashlib.sha256(text.encode("utf-8")).hexdigest(), 16) % 10**10 + ) return comp_hash diff --git a/tests/exporters/anki/test_card_converter.py b/tests/exporters/anki/test_card_converter.py index 5133ff55..628e68e4 100644 --- a/tests/exporters/anki/test_card_converter.py +++ b/tests/exporters/anki/test_card_converter.py @@ -90,7 +90,9 @@ def test_get_subtags(): def test_qa_uuid_generation(): file_path = ( - Path(__file__).parent.parent.parent / "test_md_files" / "test_card_guid.md" + Path(__file__).parent.parent.parent + / "test_md_files" + / "test_card_guid.md" ) cards = TestCardPipeline(prompt_extractors=[QAPromptExtractor()]).run( input_path=file_path, @@ -106,9 +108,13 @@ def test_qa_uuid_generation(): def test_cloze_uuid_generation(): file_path = ( - Path(__file__).parent.parent.parent / "test_md_files" / "test_card_guid.md" + Path(__file__).parent.parent.parent + / "test_md_files" + / "test_card_guid.md" ) - cloze_cards = TestCardPipeline(prompt_extractors=[ClozePromptExtractor()]).run( + cloze_cards = TestCardPipeline( + prompt_extractors=[ClozePromptExtractor()] + ).run( input_path=file_path, ) @@ -121,9 +127,7 @@ def test_get_bear_id(): factory = MarkdownNoteFactory() note_str = r"Q. A card with a GUID.\nA. And here is its answer.\n\nQS. How about a card like this?\nA. Yes, an answer too.\n\nQ. How about multiline questions?\n* Like this\n* Or this?\nA. What is the hash?\n\nAnd some {cloze} deletions? For sure! Multipe {even}.\n\n" - expected_id = ( - r"" - ) + expected_id = r"" extracted_id = factory.get_note_id(note_str) diff --git a/tests/prompt_extractors/test_qa_prompt_extractor.py b/tests/prompt_extractors/test_qa_prompt_extractor.py index 233ecd49..66dca9b8 100644 --- a/tests/prompt_extractors/test_qa_prompt_extractor.py +++ b/tests/prompt_extractors/test_qa_prompt_extractor.py @@ -2,7 +2,9 @@ import pytest from personal_mnemonic_medium.note_factories.note import Document -from personal_mnemonic_medium.prompt_extractors.qa_extractor import QAPromptExtractor +from personal_mnemonic_medium.prompt_extractors.qa_extractor import ( + QAPromptExtractor, +) @pytest.fixture() @@ -35,13 +37,19 @@ def test_has_qa_matches(qa_extractor: QAPromptExtractor): "QA. Testing something else, even with QA in it!", "\\Q. Testing newlines as well!", ] - matches = [string for string in example_strings if qa_extractor._has_qa(string)] + matches = [ + string for string in example_strings if qa_extractor._has_qa(string) + ] assert len(matches) == 3 def test_has_qa_does_not_match(qa_extractor: QAPromptExtractor): - example_strings = ["\nQ.E.D.", "> A question like this, or", "::Q. A comment!::"] + example_strings = [ + "\nQ.E.D.", + "> A question like this, or", + "::Q. A comment!::", + ] matches = 0