/
/
/
1[project]
2name = "music_assistant"
3# The version is set by GH action on release
4authors = [
5 {name = "The Music Assistant Authors", email = "[email protected]"},
6]
7classifiers = [
8 "Environment :: Console",
9 "Programming Language :: Python :: 3.14",
10]
11dependencies = [
12 "aiodns>=4.0.4",
13 "aiohttp_asyncmdnsresolver==0.2.0",
14 "Brotli>=1.0.9",
15 "aiohttp==3.14.3",
16 "aiohttp-fast-zlib==0.3.0",
17 "aiohttp-socks==0.11.0",
18 "aiofiles==24.1.0",
19 "aiosqlite==0.22.1",
20 "awesomeversion>=24.6.0",
21 "certifi==2025.11.12",
22 "colorlog==6.10.1",
23 "modern_colorthief==0.2.1",
24 "cryptography==50.0.0",
25 "chardet>=5.2.0",
26 "ifaddr==0.2.0",
27 "getmac==0.9.5",
28 "mashumaro==3.20",
29 "music-assistant-models==1.1.188",
30 "music-assistant-frontend==2.17.271",
31 "mutagen==1.48.1",
32 "orjson==3.11.6",
33 "pillow==12.3.0",
34 "podcastparser==0.6.11",
35 "propcache>=0.2.1",
36 "python-slugify==8.0.4",
37 "xmltodict==1.0.4",
38 "markdownify==1.2.3",
39 "shortuuid==1.0.13",
40 "zeroconf==0.149.16",
41 "uv>=0.8.0",
42 "librosa==0.11.0",
43 "numpy==2.3.5", # numpy 2.4.0+ uses X86_V2 CPU baseline (requires SSE4.2) which breaks older CPUs https://github.com/music-assistant/support/issues/5005#issuecomment-3972476728
44 "torch==2.13.0",
45 "torchaudio==2.11.0",
46 "gql[all]==4.0.0",
47 # TEMPORARY test-drive pin â replace with PyPI release before merge (see webrtc_aiolibdatachannel_plan.md Phase C)
48 "aiolibdatachannel @ git+https://github.com/music-assistant/aiolibdatachannel@feat/certpem-testdrive",
49 "pyjwt[crypto]>=2.10.1",
50]
51description = "Music Assistant"
52license = {text = "Apache-2.0"}
53readme = "README.md"
54requires-python = ">=3.14"
55version = "0.0.0"
56
57[project.optional-dependencies]
58test = [
59 "codespell==2.4.3",
60 "mypy==2.3.0",
61 "pre-commit==4.6.1",
62 "pre-commit-hooks==6.0.0",
63 "psutil==7.2.2",
64 "pytest==9.0.3",
65 "pytest-aiohttp==1.1.0",
66 "pytest-codspeed==5.0.3",
67 "pytest-cov==7.0.0",
68 "pytest-timeout==2.4.0",
69 "pytest-xdist==3.8.0",
70 "syrupy==5.5.3",
71 "tomli==2.4.1",
72 "ruff==0.15.22",
73 "yappi==1.7.6",
74]
75
76[project.scripts]
77mass = "music_assistant.__main__:main"
78
79[tool.codespell]
80# explicit is misspelled in the iTunes API
81# "additionals" is a fixed field name from the Nicovideo API fixtures
82# "connexion" is a French word in the MusicMe login URL path
83# "claus" is Santa Claus, in the christmas_music genre description
84# "childrens" is the normalized form of the "Children's" audiobook genre (apostrophe stripped)
85# "speaches" is the name of a self-hostable OpenAI-compatible speech server
86# translations/*.json are machine-translated locale files (Lokalise); foreign words aren't typos
87# pr-review/standards.md and instructions/*.instructions.md quote reviewer comments verbatim
88ignore-words-list = "provid,hass,followings,childs,explict,additionals,commitish,nam,connexion,claus,childrens,speaches,"
89skip = """*.js,*.svg,\
90.github/pr-review/standards.md,\
91.github/instructions/music-assistant-standards.instructions.md,\
92music_assistant/helpers/resources/countries.json,\
93music_assistant/helpers/resources/genres/genre_mapping.json,\
94music_assistant/providers/sonic_analysis/vendored_clap/**,\
95music_assistant/translations/*.json,\
96NOTICE,\
97"""
98
99[tool.setuptools]
100include-package-data = true
101packages = ["music_assistant"]
102platforms = ["any"]
103zip-safe = false
104
105[tool.setuptools.package-data]
106music_assistant = ["py.typed", "helpers/app_secrets.json"]
107
108[tool.ruff]
109fix = true
110show-fixes = true
111
112line-length = 100
113target-version = "py314"
114
115[tool.ruff.lint.pydocstyle]
116# Use Sphinx-style docstrings with :param: syntax
117# pep257 is the base convention, we enforce Sphinx-style parameters separately
118convention = "pep257"
119
120[tool.ruff.lint.pylint]
121
122max-args = 10
123max-branches = 25
124max-returns = 15
125max-statements = 50
126
127[tool.mypy]
128platform = "linux"
129python_version = "3.14"
130
131# set this to normal when we have fixed all exclusions
132follow_imports = "silent"
133
134# suppress errors about unsatisfied imports
135ignore_missing_imports = true
136
137# be strict
138check_untyped_defs = true
139disable_error_code = [
140 "annotation-unchecked",
141 "import-not-found",
142 "import-untyped",
143]
144disallow_any_generics = true
145disallow_incomplete_defs = true
146disallow_subclassing_any = true
147disallow_untyped_calls = true
148disallow_untyped_decorators = true
149disallow_untyped_defs = true
150enable_error_code = [
151 "ignore-without-code",
152 "redundant-self",
153 "truthy-iterable",
154]
155exclude = [
156 '^music_assistant/providers/sonic_analysis/vendored_clap/.*$',
157]
158extra_checks = false
159local_partial_types = true
160no_implicit_optional = true
161no_implicit_reexport = true
162packages = [
163 "tests",
164 "music_assistant",
165]
166show_error_codes = true
167strict_equality = true
168strict_optional = true
169warn_incomplete_stub = true
170warn_no_return = true
171warn_redundant_casts = true
172warn_return_any = true
173warn_unreachable = true
174warn_unused_configs = true
175warn_unused_ignores = true
176
177# plexapi ships a py.typed marker but its annotations are incomplete (unannotated
178# methods, attributes declared as None and only set dynamically). Treat it as an
179# untyped dependency rather than trusting its type info.
180[[tool.mypy.overrides]]
181follow_imports = "skip"
182module = ["plexapi", "plexapi.*"]
183
184[tool.ruff.format]
185# Force Linux/macOS line endings
186line-ending = "lf"
187
188[tool.pytest.ini_options]
189asyncio_mode = "auto"
190timeout = 120
191filterwarnings = [
192 # Suppress Python 3.14 AsyncMock internal warnings about unawaited coroutines
193 "ignore:coroutine.*was never awaited:RuntimeWarning",
194]
195
196[tool.ruff.lint]
197ignore = [
198 "ANN002", # Just annoying, not really useful
199 "ANN003", # Just annoying, not really useful
200 "ANN401", # Opinioated warning on disallowing dynamically typed expressions
201 "D203", # Conflicts with other rules
202 "D417", # False positives in some occasions
203 "EM101", # Just annoying, not really useful
204 "EM102", # Just annoying, not really useful
205 "FIX002", # Just annoying, not really useful
206 "PLR2004", # Just annoying, not really useful
207 "PGH004", # Just annoying, not really useful
208 "PD011", # Just annoying, not really useful
209 "S101", # assert is often used to satisfy type checking
210 "TC001", # Just annoying, not really useful
211 "TC003", # Just annoying, not really useful
212 "TD002", # Just annoying, not really useful
213 "TD003", # Just annoying, not really useful
214 "TD004", # Just annoying, not really useful
215 "TRY003", # Just annoying, not really useful
216 "TRY400", # Just annoying, not really useful
217 "COM812", # Conflicts with the Ruff formatter
218 "ASYNC109", # Not relevant, since we use helpers with configurable timeouts
219 "ASYNC110", # Use `asyncio.Event` instead of awaiting `asyncio.sleep` in a `while` loop
220 "N818", # Just annoying, not really useful
221 "E501", # Handled by ruff formatter
222 "ASYNC240", # Not relevant, we use asyncio not trio/anyio
223 "UP047", # Not yet ready to migrate to type parameters
224 "PLW0108", # Just annoying, not really useful
225 "FURB110", # Just annoying, not really useful # TEMPORARY DISABLED rules # The below rules must be enabled later one-by-one !
226 "BLE001",
227 "FBT001",
228 "FBT002",
229 "FBT003",
230 "ANN001",
231 "ANN201",
232 "ANN202",
233 "TRY002",
234 "PTH103",
235 "PTH100",
236 "PTH110",
237 "PTH111",
238 "PTH112",
239 "PTH113",
240 "PTH118",
241 "PTH120",
242 "PTH123",
243 "G004",
244 "S104",
245 "S105",
246 "S106",
247 "SLF001",
248 "SIM102",
249 "PERF401",
250 "PERF402",
251 "ARG002",
252 "S311",
253 "TRY301",
254 "PLR0912",
255 "B904",
256 "TRY401",
257 "S324",
258 "ERA001",
259 "PTH206",
260 "C901",
261 "S304",
262 "TRY300",
263 "S608",
264 "B007",
265 "ANN204",
266]
267
268select = ["ALL"]
269# Enforce the multi-line docstring summary on the second line (the pep257 convention disables both
270# D212 and D213; we re-enable D213 so our "summary starts on the next line" style is checked).
271extend-select = ["D213"]
272
273[tool.ruff.lint.flake8-pytest-style]
274fixture-parentheses = false
275mark-parentheses = false
276
277[tool.ruff.lint.isort]
278known-first-party = ["music_assistant"]
279
280[tool.ruff.lint.mccabe]
281max-complexity = 25
282
283[[tool.uv.index]]
284explicit = true
285name = "pytorch-cpu"
286url = "https://download.pytorch.org/whl/cpu"
287
288[tool.uv]
289# Build urllib3-future (pulled in via niquests) from source so URLLIB3_NO_OVERRIDE=1 set during
290# installs takes effect, stopping it from hijacking the stock urllib3 namespace server-wide.
291no-binary-package = ["urllib3-future"]
292
293[tool.uv.sources]
294torch = {index = "pytorch-cpu"}
295torchaudio = {index = "pytorch-cpu"}
296
297[build-system]
298build-backend = "setuptools.build_meta"
299requires = ["setuptools>=61"]
300