mirror of
https://github.com/Ascyii/typstar.git
synced 2026-01-01 13:34:24 -05:00
style: format python code using ruff
This commit is contained in:
@@ -21,3 +21,12 @@ dependencies = [
|
||||
[project.scripts]
|
||||
typstar-anki = "anki.main:main"
|
||||
|
||||
[tool.ruff]
|
||||
lint.extend-select = ["I"]
|
||||
line-length = 100
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff>=0.8.5",
|
||||
]
|
||||
|
||||
|
||||
@@ -42,11 +42,17 @@ class AnkiConnectApi:
|
||||
update[card.deck].append(card)
|
||||
n_update += 1
|
||||
|
||||
print(f"Pushing {n_add} new flashcards and {n_update} updated flashcards to Anki...", flush=True)
|
||||
print(
|
||||
f"Pushing {n_add} new flashcards and {n_update} updated flashcards to Anki...",
|
||||
flush=True,
|
||||
)
|
||||
await self._create_required_decks({*add.keys(), *update.keys()})
|
||||
await self._add_new_cards(add)
|
||||
await _gather_exceptions(
|
||||
[*self._update_cards_requests(add), *self._update_cards_requests(update, True)]
|
||||
[
|
||||
*self._update_cards_requests(add),
|
||||
*self._update_cards_requests(update, True),
|
||||
]
|
||||
)
|
||||
|
||||
async def _request_api(self, action, **params):
|
||||
@@ -71,12 +77,16 @@ class AnkiConnectApi:
|
||||
await self._request_api("updateNoteModel", note=card.as_anki_model())
|
||||
|
||||
async def _store_media(self, card):
|
||||
await self._request_api("storeMediaFile",
|
||||
filename=card.svg_filename(True),
|
||||
data=base64.b64encode(card.svg_front).decode())
|
||||
await self._request_api("storeMediaFile",
|
||||
filename=card.svg_filename(False),
|
||||
data=base64.b64encode(card.svg_back).decode())
|
||||
await self._request_api(
|
||||
"storeMediaFile",
|
||||
filename=card.svg_filename(True),
|
||||
data=base64.b64encode(card.svg_front).decode(),
|
||||
)
|
||||
await self._request_api(
|
||||
"storeMediaFile",
|
||||
filename=card.svg_filename(False),
|
||||
data=base64.b64encode(card.svg_back).decode(),
|
||||
)
|
||||
|
||||
async def _change_deck(self, deck: str, cards: List[int]):
|
||||
await self._request_api("changeDeck", deck=deck, cards=cards)
|
||||
@@ -107,7 +117,9 @@ class AnkiConnectApi:
|
||||
requests.append(self._request_api("createDeck", deck=deck))
|
||||
await _gather_exceptions(requests)
|
||||
|
||||
def _update_cards_requests(self, cards_map: dict[str, List[Flashcard]], update_deck: bool = True):
|
||||
def _update_cards_requests(
|
||||
self, cards_map: dict[str, List[Flashcard]], update_deck: bool = True
|
||||
):
|
||||
requests = []
|
||||
for deck, cards in cards_map.items():
|
||||
card_ids = []
|
||||
|
||||
@@ -25,20 +25,25 @@ class FileHandler:
|
||||
return hashlib.md5("".join(self.file_content).encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
def get_node_content(self, node: tree_sitter.Node, remove_outer=False):
|
||||
content = "".join(
|
||||
self.file_content[node.start_point.row:node.end_point.row + 1]
|
||||
)[node.start_point.column:-(len(self.file_content[node.end_point.row]) - node.end_point.column)]
|
||||
content = "".join(self.file_content[node.start_point.row : node.end_point.row + 1])[
|
||||
node.start_point.column : -(
|
||||
len(self.file_content[node.end_point.row]) - node.end_point.column
|
||||
)
|
||||
]
|
||||
return content[1:-1] if remove_outer else content
|
||||
|
||||
def update_node_content(self, node: tree_sitter.Node, value):
|
||||
new_lines = self.file_content[:node.start_point.row]
|
||||
first_line = self.file_content[node.start_point.row][:node.start_point.column]
|
||||
last_line = self.file_content[node.end_point.row][node.end_point.column:]
|
||||
new_lines.extend((
|
||||
line + "\n" for line in (first_line + str(value) + last_line).split("\n")
|
||||
if line != ""
|
||||
))
|
||||
new_lines.extend(self.file_content[node.end_point.row + 1:])
|
||||
new_lines = self.file_content[: node.start_point.row]
|
||||
first_line = self.file_content[node.start_point.row][: node.start_point.column]
|
||||
last_line = self.file_content[node.end_point.row][node.end_point.column :]
|
||||
new_lines.extend(
|
||||
(
|
||||
line + "\n"
|
||||
for line in (first_line + str(value) + last_line).split("\n")
|
||||
if line != ""
|
||||
)
|
||||
)
|
||||
new_lines.extend(self.file_content[node.end_point.row + 1 :])
|
||||
self.file_content = new_lines
|
||||
|
||||
def read(self):
|
||||
|
||||
@@ -10,7 +10,7 @@ class Flashcard:
|
||||
deck: str
|
||||
id_updated: bool
|
||||
|
||||
preamble: str
|
||||
preamble: str | None
|
||||
file_handler: FileHandler
|
||||
|
||||
note_id_node: tree_sitter.Node
|
||||
@@ -20,7 +20,15 @@ class Flashcard:
|
||||
svg_front: bytes
|
||||
svg_back: bytes
|
||||
|
||||
def __init__(self, front: str, back: str, deck: str | None, note_id: int, preamble: str, file_handler: FileHandler):
|
||||
def __init__(
|
||||
self,
|
||||
front: str,
|
||||
back: str,
|
||||
deck: str | None,
|
||||
note_id: int,
|
||||
preamble: str | None,
|
||||
file_handler: FileHandler,
|
||||
):
|
||||
if deck is None:
|
||||
deck = "Default"
|
||||
if not note_id:
|
||||
@@ -51,7 +59,7 @@ class Flashcard:
|
||||
"Front": f"tmp typst: {self.front}" if tmp else self.as_html(True),
|
||||
"Back": f"tmp typst: {self.back}" if tmp else self.as_html(False),
|
||||
},
|
||||
"tags": ["typst"]
|
||||
"tags": ["typst"],
|
||||
}
|
||||
if not self.is_new():
|
||||
model["id"] = self.note_id
|
||||
@@ -63,7 +71,9 @@ class Flashcard:
|
||||
def is_new(self) -> bool:
|
||||
return self.note_id == 0 or self.note_id is None
|
||||
|
||||
def set_ts_nodes(self, front: tree_sitter.Node, back: tree_sitter.Node, note_id: tree_sitter.Node):
|
||||
def set_ts_nodes(
|
||||
self, front: tree_sitter.Node, back: tree_sitter.Node, note_id: tree_sitter.Node
|
||||
):
|
||||
self.front_node = front
|
||||
self.back_node = back
|
||||
self.note_id_node = note_id
|
||||
|
||||
@@ -36,16 +36,33 @@ async def export_flashcards(root_dir, force_scan, clear_cache, typst_cmd, anki_u
|
||||
|
||||
|
||||
@cli.command()
|
||||
def cmd(root_dir: Annotated[
|
||||
Path, typer.Option(help="Directory scanned for flashcards and passed over to typst compile command")] = os.getcwd(),
|
||||
force_scan: Annotated[Path, typer.Option(help="File/directory to scan for flashcards while ignoring stored "
|
||||
"file hashes (e.g. on preamble change)")] = None,
|
||||
clear_cache: Annotated[bool, typer.Option(help="Clear all stored file hashes (more aggressive than force-scan "
|
||||
"as it clears hashes regardless of their path)")] = False,
|
||||
typst_cmd: Annotated[str, typer.Option(help="Typst command used for flashcard compilation")] = "typst",
|
||||
anki_url: Annotated[str, typer.Option(help="Url for Anki-Connect")] = "http://127.0.0.1:8765",
|
||||
anki_key: Annotated[str, typer.Option(help="Api key for Anki-Connect")] = None,
|
||||
):
|
||||
def cmd(
|
||||
root_dir: Annotated[
|
||||
Path,
|
||||
typer.Option(
|
||||
help="Directory scanned for flashcards and passed over to typst compile command"
|
||||
),
|
||||
] = Path(os.getcwd()),
|
||||
force_scan: Annotated[
|
||||
Path | None,
|
||||
typer.Option(
|
||||
help="File/directory to scan for flashcards while ignoring stored "
|
||||
"file hashes (e.g. on preamble change)"
|
||||
),
|
||||
] = None,
|
||||
clear_cache: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
help="Clear all stored file hashes (more aggressive than force-scan "
|
||||
"as it clears hashes regardless of their path)"
|
||||
),
|
||||
] = False,
|
||||
typst_cmd: Annotated[
|
||||
str, typer.Option(help="Typst command used for flashcard compilation")
|
||||
] = "typst",
|
||||
anki_url: Annotated[str, typer.Option(help="Url for Anki-Connect")] = "http://127.0.0.1:8765",
|
||||
anki_key: Annotated[str | None, typer.Option(help="Api key for Anki-Connect")] = None,
|
||||
):
|
||||
asyncio.run(export_flashcards(root_dir, force_scan, clear_cache, typst_cmd, anki_url, anki_key))
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import glob
|
||||
import json
|
||||
import re
|
||||
|
||||
from functools import cache
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
@@ -72,17 +71,27 @@ class FlashcardParser:
|
||||
card_captures["front"].sort(key=row_compare)
|
||||
card_captures["back"].sort(key=row_compare)
|
||||
|
||||
deck_refs: List[Tuple[int, str]] = []
|
||||
deck_refs: List[Tuple[int, str | None]] = []
|
||||
deck_refs_idx = -1
|
||||
current_deck = None
|
||||
if deck_captures:
|
||||
deck_captures["deck"].sort(key=row_compare)
|
||||
for comment in deck_captures["deck"]:
|
||||
if match := deck_regex.match(file.get_node_content(comment)):
|
||||
deck_refs.append((comment.start_point.row, None if match[1].isspace() else match[1]))
|
||||
deck_refs.append(
|
||||
(
|
||||
comment.start_point.row,
|
||||
None if match[1].isspace() else match[1],
|
||||
)
|
||||
)
|
||||
|
||||
for note_id, front, back in zip(card_captures["id"], card_captures["front"], card_captures["back"]):
|
||||
while deck_refs_idx < len(deck_refs) - 1 and back.end_point.row >= deck_refs[deck_refs_idx + 1][0]:
|
||||
for note_id, front, back in zip(
|
||||
card_captures["id"], card_captures["front"], card_captures["back"]
|
||||
):
|
||||
while (
|
||||
deck_refs_idx < len(deck_refs) - 1
|
||||
and back.end_point.row >= deck_refs[deck_refs_idx + 1][0]
|
||||
):
|
||||
deck_refs_idx += 1
|
||||
current_deck = deck_refs[deck_refs_idx][1]
|
||||
|
||||
@@ -98,7 +107,7 @@ class FlashcardParser:
|
||||
cards.append(card)
|
||||
return cards
|
||||
|
||||
def parse_directory(self, root_dir: Path, force_scan: Path = None):
|
||||
def parse_directory(self, root_dir: Path, force_scan: Path | None = None):
|
||||
single_file = None
|
||||
is_force_scan = force_scan is not None
|
||||
if is_force_scan:
|
||||
@@ -110,7 +119,10 @@ class FlashcardParser:
|
||||
else:
|
||||
scan_dir = root_dir
|
||||
|
||||
print(f"Parsing flashcards in {scan_dir if single_file is None else single_file} ...", flush=True)
|
||||
print(
|
||||
f"Parsing flashcards in {scan_dir if single_file is None else single_file} ...",
|
||||
flush=True,
|
||||
)
|
||||
preambles = {}
|
||||
flashcards = []
|
||||
|
||||
@@ -121,7 +133,9 @@ class FlashcardParser:
|
||||
return preamble
|
||||
path = path.parent
|
||||
|
||||
for file in sorted(glob.glob(f"{root_dir}/**/.anki.typ", include_hidden=True, recursive=True)):
|
||||
for file in sorted(
|
||||
glob.glob(f"{root_dir}/**/.anki.typ", include_hidden=True, recursive=True)
|
||||
):
|
||||
file = Path(file)
|
||||
if file.name == ".anki.typ":
|
||||
preambles[file.parent] = file.read_text(encoding="utf-8")
|
||||
|
||||
@@ -31,7 +31,11 @@ class TypstCompiler:
|
||||
def __init__(self, typst_root_dir: Path, typst_cmd: str):
|
||||
self.typst_cmd = typst_cmd
|
||||
self.typst_root_dir = typst_root_dir
|
||||
self.max_processes = round(os.cpu_count() * 1.5)
|
||||
n_cpus = os.cpu_count()
|
||||
if n_cpus is None:
|
||||
self.max_processes = 10
|
||||
else:
|
||||
self.max_processes = round(1.5 * n_cpus)
|
||||
|
||||
async def _compile(self, src: str, directory: Path) -> bytes:
|
||||
tmp_path = f"{directory}/tmp_{random.randint(1, 1000000000)}.typ"
|
||||
@@ -50,8 +54,12 @@ class TypstCompiler:
|
||||
|
||||
async def _compile_flashcard(self, card: Flashcard):
|
||||
preamble = default_preamble if card.preamble is None else card.preamble
|
||||
front = await self._compile(preamble + "\n" + card.as_typst(True), card.file_handler.directory_path)
|
||||
back = await self._compile(preamble + "\n" + card.as_typst(False), card.file_handler.directory_path)
|
||||
front = await self._compile(
|
||||
preamble + "\n" + card.as_typst(True), card.file_handler.directory_path
|
||||
)
|
||||
back = await self._compile(
|
||||
preamble + "\n" + card.as_typst(False), card.file_handler.directory_path
|
||||
)
|
||||
card.set_svgs(front, back)
|
||||
|
||||
async def compile_flashcards(self, cards: List[Flashcard]):
|
||||
@@ -62,7 +70,9 @@ class TypstCompiler:
|
||||
async with semaphore:
|
||||
return await self._compile_flashcard(card)
|
||||
|
||||
results = await asyncio.gather(*(compile_coro(card) for card in cards), return_exceptions=True)
|
||||
results = await asyncio.gather(
|
||||
*(compile_coro(card) for card in cards), return_exceptions=True
|
||||
)
|
||||
for result in results:
|
||||
if isinstance(result, Exception):
|
||||
raise result
|
||||
|
||||
33
uv.lock
generated
33
uv.lock
generated
@@ -340,6 +340,31 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.8.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/5d/4b5403f3e89837decfd54c51bea7f94b7d3fae77e08858603d0e04d7ad17/ruff-0.8.5.tar.gz", hash = "sha256:1098d36f69831f7ff2a1da3e6407d5fbd6dfa2559e4f74ff2d260c5588900317", size = 3454835 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/73/f8/03391745a703ce11678eb37c48ae89ec60396ea821e9d0bcea7c8e88fd91/ruff-0.8.5-py3-none-linux_armv6l.whl", hash = "sha256:5ad11a5e3868a73ca1fa4727fe7e33735ea78b416313f4368c504dbeb69c0f88", size = 10626889 },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/74/83bb74a44183b904216f3edfb9995b89830c83aaa6ce84627f74da0e0cf8/ruff-0.8.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f69ab37771ea7e0715fead8624ec42996d101269a96e31f4d31be6fc33aa19b7", size = 10398233 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/7a/a162a4feb3ef85d594527165e366dde09d7a1e534186ff4ba5d127eda850/ruff-0.8.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b5462d7804558ccff9c08fe8cbf6c14b7efe67404316696a2dde48297b1925bb", size = 10001843 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/9f/5ee5dcd135411402e35b6ec6a8dfdadbd31c5cd1c36a624d356a38d76090/ruff-0.8.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d56de7220a35607f9fe59f8a6d018e14504f7b71d784d980835e20fc0611cd50", size = 10872507 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/67/db2df2dd4a34b602d7f6ebb1b3744c8157f0d3579973ffc58309c9c272e8/ruff-0.8.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9d99cf80b0429cbebf31cbbf6f24f05a29706f0437c40413d950e67e2d4faca4", size = 10377200 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/ff/fe3a6a73006bced73e60d171d154a82430f61d97e787f511a24bd6302611/ruff-0.8.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b75ac29715ac60d554a049dbb0ef3b55259076181c3369d79466cb130eb5afd", size = 11433155 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/95/c1d1a1fe36658c1f3e1b47e1cd5f688b72d5786695b9e621c2c38399a95e/ruff-0.8.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c9d526a62c9eda211b38463528768fd0ada25dad524cb33c0e99fcff1c67b5dc", size = 12139227 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/fe/644b70d473a27b5112ac7a3428edcc1ce0db775c301ff11aa146f71886e0/ruff-0.8.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:587c5e95007612c26509f30acc506c874dab4c4abbacd0357400bd1aa799931b", size = 11697941 },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/39/4f83e517ec173e16a47c6d102cd22a1aaebe80e1208a1f2e83ab9a0e4134/ruff-0.8.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:622b82bf3429ff0e346835ec213aec0a04d9730480cbffbb6ad9372014e31bbd", size = 12967686 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/f6/52a2973ff108d74b5da706a573379eea160bece098f7cfa3f35dc4622710/ruff-0.8.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f99be814d77a5dac8a8957104bdd8c359e85c86b0ee0e38dca447cb1095f70fb", size = 11253788 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/1f/3b30f3c65b1303cb8e268ec3b046b77ab21ed8e26921cfc7e8232aa57f2c/ruff-0.8.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01c048f9c3385e0fd7822ad0fd519afb282af9cf1778f3580e540629df89725", size = 10860360 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/a8/2a3ea6bacead963f7aeeba0c61815d9b27b0d638e6a74984aa5cc5d27733/ruff-0.8.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7512e8cb038db7f5db6aae0e24735ff9ea03bb0ed6ae2ce534e9baa23c1dc9ea", size = 10457922 },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/47/8f9514b670969aab57c5fc826fb500a16aee8feac1bcf8a91358f153a5ba/ruff-0.8.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:762f113232acd5b768d6b875d16aad6b00082add40ec91c927f0673a8ec4ede8", size = 10958347 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/d6/78a9af8209ad99541816d74f01ce678fc01ebb3f37dd7ab8966646dcd92b/ruff-0.8.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:03a90200c5dfff49e4c967b405f27fdfa81594cbb7c5ff5609e42d7fe9680da5", size = 11328882 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/77/5c8072ec7afdfdf42c7a4019044486a2b6c85ee73617f8875ec94b977fed/ruff-0.8.5-py3-none-win32.whl", hash = "sha256:8710ffd57bdaa6690cbf6ecff19884b8629ec2a2a2a2f783aa94b1cc795139ed", size = 8802515 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/b6/47d2b06784de8ae992c45cceb2a30f3f205b3236a629d7ca4c0c134839a2/ruff-0.8.5-py3-none-win_amd64.whl", hash = "sha256:4020d8bf8d3a32325c77af452a9976a9ad6455773bcb94991cf15bd66b347e47", size = 9684231 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/5e/ffee22bf9f9e4b2669d1f0179ae8804584939fb6502b51f2401e26b1e028/ruff-0.8.5-py3-none-win_arm64.whl", hash = "sha256:134ae019ef13e1b060ab7136e7828a6d83ea727ba123381307eb37c6bd5e01cb", size = 9124741 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
version = "1.5.4"
|
||||
@@ -521,6 +546,11 @@ dependencies = [
|
||||
{ name = "typer" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiohttp", specifier = ">=3.11.11" },
|
||||
@@ -529,6 +559,9 @@ requires-dist = [
|
||||
{ name = "typer", specifier = ">=0.15.1" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [{ name = "ruff", specifier = ">=0.8.5" }]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.18.3"
|
||||
|
||||
Reference in New Issue
Block a user