From 452867699e6c2a59cb81d44653d671797cd10bbf Mon Sep 17 00:00:00 2001 From: Danielle McLean Date: Tue, 30 Jul 2024 10:09:33 +1000 Subject: [PATCH] Update Mypy so I can use PEP 695 type param syntax --- pdm.lock | 46 +++++++++++++++------ pyproject.toml | 3 +- src/mpd_now_playable/cache.py | 8 ++-- src/mpd_now_playable/config/load.py | 5 +-- src/mpd_now_playable/song_receiver.py | 6 +-- src/mpd_now_playable/tools/schema/define.py | 6 +-- src/mpd_now_playable/tools/types.py | 15 +++---- stubs/aiocache/base.pyi | 6 +-- stubs/aiocache/factory.pyi | 6 +-- stubs/boltons/iterutils.pyi | 4 +- 10 files changed, 53 insertions(+), 52 deletions(-) diff --git a/pdm.lock b/pdm.lock index fa4b4e9..f863acf 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,13 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "all", "dev"] -strategy = ["cross_platform"] -lock_version = "4.4.1" -content_hash = "sha256:ddedd388cce9ed181dc2f3786240fc14e19ceb4539f0a360eeb2efb28da63ebe" +groups = ["default", "all", "dev", "memcached", "redis", "websockets"] +strategy = [] +lock_version = "4.5.0" +content_hash = "sha256:fdf0ffc09550df3ba54428f2c6ceb534fc68c4e1633299dbcfd8db9d9f325564" + +[[metadata.targets]] +requires_python = ">=3.12" [[package]] name = "aiocache" @@ -49,6 +52,9 @@ name = "aiomcache" version = "0.8.2" requires_python = ">=3.8" summary = "Minimal pure python memcached client" +dependencies = [ + "typing-extensions>=4; python_version < \"3.11\"", +] files = [ {file = "aiomcache-0.8.2-py3-none-any.whl", hash = "sha256:9d78d6b6e74e775df18b350b1cddfa96bd2f0a44d49ad27fa87759a3469cef5e"}, {file = "aiomcache-0.8.2.tar.gz", hash = "sha256:43b220d7f499a32a71871c4f457116eb23460fa216e69c1d32b81e3209e51359"}, @@ -59,6 +65,9 @@ name = "annotated-types" version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.9\"", +] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -69,6 +78,9 @@ name = "attrs" version = "23.2.0" requires_python = ">=3.7" summary = "Classes Without Boilerplate" +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", +] files = [ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, @@ -167,21 +179,22 @@ files = [ [[package]] name = "mypy" -version = "1.10.1" +version = "1.11.0" requires_python = ">=3.8" summary = "Optional static typing for Python" dependencies = [ "mypy-extensions>=1.0.0", - "typing-extensions>=4.1.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, - {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, - {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, - {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, - {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, - {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba"}, + {file = "mypy-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd"}, + {file = "mypy-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d"}, + {file = "mypy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2"}, + {file = "mypy-1.11.0-py3-none-any.whl", hash = "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace"}, + {file = "mypy-1.11.0.tar.gz", hash = "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538"}, ] [[package]] @@ -425,6 +438,11 @@ name = "redis" version = "5.0.7" requires_python = ">=3.7" summary = "Python client for Redis database and key-value store" +dependencies = [ + "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", + "importlib-metadata>=1.0; python_version < \"3.8\"", + "typing-extensions; python_version < \"3.8\"", +] files = [ {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, @@ -438,6 +456,7 @@ summary = "Render rich text, tables, progress bars, syntax highlighting, markdow dependencies = [ "markdown-it-py>=2.2.0", "pygments<3.0.0,>=2.13.0", + "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", ] files = [ {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, @@ -534,6 +553,7 @@ summary = "Yet another URL library" dependencies = [ "idna>=2.0", "multidict>=4.0", + "typing-extensions>=3.7.4; python_version < \"3.8\"", ] files = [ {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, diff --git a/pyproject.toml b/pyproject.toml index b5cb70e..b21f79e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ build-backend = "pdm.backend" [tool.mypy] mypy_path = 'stubs' plugins = ['pydantic.mypy', 'mpd_now_playable.tools.schema.plugin'] +enable_incomplete_feature = 'NewGenericSyntax' [tool.ruff.lint] select = [ @@ -104,7 +105,7 @@ excludes = ["**/.mypy_cache"] [tool.pdm.dev-dependencies] dev = [ - "mypy>=1.7.1", + "mypy>=1.11.0", "ruff>=0.1.6", "class-doc>=0.2.6", ] diff --git a/src/mpd_now_playable/cache.py b/src/mpd_now_playable/cache.py index 58394e6..4fddc14 100644 --- a/src/mpd_now_playable/cache.py +++ b/src/mpd_now_playable/cache.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Generic, Optional, TypeVar +from typing import Any, Optional import ormsgpack from aiocache import Cache @@ -8,10 +8,8 @@ from aiocache.serializers import BaseSerializer from pydantic.type_adapter import TypeAdapter from yarl import URL -T = TypeVar("T") - -class OrmsgpackSerializer(BaseSerializer, Generic[T]): +class OrmsgpackSerializer[T](BaseSerializer): DEFAULT_ENCODING = None def __init__(self, schema: TypeAdapter[T]): @@ -28,7 +26,7 @@ class OrmsgpackSerializer(BaseSerializer, Generic[T]): return self.schema.validate_python(data) -def make_cache(schema: TypeAdapter[T], url: URL, namespace: str = "") -> Cache[T]: +def make_cache[T](schema: TypeAdapter[T], url: URL, namespace: str = "") -> Cache[T]: backend = Cache.get_scheme_class(url.scheme) if backend == Cache.MEMORY: return Cache(backend) diff --git a/src/mpd_now_playable/config/load.py b/src/mpd_now_playable/config/load.py index a45a0eb..ac299d8 100644 --- a/src/mpd_now_playable/config/load.py +++ b/src/mpd_now_playable/config/load.py @@ -1,6 +1,5 @@ from collections.abc import Mapping from os import environ -from typing import TypeVar from boltons.iterutils import remap from pytomlpp import load @@ -9,8 +8,6 @@ from xdg_base_dirs import xdg_config_home from .model import Config __all__ = ("loadConfig",) -K = TypeVar("K") -V = TypeVar("V") # Sadly this is the kind of function that's incredibly easy to type statically @@ -24,7 +21,7 @@ V = TypeVar("V") # type like NonNullable. Python's type system also doesn't infer a # dictionary literal as having a structural type by default in the way # TypeScript does, of course, so that part wouldn't work anyway. -def withoutNones(data: Mapping[K, V | None]) -> Mapping[K, V]: +def withoutNones[K, V](data: Mapping[K, V | None]) -> Mapping[K, V]: return remap(data, lambda p, k, v: v is not None) diff --git a/src/mpd_now_playable/song_receiver.py b/src/mpd_now_playable/song_receiver.py index f0d06ca..d6df301 100644 --- a/src/mpd_now_playable/song_receiver.py +++ b/src/mpd_now_playable/song_receiver.py @@ -1,17 +1,15 @@ from asyncio import AbstractEventLoop, new_event_loop from dataclasses import dataclass from importlib import import_module -from typing import Generic, Iterable, Literal, Protocol, TypeVar, cast +from typing import Iterable, Literal, Protocol, cast from .config.model import BaseReceiverConfig from .playback import Playback from .player import Player from .tools.types import not_none -T = TypeVar("T", bound=AbstractEventLoop, covariant=True) - -class LoopFactory(Generic[T], Protocol): +class LoopFactory[T: AbstractEventLoop](Protocol): @property def is_replaceable(self) -> bool: ... diff --git a/src/mpd_now_playable/tools/schema/define.py b/src/mpd_now_playable/tools/schema/define.py index f33c907..dcf970f 100644 --- a/src/mpd_now_playable/tools/schema/define.py +++ b/src/mpd_now_playable/tools/schema/define.py @@ -1,10 +1,8 @@ -from typing import Callable, Protocol, Self, TypeVar +from typing import Callable, Protocol, Self from pydantic.type_adapter import TypeAdapter from yarl import URL -T = TypeVar("T") - class ModelWithSchema(Protocol): @property @@ -13,7 +11,7 @@ class ModelWithSchema(Protocol): def schema(self) -> TypeAdapter[Self]: ... -def schema(schema_id: str) -> Callable[[type[T]], type[T]]: +def schema[T](schema_id: str) -> Callable[[type[T]], type[T]]: def decorate(clazz: type[T]) -> type[T]: type.__setattr__(clazz, "id", URL(schema_id)) type.__setattr__(clazz, "schema", TypeAdapter(clazz)) diff --git a/src/mpd_now_playable/tools/types.py b/src/mpd_now_playable/tools/types.py index deb18ad..d151c9b 100644 --- a/src/mpd_now_playable/tools/types.py +++ b/src/mpd_now_playable/tools/types.py @@ -1,5 +1,5 @@ from collections.abc import Callable -from typing import Any, TypeAlias, TypeVar +from typing import Any __all__ = ( "AnyExceptList", @@ -28,27 +28,22 @@ AnyExceptList = ( ) -U = TypeVar("U") -V = TypeVar("V") - - -def not_none(value: U | None) -> U: +def not_none[U](value: U | None) -> U: if value is None: raise ValueError("None should not be possible here.") return value -def option_fmap(f: Callable[[U], V], value: U | None) -> V | None: +def option_fmap[U, V](f: Callable[[U], V], value: U | None) -> V | None: if value is None: return None return f(value) -T = TypeVar("T", bound=AnyExceptList) -MaybePlural: TypeAlias = list[T] | T +type MaybePlural[T: AnyExceptList] = list[T] | T -def un_maybe_plural(value: MaybePlural[T] | None) -> list[T]: +def un_maybe_plural[T: AnyExceptList](value: MaybePlural[T] | None) -> list[T]: match value: case None: return [] diff --git a/stubs/aiocache/base.pyi b/stubs/aiocache/base.pyi index 8b7578f..13ba741 100644 --- a/stubs/aiocache/base.pyi +++ b/stubs/aiocache/base.pyi @@ -1,7 +1,3 @@ -from typing import Generic, TypeVar - -T = TypeVar("T") - -class BaseCache(Generic[T]): +class BaseCache[T]: @staticmethod def parse_uri_path(path: str) -> dict[str, str]: ... diff --git a/stubs/aiocache/factory.pyi b/stubs/aiocache/factory.pyi index 78cc6a5..783d761 100644 --- a/stubs/aiocache/factory.pyi +++ b/stubs/aiocache/factory.pyi @@ -1,11 +1,9 @@ -from typing import ClassVar, Optional, TypeVar +from typing import ClassVar, Optional from .base import BaseCache from .serializers import BaseSerializer -T = TypeVar("T") - -class Cache(BaseCache[T]): +class Cache[T](BaseCache[T]): MEMORY: ClassVar[type[BaseCache]] REDIS: ClassVar[type[BaseCache] | None] MEMCACHED: ClassVar[type[BaseCache] | None] diff --git a/stubs/boltons/iterutils.pyi b/stubs/boltons/iterutils.pyi index 2b56e0a..47fc123 100644 --- a/stubs/boltons/iterutils.pyi +++ b/stubs/boltons/iterutils.pyi @@ -1,5 +1,5 @@ from collections.abc import Callable, Mapping -from typing import TypeAlias, TypeVar +from typing import TypeVar # Apparently you need Python 3.13 for type var defaults to work. But since this # is just a stub file, it's okay if they aren't supported at runtime. @@ -8,7 +8,7 @@ KOut = TypeVar("KOut", default=KIn) VIn = TypeVar("VIn") VOut = TypeVar("VOut", default=VIn) -Path: TypeAlias = tuple[KIn, ...] +type Path[KIn] = tuple[KIn, ...] # remap() is Complicated and really difficult to define a type for, so I'm not # surprised the boltons package doesn't try to type it for you. This particular