Skip to content

Commit 5feae1b

Browse files
Improve questionary (#57)
- Improve questionary interactions - Add support for Py3.14 - Minor tweaks/fixes
1 parent 2597fa0 commit 5feae1b

18 files changed

Lines changed: 541 additions & 486 deletions

.github/workflows/publishing.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- uses: actions/checkout@v5
1414
- uses: astral-sh/setup-uv@v7
1515
- name: Setup Python
16-
run: uv python install 3.13
16+
run: uv python install 3.14
1717
- name: Build wheel and source tarball
1818
run: uv build
1919
- uses: actions/upload-artifact@v4
@@ -35,7 +35,7 @@ jobs:
3535
path: dist/
3636
- uses: astral-sh/setup-uv@v7
3737
- name: Setup Python
38-
run: uv python install 3.13
38+
run: uv python install 3.14
3939
- name: Validate dist
4040
run: uv run --with twine twine check dist/*
4141

.github/workflows/rich-codex.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- uses: actions/checkout@v5
1919
- uses: astral-sh/setup-uv@v7
2020
- name: Setup Python
21-
run: uv python install '3.13'
21+
run: uv python install 3.14
2222
- name: Install project
2323
run: uv sync --dev
2424

.github/workflows/testing.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
- '3.11'
2727
- '3.12'
2828
- '3.13'
29+
- '3.14'
2930
os:
3031
- ubuntu-latest
3132
- macos-latest

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.13.1
3+
rev: v0.13.2
44
hooks:
55
- id: ruff-format
66
- id: ruff-check

perdoo/__init__.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@
66
"get_state_root",
77
"setup_logging",
88
]
9-
__version__ = "0.6.0"
9+
__version__ = "0.7.0"
1010

1111
import logging
1212
import os
1313
from functools import cache
1414
from pathlib import Path
1515

1616
from rich.logging import RichHandler
17-
from rich.traceback import install
1817

1918
from perdoo.console import CONSOLE
2019

@@ -52,15 +51,14 @@ def get_state_root() -> Path:
5251

5352

5453
def setup_logging(debug: bool = False) -> None:
55-
install(show_locals=debug, max_frames=3, console=CONSOLE)
56-
5754
console_handler = RichHandler(
5855
rich_tracebacks=True,
59-
tracebacks_show_locals=True,
56+
tracebacks_show_locals=debug,
57+
tracebacks_max_frames=3,
6058
omit_repeated_times=False,
6159
show_level=True,
6260
show_time=False,
63-
show_path=True,
61+
show_path=debug,
6462
console=CONSOLE,
6563
)
6664
console_handler.setLevel(logging.DEBUG if debug else logging.INFO)

perdoo/__main__.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ def load(value: str) -> "SyncOption":
4444
return entry
4545
raise ValueError(f"'{value}' isn't a valid SyncOption")
4646

47-
def __str__(self) -> str:
48-
return self.value
49-
5047

5148
@app.callback(invoke_without_command=True)
5249
def common(
@@ -109,30 +106,30 @@ def _create_search_from_metron(metron_info: MetronInfo) -> Search:
109106
)
110107

111108

112-
def _create_search_from_comic_info(comic_info: ComicInfo) -> Search:
109+
def _create_search_from_comic_info(comic_info: ComicInfo, filename: str) -> Search:
113110
volume = comic_info.volume if comic_info.volume else None
114111
year = volume if volume and volume > 1900 else None
115112
volume = volume if volume and volume < 1900 else None
116113
return Search(
117-
series=SeriesSearch(name=comic_info.series, volume=volume, year=year),
114+
series=SeriesSearch(name=comic_info.series or filename, volume=volume, year=year),
118115
issue=IssueSearch(number=comic_info.number),
119116
)
120117

121118

122-
def _create_search_from_filename(fallback_title: str) -> Search:
123-
series_name = comicfn2dict(fallback_title).get("series", fallback_title).replace("-", " ")
119+
def _create_search_from_filename(filename: str) -> Search:
120+
series_name = comicfn2dict(filename).get("series", filename).replace("-", " ")
124121
return Search(series=SeriesSearch(name=series_name), issue=IssueSearch())
125122

126123

127124
def get_search_details(
128-
metadata: tuple[MetronInfo | None, ComicInfo | None], fallback_title: str
125+
metadata: tuple[MetronInfo | None, ComicInfo | None], filename: str
129126
) -> Search:
130127
metron_info, comic_info = metadata
131128
if metron_info and metron_info.series and metron_info.series.name:
132-
return _create_search_from_metron(metron_info)
129+
return _create_search_from_metron(metron_info=metron_info)
133130
if comic_info and comic_info.series:
134-
return _create_search_from_comic_info(comic_info)
135-
return _create_search_from_filename(fallback_title)
131+
return _create_search_from_comic_info(comic_info=comic_info, filename=filename)
132+
return _create_search_from_filename(filename=filename)
136133

137134

138135
def load_page_info(entry: Comic, comic_info: ComicInfo) -> list[Page]:
@@ -181,7 +178,7 @@ def run(
181178
case_sensitive=False,
182179
help="Sync ComicInfo/MetronInfo with online services.",
183180
),
184-
] = SyncOption.OUTDATED.value,
181+
] = SyncOption.OUTDATED,
185182
skip_clean: Annotated[
186183
bool,
187184
Option(
@@ -251,7 +248,8 @@ def run(
251248
metadata: tuple[MetronInfo | None, ComicInfo | None] = (entry.metron_info, entry.comic_info)
252249

253250
if sync != SyncOption.SKIP:
254-
search = get_search_details(metadata=metadata, fallback_title=entry.path.stem)
251+
search = get_search_details(metadata=metadata, filename=entry.path.stem)
252+
search.filename = entry.path.stem
255253
last_modified = date(1900, 1, 1)
256254
if sync == SyncOption.OUTDATED:
257255
metron_info, _ = metadata

perdoo/comic.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ComicMetadataError,
2121
MetadataFormat,
2222
)
23+
from natsort import humansorted, ns
2324

2425
from perdoo.metadata import ComicInfo, MetronInfo
2526
from perdoo.settings import Naming
@@ -101,17 +102,17 @@ def read_metadata(self, metadata_format: MetadataFormat) -> None:
101102
raise ComicMetadataError(f"Unsupported metadata format: {metadata_format}")
102103

103104
def convert(self, extension: Literal["cbt", "cbz"]) -> None:
104-
check, archiver = {
105-
"cbt": (self.is_cbt, TarArchiver),
106-
"cbz": (self.is_cbz, ZipArchiver),
107-
}.get(extension)
105+
check, archiver = {"cbt": (self.is_cbt, TarArchiver), "cbz": (self.is_cbz, ZipArchiver)}[
106+
extension
107+
]
108108
if check():
109109
return
110110
output_file = self.path.with_suffix(f".{extension}")
111111
with self.archive as source, archiver(path=output_file) as destination:
112112
LOGGER.debug("Converting '%s' to '%s'", source.path.name, destination.path.name)
113113
if destination.copy_from_archive(other_archive=source):
114114
self._archiver = destination
115+
source.path.unlink()
115116

116117
def clean_archive(self) -> None:
117118
with self.archive as source:
@@ -148,11 +149,14 @@ def _get_filepath_from_metadata(self, naming: Naming) -> str | None:
148149

149150
def _rename_images(self, base_name: str) -> None:
150151
with self.archive as source:
151-
files = [
152-
x
153-
for x in source.get_filename_list()
154-
if Path(x).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
155-
]
152+
files = humansorted(
153+
[
154+
x
155+
for x in source.get_filename_list()
156+
if Path(x).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
157+
],
158+
alg=ns.NA | ns.G | ns.P,
159+
)
156160
pad_count = len(str(len(files))) if files else 1
157161
for idx, filename in enumerate(files):
158162
img_file = Path(filename)
@@ -171,7 +175,7 @@ def rename(self, naming: Naming, output_folder: Path) -> None:
171175
new_filepath = new_filepath.lstrip("/")
172176

173177
output = output_folder / f"{new_filepath}.cbz"
174-
if output == self.path:
178+
if output.relative_to(output_folder) == self.path.resolve().relative_to(output_folder):
175179
return
176180
if output.exists():
177181
LOGGER.warning("'%s' already exists, skipping", output.relative_to(output_folder))

perdoo/metadata/comic_info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,12 +343,12 @@ def get_filename(self, settings: Naming) -> str:
343343
"series-id": lambda _: None,
344344
"series-name": lambda x: x.series,
345345
"series-sort-name": lambda _: None,
346-
"series-year": lambda x: x.volume if x.volume > 1900 else None,
346+
"series-year": lambda x: x.volume if x.volume and x.volume > 1900 else None,
347347
"store-date": lambda _: None,
348348
"store-day": lambda _: None,
349349
"store-month": lambda _: None,
350350
"store-year": lambda _: None,
351351
"title": lambda x: x.title,
352352
"upc": lambda _: None,
353-
"volume": lambda x: x.volume if x.volume < 1900 else None,
353+
"volume": lambda x: x.volume if x.volume and x.volume < 1900 else None,
354354
}

perdoo/services/_base.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
__all__ = ["BaseService"]
22

3-
from abc import abstractmethod
3+
from abc import ABC, abstractmethod
44
from typing import Generic, TypeVar
55

66
from perdoo.metadata import ComicInfo, MetronInfo
@@ -10,20 +10,20 @@
1010
C = TypeVar("C")
1111

1212

13-
class BaseService(Generic[S, C]):
13+
class BaseService(ABC, Generic[S, C]):
1414
@abstractmethod
1515
def _search_series(
16-
self, name: str | None, volume: int | None, year: int | None
16+
self, name: str | None, volume: int | None, year: int | None, filename: str
1717
) -> int | None: ...
1818

1919
@abstractmethod
20-
def fetch_series(self, search: SeriesSearch) -> S | None: ...
20+
def fetch_series(self, search: SeriesSearch, filename: str) -> S | None: ...
2121

2222
@abstractmethod
23-
def _search_issue(self, series_id: int, number: str | None) -> int | None: ...
23+
def _search_issue(self, series_id: int, number: str | None, filename: str) -> int | None: ...
2424

2525
@abstractmethod
26-
def fetch_issue(self, series_id: int, search: IssueSearch) -> C | None: ...
26+
def fetch_issue(self, series_id: int, search: IssueSearch, filename: str) -> C | None: ...
2727

2828
@abstractmethod
2929
def _process_metron_info(self, series: S, issue: C) -> MetronInfo | None: ...

0 commit comments

Comments
 (0)