Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
_commit: v2.1.3
_commit: v2.2.0
_src_path: gh:mopidy/mopidy-ext-template
author_email: [email protected]
author_full_name: Stein Magnus Jodal
Expand Down
22 changes: 10 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,47 @@ on:
push:
branches:
- main
workflow_dispatch:

jobs:
build:
name: Build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: hynek/build-and-inspect-python-package@v2

main:
strategy:
fail-fast: false
matrix:
include:
- name: "pytest (3.11)"
python: "3.11"
tox: "3.11"
- name: "pytest (3.12)"
python: "3.12"
tox: "3.12"
- name: "pytest (3.13)"
python: "3.13"
tox: "3.13"
- name: "pytest (3.14)"
python: "3.14"
tox: "3.14"
coverage: true
- name: "pyright"
python: "3.13"
python: "3.14"
tox: "pyright"
- name: "ruff check"
python: "3.13"
python: "3.14"
tox: "ruff-check"
- name: "ruff format"
python: "3.13"
python: "3.14"
tox: "ruff-format"

name: ${{ matrix.name }}
runs-on: ubuntu-24.04
container: ghcr.io/mopidy/ci:latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Fix home dir permissions to enable pip caching
run: chown -R root /github/home
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
cache: pip
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: hynek/build-and-inspect-python-package@v2
id: build
- uses: actions/download-artifact@v4
Expand Down
22 changes: 16 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "mopidy-mpd"
description = "Mopidy extension for controlling Mopidy from MPD clients"
readme = "README.md"
requires-python = ">= 3.11"
requires-python = ">= 3.13"
license = { text = "Apache-2.0" }
authors = [{ name = "Stein Magnus Jodal", email = "[email protected]" }]
classifiers = [
Expand All @@ -13,7 +13,11 @@ classifiers = [
"Topic :: Multimedia :: Sound/Audio :: Players",
]
dynamic = ["version"]
dependencies = ["mopidy >= 4.0.0a4", "pygobject >= 3.42", "pykka >= 4"]
dependencies = [
"mopidy >= 4.0.0a7",
"pygobject >= 3.50",
"pykka >= 4.1",
]

[project.urls]
Homepage = "https://github.com/mopidy/mopidy-mpd"
Expand All @@ -23,7 +27,7 @@ mpd = "mopidy_mpd:Extension"


[build-system]
requires = ["setuptools >= 66", "setuptools-scm >= 7.1"]
requires = ["setuptools >= 78", "setuptools-scm >= 8.2"]
build-backend = "setuptools.build_meta"


Expand All @@ -50,7 +54,7 @@ show_missing = true


[tool.pyright]
pythonVersion = "3.11"
pythonVersion = "3.13"
typeCheckingMode = "standard"
# Not all dependencies have type hints:
reportMissingTypeStubs = false
Expand All @@ -68,7 +72,7 @@ filterwarnings = [


[tool.ruff]
target-version = "py311"
target-version = "py313"

[tool.ruff.lint]
select = ["ALL"]
Expand Down Expand Up @@ -122,7 +126,13 @@ ignore = [


[tool.tox]
env_list = ["3.11", "3.12", "3.13", "pyright", "ruff-check", "ruff-format"]
env_list = [
"3.13",
"3.14",
"pyright",
"ruff-check",
"ruff-format",
]

[tool.tox.env_run_base]
package = "wheel"
Expand Down
2 changes: 1 addition & 1 deletion src/mopidy_mpd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ def get_config_schema(self) -> config.ConfigSchema:
return schema

def setup(self, registry: ext.Registry) -> None:
from .actor import MpdFrontend
from .actor import MpdFrontend # noqa: PLC0415

registry.add("frontend", MpdFrontend)
8 changes: 3 additions & 5 deletions src/mopidy_mpd/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,20 @@ def __init__(
@overload
def browse(
self, path: str | None, *, recursive: bool, lookup: Literal[True]
) -> Generator[
tuple[str, pykka.Future[dict[Uri, list[Track]]] | None], Any, None
]: ...
) -> Generator[tuple[str, pykka.Future[dict[Uri, list[Track]]] | None], Any]: ...

@overload
def browse(
self, path: str | None, *, recursive: bool, lookup: Literal[False]
) -> Generator[tuple[str, Ref | None], Any, None]: ...
) -> Generator[tuple[str, Ref | None], Any]: ...

def browse( # noqa: C901, PLR0912
self,
path: str | None,
*,
recursive: bool = True,
lookup: bool = True,
) -> Generator[Any, Any, None]:
) -> Generator[Any, Any]:
"""
Browse the contents of a given directory path.

Expand Down
5 changes: 2 additions & 3 deletions src/mopidy_mpd/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from typing import (
TYPE_CHECKING,
NewType,
TypeAlias,
TypeVar,
)

Expand All @@ -25,9 +24,9 @@
protocol.load_protocol_modules()

T = TypeVar("T")
Request: TypeAlias = str
type Request = str
Response = NewType("Response", list[str])
Filter: TypeAlias = Callable[[Request, Response, list["Filter"]], Response]
type Filter = Callable[[Request, Response, list["Filter"]], Response]


class MpdDispatcher:
Expand Down
3 changes: 2 additions & 1 deletion src/mopidy_mpd/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from types import TracebackType

from mopidy.core import CoreProxy

from mopidy_mpd import types
from mopidy_mpd.session import MpdSession, MpdSessionKwargs
from mopidy_mpd.types import SocketAddress
Expand Down Expand Up @@ -502,7 +503,7 @@ def on_stop(self) -> None:
"""Clean up connection resouces when actor stops."""
self.connection.stop("Actor is shutting down.")

def parse_lines(self) -> Generator[bytes, Any, None]:
def parse_lines(self) -> Generator[bytes, Any]:
"""Consume new data and yield any lines found."""
while re.search(self.terminator, self.recv_buffer):
line, self.recv_buffer = self.delimiter.split(self.recv_buffer, 1)
Expand Down
16 changes: 8 additions & 8 deletions src/mopidy_mpd/protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import inspect
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, TypeAlias
from typing import TYPE_CHECKING, Any

from mopidy_mpd import exceptions

Expand All @@ -31,20 +31,20 @@
VERSION = "0.19.0"


ResultValue: TypeAlias = str | int
ResultDict: TypeAlias = dict[str, ResultValue]
ResultTuple: TypeAlias = tuple[str, ResultValue]
ResultList: TypeAlias = list[ResultTuple | ResultDict]
Result: TypeAlias = None | ResultDict | ResultTuple | ResultList
HandlerFunc: TypeAlias = Callable[..., Result]
type ResultValue = str | int
type ResultDict = dict[str, ResultValue]
type ResultTuple = tuple[str, ResultValue]
type ResultList = list[ResultTuple | ResultDict]
type Result = None | ResultDict | ResultTuple | ResultList
type HandlerFunc = Callable[..., Result]


def load_protocol_modules() -> None:
"""
The protocol modules must be imported to get them registered in
:attr:`commands`.
"""
from . import ( # noqa: F401
from . import ( # noqa: F401, PLC0415
audio_output,
channels,
command_list,
Expand Down
8 changes: 5 additions & 3 deletions src/mopidy_mpd/protocol/music_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
from typing import TYPE_CHECKING, cast

from mopidy.models import Album, Artist, SearchResult, Track
from mopidy.types import DistinctField, Query, SearchField, Uri

from mopidy_mpd import exceptions, protocol, translator
from mopidy_mpd.protocol import stored_playlists

if TYPE_CHECKING:
from collections.abc import Iterable, Sequence

from mopidy.types import DistinctField, Query, SearchField, Uri

from mopidy_mpd.context import MpdContext


Expand Down Expand Up @@ -67,7 +69,7 @@ def _query_for_search(parameters: Sequence[str]) -> Query[SearchField]:
value = parameters.pop(0)
if value.strip():
query.setdefault(field, []).append(value)
return cast(Query[SearchField], query)
return cast("Query[SearchField]", query)


def _get_albums(search_results: Iterable[SearchResult]) -> list[Album]:
Expand Down Expand Up @@ -418,7 +420,7 @@ def lsinfo(context: MpdContext, uri: str | None = None) -> protocol.Result:
# `protocol.Result``, but this information disappears because of the
# typing of the `protocol.commands.add()`` decorator.
cast(
protocol.ResultList,
"protocol.ResultList",
stored_playlists.listplaylists(context),
)
)
Expand Down
2 changes: 1 addition & 1 deletion src/mopidy_mpd/protocol/playback.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def next_(context: MpdContext) -> None:


@protocol.commands.add("pause", state=protocol.BOOL)
def pause(context: MpdContext, state: bool | None = None) -> None:
def pause(context: MpdContext, state: bool | None = None) -> None: # noqa: FBT001
"""
*musicpd.org, playback section:*

Expand Down
7 changes: 4 additions & 3 deletions src/mopidy_mpd/protocol/stored_playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
from typing import TYPE_CHECKING, Literal, cast, overload
from urllib.parse import urlparse

from mopidy.models import Track
from mopidy.types import Uri, UriScheme

from mopidy_mpd import exceptions, protocol, translator

if TYPE_CHECKING:
from collections.abc import Iterable

from mopidy.models import Playlist
from mopidy.models import Playlist, Track

from mopidy_mpd.context import MpdContext

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -176,7 +177,7 @@ def load(
"""
playlist = _get_playlist(context, name, must_exist=True)
tracks = cast( # TODO(type): Improve typing of models to avoid cast.
tuple[Track],
"tuple[Track]",
playlist.tracks[playlist_slice], # pyright: ignore[reportIndexIssue]
)
track_uris = [track.uri for track in tracks]
Expand Down
1 change: 1 addition & 0 deletions src/mopidy_mpd/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

if TYPE_CHECKING:
from mopidy.core import CoreProxy

from mopidy_mpd.uri_mapper import MpdUriMapper


Expand Down
4 changes: 2 additions & 2 deletions src/mopidy_mpd/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, TypeAlias, TypedDict
from typing import TYPE_CHECKING, TypedDict

from mopidy.config import Config as MopidyConfig

Expand All @@ -23,4 +23,4 @@ class MpdConfig(TypedDict):
default_playlist_scheme: UriScheme


SocketAddress: TypeAlias = tuple[str, int | None]
type SocketAddress = tuple[str, int | None]
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class IsA:
class IsA: # noqa: PLW1641
def __init__(self, klass):
self.klass = klass

Expand Down
2 changes: 1 addition & 1 deletion tests/protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def setUp(self):
self.backend = dummy_backend.create_proxy(audio=self.audio)

self.core = cast(
core.CoreProxy,
"core.CoreProxy",
core.Core.start(
self.get_config(),
audio=self.audio,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_idle_hooked_up_correctly(event, expected):
frontend = actor.MpdFrontend(core=mock.Mock(), config=config)

with mock.patch("mopidy.listener.send") as send_mock:
frontend.on_event(event[0], **{e: None for e in event[1:]})
frontend.on_event(event[0], **dict.fromkeys(event[1:]))

if expected is None:
assert not send_mock.call_args
Expand Down
4 changes: 2 additions & 2 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def b_track() -> Ref:

@pytest.fixture
def backend_to_browse(a_track: Ref, b_track: Ref) -> BackendProxy:
backend = cast(BackendProxy, dummy_backend.create_proxy())
backend = cast("BackendProxy", dummy_backend.create_proxy())
backend.library.dummy_browse_result = {
"dummy:/": [
a_track,
Expand All @@ -39,7 +39,7 @@ def backend_to_browse(a_track: Ref, b_track: Ref) -> BackendProxy:
@pytest.fixture
def mpd_context(backend_to_browse: BackendProxy) -> MpdContext:
core = cast(
CoreProxy,
"CoreProxy",
Core.start(config=None, backends=[backend_to_browse]).proxy(),
)
return MpdContext(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def setUp(self):
config = {"mpd": {"password": None, "command_blacklist": ["disabled"]}}
self.backend = dummy_backend.create_proxy()
self.core = cast(
CoreProxy, Core.start(config=None, backends=[self.backend]).proxy()
"CoreProxy", Core.start(config=None, backends=[self.backend]).proxy()
)
self.dispatcher = MpdDispatcher(
config=config,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def setUp(self):
self.backend = dummy_backend.create_proxy(audio=self.audio)

self.core = cast(
core.CoreProxy,
"core.CoreProxy",
core.Core.start(
config,
audio=self.audio,
Expand Down
Loading