Skip to content

Commit

Permalink
Merge pull request #78
Browse files Browse the repository at this point in the history
* Drop python-3.9 support
  • Loading branch information
bpepple authored Feb 11, 2024
1 parent 487ee7f commit 9acdff9
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
os:
- ubuntu-latest
- macos-latest
Expand Down
3 changes: 0 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
.. image:: https://img.shields.io/pypi/pyversions/darkseid.svg
:target: https://pypi.org/project/darkseid/

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black

.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Ruff
Expand Down
19 changes: 9 additions & 10 deletions darkseid/comic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import os
import zipfile
from pathlib import Path
from typing import Optional

import rarfile
from natsort import natsorted, ns
Expand Down Expand Up @@ -36,10 +35,10 @@ def __init__(self: "Comic", path: str) -> None:
self.path = Path(path)

self.ci_xml_filename = "ComicInfo.xml"
self.has_md: Optional[bool] = None
self.page_count: Optional[int] = None
self.page_list: Optional[list[str]] = None
self.metadata: Optional[Metadata] = None
self.has_md: bool | None = None
self.page_count: int | None = None
self.page_list: list[str] | None = None
self.metadata: Metadata | None = None

if self.zip_test():
self.archive_type: int = self.ArchiveType.zip
Expand Down Expand Up @@ -90,7 +89,7 @@ def seems_to_be_a_comic_archive(self: "Comic") -> bool:
(self.is_zip() or self.is_rar()) and (self.get_number_of_pages() > 0),
)

def get_page(self: "Comic", index: int) -> Optional[bytes]:
def get_page(self: "Comic", index: int) -> bytes | None:
"""Returns an image(page) from an archive."""
image_data = None

Expand All @@ -104,7 +103,7 @@ def get_page(self: "Comic", index: int) -> Optional[bytes]:

return image_data

def get_page_name(self: "Comic", index: int) -> Optional[str]:
def get_page_name(self: "Comic", index: int) -> str | None:
"""Returns the page name from an index."""
if index is None:
return None
Expand Down Expand Up @@ -165,7 +164,7 @@ def read_metadata(self: "Comic") -> Metadata:

return self.metadata

def read_raw_metadata(self: "Comic") -> Optional[str]:
def read_raw_metadata(self: "Comic") -> str | None:
if not self.has_metadata():
return None
try:
Expand All @@ -177,7 +176,7 @@ def read_raw_metadata(self: "Comic") -> Optional[str]:
raw_metadata = None
return raw_metadata

def write_metadata(self: "Comic", metadata: Optional[Metadata]) -> bool:
def write_metadata(self: "Comic", metadata: Metadata | None) -> bool:
"""Write the metadata to the archive."""
if metadata is None or not self.is_writable():
return False
Expand Down Expand Up @@ -211,7 +210,7 @@ def _successful_write(
self: "Comic",
write_success: bool,
has_md: bool,
metadata: Optional[Metadata],
metadata: Metadata | None,
) -> bool:
if write_success:
self.has_md = has_md
Expand Down
24 changes: 12 additions & 12 deletions darkseid/comicinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import xml.etree.ElementTree as ET # noqa: N817
from datetime import date
from re import split
from typing import Any, ClassVar, Optional, Union, cast
from typing import Any, ClassVar, cast

from darkseid.issue_string import IssueString
from darkseid.metadata import Arc, Basic, Credit, ImageMetadata, Metadata, Role, Series
Expand Down Expand Up @@ -88,7 +88,7 @@ def metadata_from_string(self: "ComicInfo", string: str) -> Metadata:
def string_from_metadata(
self: "ComicInfo",
md: Metadata,
xml: Optional[any] = None,
xml: bytes = b"",
) -> str:
tree = self.convert_metadata_to_xml(md, xml)
return ET.tostring(tree.getroot(), encoding="utf-8", xml_declaration=True).decode()
Expand All @@ -113,27 +113,27 @@ def _get_root(xml: any) -> ET.Element:
@classmethod
def validate_age_rating(
cls: type["ComicInfo"],
val: Optional[str] = None,
) -> Optional[str]:
val: str | None = None,
) -> str | None:
if val is not None:
return "Unknown" if val not in cls.ci_age_ratings else val
return None

@classmethod
def validate_manga(cls: type["ComicInfo"], val: Optional[str] = None) -> Optional[str]:
def validate_manga(cls: type["ComicInfo"], val: str | None = None) -> str | None:
if val is not None:
return "Unknown" if val not in cls.ci_manga else val
return None

def convert_metadata_to_xml(
self: "ComicInfo",
md: Metadata,
xml: Optional[any] = None,
xml: bytes = b"",
) -> ET.ElementTree:
root = self._get_root(xml)

# helper func
def assign(cix_entry: str, md_entry: Optional[Union[str, int]]) -> None:
def assign(cix_entry: str, md_entry: str | int | None) -> None:
if md_entry is not None and md_entry:
et_entry = root.find(cix_entry)
if et_entry is not None:
Expand All @@ -145,7 +145,7 @@ def assign(cix_entry: str, md_entry: Optional[Union[str, int]]) -> None:
if et_entry is not None:
root.remove(et_entry)

def get_resource_list(resource: list[Basic]) -> Optional[str]:
def get_resource_list(resource: list[Basic]) -> str | None:
return list_to_string([i.name for i in resource]) if resource else None

assign("Title", get_resource_list(md.stories))
Expand Down Expand Up @@ -252,17 +252,17 @@ def convert_xml_to_metadata(self: "ComicInfo", tree: ET.ElementTree) -> Metadata
if root.tag != "ComicInfo":
raise ValueError("Metadata is not ComicInfo format")

def get(txt: str) -> Optional[Union[str, int]]:
def get(txt: str) -> str | int | None:
tag = root.find(txt)
return None if tag is None else tag.text

def string_to_resource(string: str) -> Optional[list[Basic]]:
def string_to_resource(string: str) -> list[Basic] | None:
if string is not None:
# TODO: Make the delimiter also check for ','
return [Basic(x.strip()) for x in string.split(";")]
return None

def string_to_arc(string: str) -> Optional[list[Arc]]:
def string_to_arc(string: str) -> list[Arc] | None:
if string is not None:
return [Arc(x.strip()) for x in string.split(";")]
return None
Expand Down Expand Up @@ -338,7 +338,7 @@ def write_to_external_file(
self: "ComicInfo",
filename: str,
md: Metadata,
xml: Optional[any] = None,
xml: bytes = b"",
) -> None:
tree = self.convert_metadata_to_xml(md, xml)
tree.write(filename, encoding="utf-8", xml_declaration=True)
Expand Down
10 changes: 4 additions & 6 deletions darkseid/issue_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

# Copyright 2012-2014 Anthony Beville

from typing import Optional


class IssueString:
"""Class to handle various types of comic issue numbers.
Expand All @@ -21,13 +19,13 @@ def __init__(self: "IssueString", text: str) -> None:
# break up the issue number string into 2 parts: the numeric and suffix string.
# (assumes that the numeric portion is always first)

self.num: Optional[float] = None
self.num: float | None = None
self.suffix: str = ""

if text is None:
return

if isinstance(text, (int, float)):
if isinstance(text, int | float):
text = str(text)

if not text:
Expand Down Expand Up @@ -114,7 +112,7 @@ def as_string(self: "IssueString", pad: int = 0) -> str:

return num_s

def as_float(self: "IssueString") -> Optional[float]:
def as_float(self: "IssueString") -> float | None:
"""Return a float with no suffix.
example: "1½" is returned as "1.5"
Expand All @@ -126,7 +124,7 @@ def as_float(self: "IssueString") -> Optional[float]:
return self.num + 0.5 if self.num is not None else 0.5
return self.num

def as_int(self: "IssueString") -> Optional[int]:
def as_int(self: "IssueString") -> int | None:
"""Returns the integer version of the float.
:returns: String as an integer.
Expand Down
84 changes: 42 additions & 42 deletions darkseid/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from dataclasses import dataclass, field, fields
from datetime import date
from decimal import Decimal
from typing import Optional, TypedDict
from typing import TypedDict

import pycountry

Expand Down Expand Up @@ -132,7 +132,7 @@ def validate_country(value: str, **_: any) -> str:
@dataclass
class Basic:
name: str
id_: Optional[int] = None
id_: int | None = None


@dataclass
Expand All @@ -142,13 +142,13 @@ class Role(Basic):

@dataclass
class Series(Basic, Validations):
sort_name: Optional[str] = None
volume: Optional[int] = None
format: Optional[str] = None
language: Optional[str] = None # 2-letter iso code
sort_name: str | None = None
volume: int | None = None
format: str | None = None
language: str | None = None # 2-letter iso code

@staticmethod
def validate_language(value: str, **_: any) -> Optional[str]:
def validate_language(value: str, **_: any) -> str | None:
"""
Validates a language value.
Expand Down Expand Up @@ -191,23 +191,23 @@ def validate_language(value: str, **_: any) -> Optional[str]:

@dataclass
class Arc(Basic):
number: Optional[int] = None
number: int | None = None


@dataclass
class Credit:
person: str
role: list[Role]
id_: Optional[int] = None
id_: int | None = None


@dataclass
class GTIN(Validations):
upc: Optional[int] = None
isbn: Optional[int] = None
upc: int | None = None
isbn: int | None = None

@staticmethod
def validate_upc(value: int, **_: any) -> Optional[int]:
def validate_upc(value: int, **_: any) -> int | None:
"""
Validates a UPC (Universal Product Code) value.
Expand Down Expand Up @@ -245,7 +245,7 @@ def validate_upc(value: int, **_: any) -> Optional[int]:
return value

@staticmethod
def validate_isbn(value: int, **_: any) -> Optional[int]:
def validate_isbn(value: int, **_: any) -> int | None:
"""
Validates an ISBN (International Standard Book Number) value.
Expand Down Expand Up @@ -348,40 +348,40 @@ class Metadata:
"""

is_empty: bool = True
tag_origin: Optional[str] = None
tag_origin: str | None = None

info_source: Optional[Basic] = None
series: Optional[Series] = None
issue: Optional[str] = None
collection_title: Optional[str] = None
info_source: Basic | None = None
series: Series | None = None
issue: str | None = None
collection_title: str | None = None
stories: list[Basic] = field(default_factory=list)
publisher: Optional[Basic] = None
cover_date: Optional[date] = None
store_date: Optional[date] = None
publisher: Basic | None = None
cover_date: date | None = None
store_date: date | None = None
prices: list[Price] = field(default_factory=list)
gtin: Optional[GTIN] = None
issue_count: Optional[int] = None
gtin: GTIN | None = None
issue_count: int | None = None
genres: list[Basic] = field(default_factory=list)
comments: Optional[str] = None # use same way as Summary in CIX

volume_count: Optional[str] = None
critical_rating: Optional[str] = None
country: Optional[str] = None

alternate_series: Optional[str] = None
alternate_number: Optional[str] = None
alternate_count: Optional[int] = None
imprint: Optional[str] = None
notes: Optional[str] = None
web_link: Optional[str] = None
manga: Optional[str] = None
black_and_white: Optional[bool] = None
page_count: Optional[int] = None
age_rating: Optional[str] = None
comments: str | None = None # use same way as Summary in CIX

volume_count: str | None = None
critical_rating: str | None = None
country: str | None = None

alternate_series: str | None = None
alternate_number: str | None = None
alternate_count: int | None = None
imprint: str | None = None
notes: str | None = None
web_link: str | None = None
manga: str | None = None
black_and_white: bool | None = None
page_count: int | None = None
age_rating: str | None = None

story_arcs: list[Arc] = field(default_factory=list)
series_group: Optional[str] = None
scan_info: Optional[str] = None
series_group: str | None = None
scan_info: str | None = None

characters: list[Basic] = field(default_factory=list)
teams: list[Basic] = field(default_factory=list)
Expand Down Expand Up @@ -634,7 +634,7 @@ def get_cover_page_index_list(self: "Metadata") -> list[int]:

return coverlist

def _existing_credit(self: "Metadata", creator: str) -> tuple[bool, Optional[int]]:
def _existing_credit(self: "Metadata", creator: str) -> tuple[bool, int | None]:
return (
next(
(
Expand Down
7 changes: 4 additions & 3 deletions darkseid/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import itertools
from collections import defaultdict
from pathlib import Path
from typing import Optional, Union # Union type can be removed when Python3.9 support ends


def get_recursive_filelist(pathlist: list[Path]) -> list[Path]:
Expand Down Expand Up @@ -88,12 +87,14 @@ def unique_file(file_name: Path) -> Path:
return file_name


def xlate(data: Optional[Union[int, str]], is_int: bool = False) -> Optional[Union[int, str]]:
def xlate(data: int | str | None, is_int: bool = False) -> int | str | None:
if data is None or data == "":
return None
if is_int:
i = str(data).translate(
defaultdict(lambda: None, zip((ord(c) for c in "1234567890"), "1234567890")),
defaultdict(
lambda: None, zip((ord(c) for c in "1234567890"), "1234567890", strict=False)
),
)
if i == "0":
return "0"
Expand Down
Loading

0 comments on commit 9acdff9

Please sign in to comment.