Compare commits
3 commits
35703de261
...
2f70c6f7fa
Author | SHA1 | Date | |
---|---|---|---|
2f70c6f7fa | |||
3cb5db7528 | |||
2def2aece5 |
12 changed files with 89 additions and 27 deletions
25
pdm.lock
25
pdm.lock
|
@ -5,7 +5,7 @@
|
||||||
groups = ["default", "all", "dev"]
|
groups = ["default", "all", "dev"]
|
||||||
strategy = ["cross_platform"]
|
strategy = ["cross_platform"]
|
||||||
lock_version = "4.4.1"
|
lock_version = "4.4.1"
|
||||||
content_hash = "sha256:828f8051de2c2a04cede8130abf86764edb38f0428cd1d52dd797bff43452afe"
|
content_hash = "sha256:17fdf74bf4b2980c66c106cebcb936921e671e8adaeb2f2e95d95f255704aa38"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiocache"
|
name = "aiocache"
|
||||||
|
@ -80,6 +80,19 @@ files = [
|
||||||
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
|
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "class-doc"
|
||||||
|
version = "0.2.6"
|
||||||
|
requires_python = ">=3.6,<4.0"
|
||||||
|
summary = "Extract attributes docstrings defined in various ways"
|
||||||
|
dependencies = [
|
||||||
|
"more-itertools>=5.0.0",
|
||||||
|
]
|
||||||
|
files = [
|
||||||
|
{file = "class-doc-0.2.6.tar.gz", hash = "sha256:f5e036ed9b7f6de528affdd9f038851910b342d4c1c1252983a55ff080b530e0"},
|
||||||
|
{file = "class_doc-0.2.6-py3-none-any.whl", hash = "sha256:e6f2cea2dfbe93f76dee25de13d70dc0d2269698e8b849f751d98dc894c52ea5"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.7"
|
version = "3.7"
|
||||||
|
@ -90,6 +103,16 @@ files = [
|
||||||
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
|
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "more-itertools"
|
||||||
|
version = "10.3.0"
|
||||||
|
requires_python = ">=3.8"
|
||||||
|
summary = "More routines for operating on iterables, beyond itertools"
|
||||||
|
files = [
|
||||||
|
{file = "more-itertools-10.3.0.tar.gz", hash = "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463"},
|
||||||
|
{file = "more_itertools-10.3.0-py3-none-any.whl", hash = "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multidict"
|
name = "multidict"
|
||||||
version = "6.0.5"
|
version = "6.0.5"
|
||||||
|
|
|
@ -44,30 +44,10 @@ Issues = "https://git.00dani.me/00dani/mpd-now-playable/issues"
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
mpd-now-playable = 'mpd_now_playable.cli:main'
|
mpd-now-playable = 'mpd_now_playable.cli:main'
|
||||||
|
|
||||||
[tool.pdm.scripts]
|
|
||||||
start = {call = 'mpd_now_playable.cli:main'}
|
|
||||||
lint = 'ruff check src/mpd_now_playable'
|
|
||||||
typecheck = 'mypy -p mpd_now_playable'
|
|
||||||
check = {composite = ['lint', 'typecheck'], keep_going = true}
|
|
||||||
|
|
||||||
[tool.pdm.version]
|
|
||||||
source = "scm"
|
|
||||||
write_to = 'mpd_now_playable/__version__.py'
|
|
||||||
write_template = "__version__ = '{}'"
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["pdm-backend"]
|
requires = ["pdm-backend"]
|
||||||
build-backend = "pdm.backend"
|
build-backend = "pdm.backend"
|
||||||
|
|
||||||
[tool.pdm.build]
|
|
||||||
excludes = ["**/.mypy_cache"]
|
|
||||||
|
|
||||||
[tool.pdm.dev-dependencies]
|
|
||||||
dev = [
|
|
||||||
"mypy>=1.7.1",
|
|
||||||
"ruff>=0.1.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
mypy_path = 'stubs'
|
mypy_path = 'stubs'
|
||||||
|
|
||||||
|
@ -101,3 +81,26 @@ mypy-init-return = true
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
# I prefer tabs for accessibility reasons.
|
# I prefer tabs for accessibility reasons.
|
||||||
indent-style = "tab"
|
indent-style = "tab"
|
||||||
|
|
||||||
|
[tool.pdm.scripts]
|
||||||
|
start = {call = 'mpd_now_playable.cli:main'}
|
||||||
|
lint = 'ruff check src/mpd_now_playable'
|
||||||
|
typecheck = 'mypy -p mpd_now_playable'
|
||||||
|
check = {composite = ['lint', 'typecheck'], keep_going = true}
|
||||||
|
|
||||||
|
[tool.pdm.version]
|
||||||
|
source = "scm"
|
||||||
|
write_to = 'mpd_now_playable/__version__.py'
|
||||||
|
write_template = "__version__ = '{}'"
|
||||||
|
|
||||||
|
[tool.pdm.build]
|
||||||
|
excludes = ["**/.mypy_cache"]
|
||||||
|
|
||||||
|
[tool.pdm.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
"mypy>=1.7.1",
|
||||||
|
"ruff>=0.1.6",
|
||||||
|
"class-doc>=0.2.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,9 @@ from MediaPlayer import (
|
||||||
MPRemoteCommandHandlerStatusSuccess,
|
MPRemoteCommandHandlerStatusSuccess,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..async_tools import run_background_task
|
|
||||||
from ..player import Player
|
from ..player import Player
|
||||||
from ..song import PlaybackState, Song
|
from ..song import PlaybackState, Song
|
||||||
|
from ..tools.asyncio import run_background_task
|
||||||
from .persistent_id import song_to_persistent_id
|
from .persistent_id import song_to_persistent_id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,12 @@
|
||||||
"$ref": "#/definitions/URL"
|
"$ref": "#/definitions/URL"
|
||||||
},
|
},
|
||||||
"cache": {
|
"cache": {
|
||||||
"$ref": "#/definitions/URL"
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/URL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A URL describing a cache service for mpd-now-playable to use. Supported protocols are memory://, redis://, and memcached://."
|
||||||
},
|
},
|
||||||
"mpd": {
|
"mpd": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
@ -24,14 +29,17 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"host": {
|
"host": {
|
||||||
"default": "127.0.0.1",
|
"default": "127.0.0.1",
|
||||||
|
"description": "The hostname or IP address of your MPD server. If you're running MPD on your local machine, you don't need to configure this.",
|
||||||
"format": "hostname",
|
"format": "hostname",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
|
"description": "The password required to connect to your MPD instance, if you need one.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"port": {
|
"port": {
|
||||||
"default": 6600,
|
"default": 6600,
|
||||||
|
"description": "The port on which to connect to MPD. Unless you're managing multiple MPD servers on one machine for some reason, you probably haven't changed this from the default port, 6600.",
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
|
|
@ -11,8 +11,14 @@ __all__ = ("MpdConfig", "Config")
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class MpdConfig:
|
class MpdConfig:
|
||||||
|
#: The password required to connect to your MPD instance, if you need one.
|
||||||
password: Optional[str] = optional()
|
password: Optional[str] = optional()
|
||||||
|
#: The hostname or IP address of your MPD server. If you're running MPD
|
||||||
|
#: on your local machine, you don't need to configure this.
|
||||||
host: Host = Host("127.0.0.1")
|
host: Host = Host("127.0.0.1")
|
||||||
|
#: The port on which to connect to MPD. Unless you're managing multiple MPD
|
||||||
|
#: servers on one machine for some reason, you probably haven't changed this
|
||||||
|
#: from the default port, 6600.
|
||||||
port: Port = Port(6600)
|
port: Port = Port(6600)
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,5 +28,8 @@ class Config:
|
||||||
default=URL("https://cdn.00dani.me/m/schemata/mpd-now-playable/config-v1.json"),
|
default=URL("https://cdn.00dani.me/m/schemata/mpd-now-playable/config-v1.json"),
|
||||||
metadata=alias("$schema"),
|
metadata=alias("$schema"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#: A URL describing a cache service for mpd-now-playable to use. Supported
|
||||||
|
#: protocols are memory://, redis://, and memcached://.
|
||||||
cache: Optional[URL] = optional()
|
cache: Optional[URL] = optional()
|
||||||
mpd: MpdConfig = field(default_factory=MpdConfig)
|
mpd: MpdConfig = field(default_factory=MpdConfig)
|
||||||
|
|
|
@ -1,15 +1,33 @@
|
||||||
from json import dump
|
from json import dump
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pprint import pp
|
from pprint import pp
|
||||||
|
from shutil import get_terminal_size
|
||||||
from typing import Any, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
from apischema import schema, settings
|
||||||
from apischema.json_schema import JsonSchemaVersion, deserialization_schema
|
from apischema.json_schema import JsonSchemaVersion, deserialization_schema
|
||||||
|
from apischema.schemas import Schema
|
||||||
|
from class_doc import extract_docs_from_cls_obj
|
||||||
|
|
||||||
from .model import Config
|
from .model import Config
|
||||||
|
|
||||||
|
|
||||||
|
def field_base_schema(tp: type, name: str, alias: str) -> Schema | None:
|
||||||
|
desc_lines = extract_docs_from_cls_obj(tp).get(name, [])
|
||||||
|
if desc_lines:
|
||||||
|
print((tp, name, alias))
|
||||||
|
return schema(description=" ".join(desc_lines))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
settings.base_schema.field = field_base_schema
|
||||||
|
|
||||||
|
|
||||||
def generate() -> Mapping[str, Any]:
|
def generate() -> Mapping[str, Any]:
|
||||||
return deserialization_schema(Config, version=JsonSchemaVersion.DRAFT_7)
|
return deserialization_schema(
|
||||||
|
Config,
|
||||||
|
version=JsonSchemaVersion.DRAFT_7,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def write() -> None:
|
def write() -> None:
|
||||||
|
@ -18,7 +36,7 @@ def write() -> None:
|
||||||
|
|
||||||
schema_file = Path(__file__).parent / Config.schema.name
|
schema_file = Path(__file__).parent / Config.schema.name
|
||||||
print(f"Writing this schema to {schema_file}")
|
print(f"Writing this schema to {schema_file}")
|
||||||
pp(schema)
|
pp(schema, sort_dicts=True, width=get_terminal_size().columns)
|
||||||
with open(schema_file, "w") as fp:
|
with open(schema_file, "w") as fp:
|
||||||
dump(schema, fp, indent="\t", sort_keys=True)
|
dump(schema, fp, indent="\t", sort_keys=True)
|
||||||
fp.write("\n")
|
fp.write("\n")
|
||||||
|
|
|
@ -4,8 +4,8 @@ from typing import TypedDict
|
||||||
|
|
||||||
from yarl import URL
|
from yarl import URL
|
||||||
|
|
||||||
from ..async_tools import run_background_task
|
|
||||||
from ..cache import Cache, make_cache
|
from ..cache import Cache, make_cache
|
||||||
|
from ..tools.asyncio import run_background_task
|
||||||
from .types import CurrentSongResponse, MpdStateHandler
|
from .types import CurrentSongResponse, MpdStateHandler
|
||||||
|
|
||||||
CACHE_TTL = 60 * 60 # seconds = 1 hour
|
CACHE_TTL = 60 * 60 # seconds = 1 hour
|
||||||
|
|
|
@ -9,7 +9,7 @@ from yarl import URL
|
||||||
from ..config.model import MpdConfig
|
from ..config.model import MpdConfig
|
||||||
from ..player import Player
|
from ..player import Player
|
||||||
from ..song import PlaybackState, Song, SongListener
|
from ..song import PlaybackState, Song, SongListener
|
||||||
from ..type_tools import convert_if_exists
|
from ..tools.types import convert_if_exists
|
||||||
from .artwork_cache import MpdArtworkCache
|
from .artwork_cache import MpdArtworkCache
|
||||||
from .types import CurrentSongResponse, StatusResponse
|
from .types import CurrentSongResponse, StatusResponse
|
||||||
|
|
||||||
|
|
0
src/mpd_now_playable/tools/__init__.py
Normal file
0
src/mpd_now_playable/tools/__init__.py
Normal file
1
stubs/class_doc/__init__.pyi
Normal file
1
stubs/class_doc/__init__.pyi
Normal file
|
@ -0,0 +1 @@
|
||||||
|
def extract_docs_from_cls_obj(cls: type) -> dict[str, list[str]]: ...
|
Loading…
Reference in a new issue