From 7863eb1a6fa8b5f443282126e0536fe54f747d0b Mon Sep 17 00:00:00 2001 From: Buried-In-Code <6057651+Buried-In-Code@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:14:25 +1300 Subject: [PATCH 1/2] Add support for Py3.14 Drop support for Py3.9 --- .editorconfig | 30 --------------- .github/dependabot.yaml | 2 +- .github/workflows/publishing.yaml | 41 ++++++++++++++------ .github/workflows/testing.yaml | 13 ++++++- .pre-commit-config.yaml | 20 ++++++++-- .readthedocs.yaml | 2 +- pyproject.toml | 36 ++++++++--------- simyan/comicvine.py | 64 +++++++++++++++---------------- simyan/schemas/character.py | 18 ++++----- simyan/schemas/concept.py | 11 +++--- simyan/schemas/creator.py | 22 +++++------ simyan/schemas/generic_entries.py | 13 +++---- simyan/schemas/issue.py | 17 ++++---- simyan/schemas/item.py | 11 +++--- simyan/schemas/location.py | 13 +++---- simyan/schemas/origin.py | 3 +- simyan/schemas/power.py | 5 +-- simyan/schemas/publisher.py | 13 +++---- simyan/schemas/story_arc.py | 11 +++--- simyan/schemas/team.py | 11 +++--- simyan/schemas/volume.py | 17 ++++---- simyan/sqlite_cache.py | 4 +- 22 files changed, 188 insertions(+), 189 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 8a6e209..0000000 --- a/.editorconfig +++ /dev/null @@ -1,30 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -max_line_length = 100 - -[*.bat] -indent_style = tab - -[*.json] -insert_final_newline = false - -[*.md] -trim_trailing_whitespace = false - -[*.py] -indent_size = 4 - -[{*.xml,*.xsd}] -indent_size = 4 -indent_style = tab -insert_final_newline = false - -[{*.yaml,*.yml}] -indent_style = tab diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 422c06f..b507b06 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -9,7 +9,7 @@ updates: patterns: - "*" - - package-ecosystem: pip + - package-ecosystem: uv directory: / schedule: interval: daily diff --git a/.github/workflows/publishing.yaml b/.github/workflows/publishing.yaml index ec1c810..fdb6428 100644 --- a/.github/workflows/publishing.yaml +++ b/.github/workflows/publishing.yaml @@ -5,33 +5,52 @@ on: push jobs: build: name: Build + permissions: + contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: actions/setup-python@v6 - with: - python-version: '3.12' - - name: Install pypa/build - run: pip install build - - name: Build a binary wheel and a source tarball - run: python -m build + - uses: astral-sh/setup-uv@v6 + - name: Setup Python + run: uv python install 3.14 + - name: Build wheel and source tarball + run: uv build - uses: actions/upload-artifact@v4 with: name: python-package-distributions path: dist/ - publish-to-pypi: - name: Publish to PyPI - if: startsWith(github.ref, 'refs/tags/') + validate: + name: Validate dist needs: - build + permissions: {} runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@v5 + with: + name: python-package-distributions + path: dist/ + - uses: astral-sh/setup-uv@v6 + - name: Setup Python + run: uv python install 3.14 + - name: Validate dist + run: uv run --with twine twine check dist/* + + publish: environment: name: pypi - url: https://pypi.org/p/simyan + url: https://pypi.org/p/Simyan + if: startsWith(github.ref, 'refs/tags/') + name: Publish to PyPI + needs: + - build + - validate permissions: id-token: write + runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v5 diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index 31e802e..ec628b2 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -22,16 +22,20 @@ jobs: fail-fast: false matrix: python-version: - - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' + - '3.14' os: - ubuntu-latest - macos-latest - windows-latest + + permissions: + contents: read runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v5 - uses: astral-sh/setup-uv@v6 @@ -43,10 +47,15 @@ jobs: env: COMICVINE__API_KEY: IGNORED run: uv run pytest + collector: - needs: [pytest] if: always() + needs: + - pytest + + permissions: {} runs-on: ubuntu-latest + steps: - name: Check for failures if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf1b266..972428c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.2 + rev: v0.14.0 hooks: - id: ruff-format - id: ruff-check @@ -16,19 +16,27 @@ repos: - --wrap=keep exclude: ".github\/ISSUE_TEMPLATE\/.*.md" - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: + - id: check-added-large-files - id: check-ast - id: check-builtin-literals - id: check-case-conflict - id: check-docstring-first +# - id: check-executables-have-shebangs + - id: check-illegal-windows-names +# - id: check-json - id: check-merge-conflict args: - --assume-in-merge + - id: check-shebang-scripts-are-executable - id: check-toml + - id: check-vcs-permalinks +# - id: check-xml - id: check-yaml args: - --allow-multiple-documents + - id: debug-statements - id: end-of-file-fixer exclude_types: - json @@ -39,11 +47,17 @@ repos: args: - --fix=auto - id: name-tests-test +# - id: pretty-format-json +# args: +# - --autofix +# - --indent=2 - id: trailing-whitespace args: - --markdown-linebreak-ext=md + exclude_types: + - svg - repo: https://github.com/pappasam/toml-sort - rev: v0.24.2 + rev: v0.24.3 hooks: - id: toml-sort args: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 02a4b66..79842bf 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,7 +6,7 @@ version: 2 # Set the version of Python and other tools you might need build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: python: "3.13" jobs: diff --git a/pyproject.toml b/pyproject.toml index bc2075a..0568550 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,19 +4,19 @@ requires = ["hatchling"] [dependency-groups] dev = [ - "pre-commit >= 4.2.0" + "pre-commit >= 4.3.0" ] docs = [ - "mkdocs >= 1.6.1", - "mkdocs-include-markdown-plugin >= 7.1.2", + "mkdocs >= 1.6.0", + "mkdocs-include-markdown-plugin >= 7.2.0", "mkdocs-material >= 9.6.0", - "mkdocstrings[python] >= 0.29.0" + "mkdocstrings[python] >= 0.30.0" ] tests = [ "pytest >= 8.4.0", - "pytest-cov >= 6.2.0", - "tox >= 4.27.0", - "tox-uv >= 1.26.0" + "pytest-cov >= 7.0.0", + "tox >= 4.30.0", + "tox-uv >= 1.28.0" ] [project] @@ -26,37 +26,35 @@ authors = [ classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", + "Framework :: Pydantic :: 2", "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Natural Language :: English", - "Operating System :: MacOS", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", + "Operating System :: OS Independent", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.14", "Programming Language :: Python", "Topic :: Internet", "Typing :: Typed" ] dependencies = [ - "eval-type-backport >= 0.2.0; python_version < '3.10'", - "pydantic >= 2.11.0", - "pyrate-limiter >= 3.7.1", - "requests >= 2.32.3" + "pydantic >= 2.12.0", + "pyrate-limiter >= 3.9.0", + "requests >= 2.32.0" ] description = "A Python wrapper for the Comicvine API." dynamic = ["version"] keywords = ["comic", "comics", "metadata"] -license = {text = "GPL-3.0-or-later"} +license = "GPL-3.0-or-later" +license-files = ["LICENSE"] maintainers = [ {email = "BuriedInCode@tuta.io", name = "BuriedInCode"} ] name = "Simyan" readme = "README.md" -requires-python = ">= 3.9" +requires-python = ">= 3.10" [project.urls] Documentation = "https://simyan.readthedocs.io/en/latest/" @@ -137,7 +135,7 @@ overrides."tool.tox.env_list".inline_arrays = false overrides."tool.tox.env_run_base.commands".inline_arrays = false [tool.tox] -env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"] +env_list = ["3.10", "3.11", "3.12", "3.13", "3.14"] min_version = "4.22" [tool.tox.env_run_base] diff --git a/simyan/comicvine.py b/simyan/comicvine.py index a156953..e3d7a86 100644 --- a/simyan/comicvine.py +++ b/simyan/comicvine.py @@ -10,7 +10,7 @@ import platform import re from enum import Enum -from typing import Any, ClassVar, Final, Optional, TypeVar, Union +from typing import Any, ClassVar, Final, TypeVar from urllib.parse import urlencode, urlparse from pydantic import TypeAdapter, ValidationError @@ -121,7 +121,7 @@ class Comicvine: _limiter = Limiter(_bucket, raise_when_fail=False, max_delay=Duration.DAY) decorator = _limiter.as_decorator() - def __init__(self, api_key: str, timeout: int = 30, cache: Optional[SQLiteCache] = None): + def __init__(self, api_key: str, timeout: int = 30, cache: SQLiteCache | None = None): self.headers = { "Accept": "application/json", "User-Agent": f"Simyan/{__version__}/{platform.system()}: {platform.release()}", @@ -132,7 +132,7 @@ def __init__(self, api_key: str, timeout: int = 30, cache: Optional[SQLiteCache] @decorator(rate_mapping) def _perform_get_request( - self, url: str, params: Optional[dict[str, str]] = None + self, url: str, params: dict[str, str] | None = None ) -> dict[str, Any]: """Make GET request to Comicvine API endpoint. @@ -173,7 +173,7 @@ def _perform_get_request( raise ServiceError("Service took too long to respond") from err def _get_request( - self, endpoint: str, params: Optional[dict[str, str]] = None, skip_cache: bool = False + self, endpoint: str, params: dict[str, str] | None = None, skip_cache: bool = False ) -> dict[str, Any]: """Check cache or make GET request to Comicvine API endpoint. @@ -214,7 +214,7 @@ def _get_request( return response def _get_offset_request( - self, endpoint: str, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, endpoint: str, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[dict[str, Any]]: """Get results from offset requests. @@ -242,7 +242,7 @@ def _get_offset_request( return results[:max_results] def _get_paged_request( - self, endpoint: str, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, endpoint: str, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[dict[str, Any]]: """Get results from paged requests. @@ -271,7 +271,7 @@ def _get_paged_request( return results[:max_results] def list_publishers( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicPublisher]: """Request a list of Publishers. @@ -314,7 +314,7 @@ def get_publisher(self, publisher_id: int) -> Publisher: raise ServiceError(err) from err def list_volumes( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicVolume]: """Request a list of Volumes. @@ -357,7 +357,7 @@ def get_volume(self, volume_id: int) -> Volume: raise ServiceError(err) from err def list_issues( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicIssue]: """Request a list of Issues. @@ -400,7 +400,7 @@ def get_issue(self, issue_id: int) -> Issue: raise ServiceError(err) from err def list_story_arcs( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicStoryArc]: """Request a list of Story Arcs. @@ -443,7 +443,7 @@ def get_story_arc(self, story_arc_id: int) -> StoryArc: raise ServiceError(err) from err def list_creators( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicCreator]: """Request a list of Creators. @@ -486,7 +486,7 @@ def get_creator(self, creator_id: int) -> Creator: raise ServiceError(err) from err def list_characters( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicCharacter]: """Request a list of Characters. @@ -529,7 +529,7 @@ def get_character(self, character_id: int) -> Character: raise ServiceError(err) from err def list_teams( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicTeam]: """Request a list of Teams. @@ -572,7 +572,7 @@ def get_team(self, team_id: int) -> Team: raise ServiceError(err) from err def list_locations( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicLocation]: """Request a list of Locations. @@ -615,7 +615,7 @@ def get_location(self, location_id: int) -> Location: raise ServiceError(err) from err def list_concepts( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicConcept]: """Request a list of Concepts. @@ -658,7 +658,7 @@ def get_concept(self, concept_id: int) -> Concept: raise ServiceError(err) from err def list_powers( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicPower]: """Request a list of Powers. @@ -701,7 +701,7 @@ def get_power(self, power_id: int) -> Power: raise ServiceError(err) from err def list_origins( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicOrigin]: """Request a list of Origins. @@ -744,7 +744,7 @@ def get_origin(self, origin_id: int) -> Origin: raise ServiceError(err) from err def list_items( - self, params: Optional[dict[str, Any]] = None, max_results: int = 500 + self, params: dict[str, Any] | None = None, max_results: int = 500 ) -> list[BasicItem]: """Request a list of Items. @@ -788,20 +788,20 @@ def get_item(self, item_id: int) -> Item: def search( self, resource: ComicvineResource, query: str, max_results: int = 500 - ) -> Union[ - list[BasicPublisher], - list[BasicVolume], - list[BasicIssue], - list[BasicStoryArc], - list[BasicCreator], - list[BasicCharacter], - list[BasicTeam], - list[BasicLocation], - list[BasicConcept], - list[BasicPower], - list[BasicOrigin], - list[BasicItem], - ]: + ) -> ( + list[BasicPublisher] + | list[BasicVolume] + | list[BasicIssue] + | list[BasicStoryArc] + | list[BasicCreator] + | list[BasicCharacter] + | list[BasicTeam] + | list[BasicLocation] + | list[BasicConcept] + | list[BasicPower] + | list[BasicOrigin] + | list[BasicItem] + ): """Request a list of search results filtered by provided resource. Args: diff --git a/simyan/schemas/character.py b/simyan/schemas/character.py index d63aa31..9d4f9db 100644 --- a/simyan/schemas/character.py +++ b/simyan/schemas/character.py @@ -8,7 +8,7 @@ __all__ = ["BasicCharacter", "Character"] from datetime import date, datetime -from typing import Any, Optional +from typing import Any from pydantic import Field, HttpUrl @@ -39,23 +39,23 @@ class BasicCharacter(BaseModel): summary: Short description of the Character. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - date_of_birth: Optional[date] = Field(alias="birth", default=None) - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + date_of_birth: date | None = Field(alias="birth", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) gender: int id: int image: Images issue_count: int = Field(alias="count_of_issue_appearances") name: str - origin: Optional[GenericEntry] = None - publisher: Optional[GenericEntry] = None - real_name: Optional[str] = None + origin: GenericEntry | None = None + publisher: GenericEntry | None = None + real_name: str | None = None site_url: HttpUrl = Field(alias="site_detail_url") - summary: Optional[str] = Field(alias="deck", default=None) + summary: str | None = Field(alias="deck", default=None) def __init__(self, **data: Any): if data.get("birth"): diff --git a/simyan/schemas/concept.py b/simyan/schemas/concept.py index 92ad24c..d7c6f18 100644 --- a/simyan/schemas/concept.py +++ b/simyan/schemas/concept.py @@ -8,7 +8,6 @@ __all__ = ["BasicConcept", "Concept"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -35,19 +34,19 @@ class BasicConcept(BaseModel): summary: Short description of the Concept. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) id: int image: Images issue_count: int = Field(alias="count_of_isssue_appearances") name: str site_url: HttpUrl = Field(alias="site_detail_url") - start_year: Optional[int] = None - summary: Optional[str] = Field(alias="deck", default=None) + start_year: int | None = None + summary: str | None = Field(alias="deck", default=None) class Concept(BasicConcept): diff --git a/simyan/schemas/creator.py b/simyan/schemas/creator.py index a84da0c..a9fff3c 100644 --- a/simyan/schemas/creator.py +++ b/simyan/schemas/creator.py @@ -8,7 +8,7 @@ __all__ = ["BasicCreator", "Creator"] from datetime import date, datetime -from typing import Any, Optional +from typing import Any from pydantic import Field, HttpUrl @@ -40,24 +40,24 @@ class BasicCreator(BaseModel): website: Url to the Creator's website. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") - country: Optional[str] = None + country: str | None = None date_added: datetime date_last_updated: datetime - date_of_birth: Optional[date] = Field(alias="birth", default=None) - date_of_death: Optional[date] = Field(alias="death", default=None) - description: Optional[str] = None - email: Optional[str] = None + date_of_birth: date | None = Field(alias="birth", default=None) + date_of_death: date | None = Field(alias="death", default=None) + description: str | None = None + email: str | None = None gender: int - hometown: Optional[str] = None + hometown: str | None = None id: int image: Images - issue_count: Optional[int] = Field(alias="count_of_isssue_appearances", default=None) + issue_count: int | None = Field(alias="count_of_isssue_appearances", default=None) name: str site_url: HttpUrl = Field(alias="site_detail_url") - summary: Optional[str] = Field(alias="deck", default=None) - website: Optional[HttpUrl] = None + summary: str | None = Field(alias="deck", default=None) + website: HttpUrl | None = None def __init__(self, **data: Any): if data.get("death"): diff --git a/simyan/schemas/generic_entries.py b/simyan/schemas/generic_entries.py index 4593112..b6b219d 100644 --- a/simyan/schemas/generic_entries.py +++ b/simyan/schemas/generic_entries.py @@ -18,7 +18,6 @@ "Images", ] -from typing import Optional from pydantic import Field, HttpUrl @@ -37,8 +36,8 @@ class GenericEntry(BaseModel, extra="forbid"): api_url: HttpUrl = Field(alias="api_detail_url") id: int - name: Optional[str] = None - site_url: Optional[HttpUrl] = Field(default=None, alias="site_detail_url") + name: str | None = None + site_url: HttpUrl | None = Field(default=None, alias="site_detail_url") class GenericCount(GenericEntry): @@ -58,7 +57,7 @@ class GenericIssue(GenericEntry): number: """ - number: Optional[str] = Field(default=None, alias="issue_number") + number: str | None = Field(default=None, alias="issue_number") class GenericCreator(GenericEntry): @@ -96,7 +95,7 @@ class Images(BaseModel, extra="forbid"): super_url: HttpUrl thumbnail: HttpUrl = Field(alias="thumb_url") tiny_url: HttpUrl - tags: Optional[str] = Field(default=None, alias="image_tags") + tags: str | None = Field(default=None, alias="image_tags") class AssociatedImage(BaseModel, extra="forbid"): @@ -111,5 +110,5 @@ class AssociatedImage(BaseModel, extra="forbid"): url: HttpUrl = Field(alias="original_url") id: int - caption: Optional[str] = None - tags: Optional[str] = Field(default=None, alias="image_tags") + caption: str | None = None + tags: str | None = Field(default=None, alias="image_tags") diff --git a/simyan/schemas/issue.py b/simyan/schemas/issue.py index 2cc9a61..8817bce 100644 --- a/simyan/schemas/issue.py +++ b/simyan/schemas/issue.py @@ -8,7 +8,6 @@ __all__ = ["BasicIssue", "Issue"] from datetime import date, datetime -from typing import Optional, Union from pydantic import Field, HttpUrl, field_validator @@ -37,20 +36,20 @@ class BasicIssue(BaseModel): volume: The volume the Issue is in. """ - aliases: Optional[str] = None + aliases: str | None = None associated_images: list[AssociatedImage] = Field(default_factory=list) api_url: HttpUrl = Field(alias="api_detail_url") - cover_date: Optional[date] = None + cover_date: date | None = None date_added: datetime date_last_updated: datetime - description: Optional[str] = None + description: str | None = None id: int image: Images - name: Optional[str] = None - number: Optional[str] = Field(alias="issue_number", default=None) + name: str | None = None + number: str | None = Field(alias="issue_number", default=None) site_url: HttpUrl = Field(alias="site_detail_url") - store_date: Optional[date] = None - summary: Optional[str] = Field(alias="deck", default=None) + store_date: date | None = None + summary: str | None = Field(alias="deck", default=None) volume: GenericEntry @@ -102,7 +101,7 @@ class Issue(BasicIssue): "first_appearance_teams", mode="before", ) - def handle_blank_list(cls, value: Union[str, list, None]) -> list: + def handle_blank_list(cls, value: str | list | None) -> list: """Convert a blank or None value to an empty list. Args: diff --git a/simyan/schemas/item.py b/simyan/schemas/item.py index aa21015..c467fb5 100644 --- a/simyan/schemas/item.py +++ b/simyan/schemas/item.py @@ -8,7 +8,6 @@ __all__ = ["BasicItem", "Item"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -35,19 +34,19 @@ class BasicItem(BaseModel): summary: Short description of the Item. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) id: int image: Images issue_count: int = Field(alias="count_of_issue_appearances") name: str site_url: HttpUrl = Field(alias="site_detail_url") - start_year: Optional[int] = None - summary: Optional[str] = Field(alias="deck", default=None) + start_year: int | None = None + summary: str | None = Field(alias="deck", default=None) class Item(BasicItem): diff --git a/simyan/schemas/location.py b/simyan/schemas/location.py index 2978218..b08b67e 100644 --- a/simyan/schemas/location.py +++ b/simyan/schemas/location.py @@ -8,7 +8,6 @@ __all__ = ["BasicLocation", "Location"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -35,19 +34,19 @@ class BasicLocation(BaseModel): summary: Short description of the Location. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) id: int image: Images - issue_count: Optional[int] = Field(alias="count_of_issue_appearances", default=None) + issue_count: int | None = Field(alias="count_of_issue_appearances", default=None) name: str site_url: str = Field(alias="site_detail_url") - start_year: Optional[int] = None - summary: Optional[str] = Field(alias="deck", default=None) + start_year: int | None = None + summary: str | None = Field(alias="deck", default=None) class Location(BasicLocation): diff --git a/simyan/schemas/origin.py b/simyan/schemas/origin.py index ee75a18..ee5bd9a 100644 --- a/simyan/schemas/origin.py +++ b/simyan/schemas/origin.py @@ -7,7 +7,6 @@ __all__ = ["BasicOrigin", "Origin"] -from typing import Optional from pydantic import Field, HttpUrl @@ -40,6 +39,6 @@ class Origin(BasicOrigin): profiles: """ - character_set: Optional[int] = None + character_set: int | None = None characters: list[GenericEntry] = Field(default_factory=list) profiles: list[int] = Field(default_factory=list) diff --git a/simyan/schemas/power.py b/simyan/schemas/power.py index d4e0743..dd0173e 100644 --- a/simyan/schemas/power.py +++ b/simyan/schemas/power.py @@ -8,7 +8,6 @@ __all__ = ["BasicPower", "Power"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -30,11 +29,11 @@ class BasicPower(BaseModel): site_url: Url to the resource in Comicvine. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None + description: str | None = None id: int name: str site_url: HttpUrl = Field(alias="site_detail_url") diff --git a/simyan/schemas/publisher.py b/simyan/schemas/publisher.py index 17abcec..e1b4b70 100644 --- a/simyan/schemas/publisher.py +++ b/simyan/schemas/publisher.py @@ -8,7 +8,6 @@ __all__ = ["BasicPublisher", "Publisher"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -35,19 +34,19 @@ class BasicPublisher(BaseModel): summary: Short description of the Publisher. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None + description: str | None = None id: int image: Images - location_address: Optional[str] = None - location_city: Optional[str] = None - location_state: Optional[str] = None + location_address: str | None = None + location_city: str | None = None + location_state: str | None = None name: str site_url: HttpUrl = Field(alias="site_detail_url") - summary: Optional[str] = Field(alias="deck", default=None) + summary: str | None = Field(alias="deck", default=None) class Publisher(BasicPublisher): diff --git a/simyan/schemas/story_arc.py b/simyan/schemas/story_arc.py index 336d24c..08722bf 100644 --- a/simyan/schemas/story_arc.py +++ b/simyan/schemas/story_arc.py @@ -8,7 +8,6 @@ __all__ = ["BasicStoryArc", "StoryArc"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -35,19 +34,19 @@ class BasicStoryArc(BaseModel): summary: Short description of the Story Arc. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) id: int image: Images issue_count: int = Field(alias="count_of_isssue_appearances") name: str - publisher: Optional[GenericEntry] = None + publisher: GenericEntry | None = None site_url: HttpUrl = Field(alias="site_detail_url") - summary: Optional[str] = Field(alias="deck", default=None) + summary: str | None = Field(alias="deck", default=None) class StoryArc(BasicStoryArc): diff --git a/simyan/schemas/team.py b/simyan/schemas/team.py index bfbe7d2..0acaa86 100644 --- a/simyan/schemas/team.py +++ b/simyan/schemas/team.py @@ -8,7 +8,6 @@ __all__ = ["BasicTeam", "Team"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl @@ -36,20 +35,20 @@ class BasicTeam(BaseModel): summary: Short description of the Team. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = Field(alias="first_appeared_in_issue", default=None) + description: str | None = None + first_issue: GenericIssue | None = Field(alias="first_appeared_in_issue", default=None) id: int image: Images issue_count: int = Field(alias="count_of_isssue_appearances") member_count: int = Field(alias="count_of_team_members") name: str - publisher: Optional[GenericEntry] = None + publisher: GenericEntry | None = None site_url: HttpUrl = Field(alias="site_detail_url") - summary: Optional[str] = Field(alias="deck", default=None) + summary: str | None = Field(alias="deck", default=None) class Team(BasicTeam): diff --git a/simyan/schemas/volume.py b/simyan/schemas/volume.py index c4c2603..de6f348 100644 --- a/simyan/schemas/volume.py +++ b/simyan/schemas/volume.py @@ -8,7 +8,6 @@ __all__ = ["BasicVolume", "Volume"] from datetime import datetime -from typing import Optional from pydantic import Field, HttpUrl, field_validator @@ -37,24 +36,24 @@ class BasicVolume(BaseModel): summary: Short description of the Volume. """ - aliases: Optional[str] = None + aliases: str | None = None api_url: HttpUrl = Field(alias="api_detail_url") date_added: datetime date_last_updated: datetime - description: Optional[str] = None - first_issue: Optional[GenericIssue] = None + description: str | None = None + first_issue: GenericIssue | None = None id: int image: Images issue_count: int = Field(alias="count_of_issues") - last_issue: Optional[GenericIssue] = None + last_issue: GenericIssue | None = None name: str - publisher: Optional[GenericEntry] = None + publisher: GenericEntry | None = None site_url: HttpUrl = Field(alias="site_detail_url") - start_year: Optional[int] = None - summary: Optional[str] = Field(alias="deck", default=None) + start_year: int | None = None + summary: str | None = Field(alias="deck", default=None) @field_validator("start_year", mode="before") - def validate_start_year(cls, value: str) -> Optional[int]: + def validate_start_year(cls, value: str) -> int | None: """Convert start_year to int or None. Args: diff --git a/simyan/sqlite_cache.py b/simyan/sqlite_cache.py index ce87c93..4340788 100644 --- a/simyan/sqlite_cache.py +++ b/simyan/sqlite_cache.py @@ -11,7 +11,7 @@ from contextlib import contextmanager from datetime import datetime, timedelta, timezone from pathlib import Path -from typing import Any, Optional +from typing import Any from simyan import get_cache_root @@ -24,7 +24,7 @@ class SQLiteCache: expiry: How long to keep cache results. """ - def __init__(self, path: Optional[Path] = None, expiry: Optional[int] = 14): + def __init__(self, path: Path | None = None, expiry: int | None = 14): self._db_path = path or (get_cache_root() / "cache.sqlite") self._expiry = expiry self.initialize() From 88f8edd57b9713c2f8642424856781b5540ce5ce Mon Sep 17 00:00:00 2001 From: Buried-In-Code <6057651+Buried-In-Code@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:29:04 +1300 Subject: [PATCH 2/2] Version bump --- simyan/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simyan/__init__.py b/simyan/__init__.py index 71655c3..34ab326 100644 --- a/simyan/__init__.py +++ b/simyan/__init__.py @@ -1,7 +1,7 @@ """simyan package entry file.""" __all__ = ["__version__", "get_cache_root"] -__version__ = "1.5.1" +__version__ = "1.6.0" import os from pathlib import Path