[tool.poetry] name = "beets" version = "2.5.1" description = "music tagger and library organizer" authors = ["Adrian Sampson "] maintainers = ["Serene-Arc"] license = "MIT" readme = "README.rst" homepage = "https://beets.io/" repository = "https://github.com/beetbox/beets" documentation = "https://beets.readthedocs.io/en/stable/" classifiers = [ "Topic :: Multimedia :: Sound/Audio", "Topic :: Multimedia :: Sound/Audio :: Players :: MP3", "License :: OSI Approved :: MIT License", "Environment :: Console", "Environment :: Web Environment", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", ] packages = [ { include = "beets" }, { include = "beetsplug" }, ] include = [ # extra files to include in the sdist { path = "docs", format = "sdist" }, { path = "extra", format = "sdist" }, { path = "man/**/*", format = "sdist" }, { path = "test/*.py", format = "sdist" }, { path = "test/rsrc/**/*", format = "sdist" }, ] exclude = ["docs/_build", "docs/modd.conf", "docs/**/*.css"] [tool.poetry.urls] Changelog = "https://github.com/beetbox/beets/blob/master/docs/changelog.rst" "Bug Tracker" = "https://github.com/beetbox/beets/issues" [tool.poetry.dependencies] python = ">=3.10,<4" colorama = { version = "*", markers = "sys_platform == 'win32'" } confuse = ">=2.1.0" jellyfish = "*" lap = ">=0.5.12" mediafile = ">=0.12.0" musicbrainzngs = ">=0.4" numpy = [ { python = "<3.13", version = ">=2.0.2" }, { python = ">=3.13", version = ">=2.3.4" }, ] platformdirs = ">=3.5.0" pyyaml = "*" typing_extensions = "*" unidecode = ">=1.3.6" beautifulsoup4 = { version = "*", optional = true } dbus-python = { version = "*", optional = true } flask = { version = "*", optional = true } flask-cors = { version = "*", optional = true } langdetect = { version = "*", optional = true } librosa = { version = ">=0.11", optional = true } scipy = [ # for librosa { python = "<3.13", version = ">=1.13.1", optional = true }, { python = ">=3.13", version = ">=1.16.1", optional = true }, ] numba = [ # for librosa { python = "<3.13", version = ">=0.60", optional = true }, { python = ">=3.13", version = ">=0.62.1", optional = true }, ] mutagen = { version = ">=1.33", optional = true } Pillow = { version = "*", optional = true } py7zr = { version = "*", optional = true } pyacoustid = { version = "*", optional = true } PyGObject = { version = "*", optional = true } pylast = { version = "*", optional = true } python-mpd2 = { version = ">=0.4.2", optional = true } python3-discogs-client = { version = ">=2.3.15", optional = true } pyxdg = { version = "*", optional = true } rarfile = { version = "*", optional = true } reflink = { version = "*", optional = true } requests = { version = "*", optional = true } resampy = { version = ">=0.4.3", optional = true } requests-oauthlib = { version = ">=0.6.1", optional = true } soco = { version = "*", optional = true } docutils = { version = ">=0.20.1", optional = true } pydata-sphinx-theme = { version = "*", optional = true } sphinx = { version = "*", optional = true } sphinx-design = { version = ">=0.6.1", optional = true } sphinx-copybutton = { version = ">=0.5.2", optional = true } titlecase = {version = "^2.4.1", optional = true} [tool.poetry.group.test.dependencies] beautifulsoup4 = "*" codecov = ">=2.1.13" flask = "*" langdetect = "*" mock = "*" pylast = "*" pytest = "*" pytest-cov = "*" pytest-flask = "*" python-mpd2 = "*" python3-discogs-client = ">=2.3.15" py7zr = "*" pyxdg = "*" rarfile = "*" requests-mock = ">=1.12.1" requests_oauthlib = "*" responses = ">=0.3.0" titlecase = "^2.4.1" [tool.poetry.group.lint.dependencies] docstrfmt = ">=1.11.1" ruff = ">=0.6.4" sphinx-lint = ">=1.0.0" [tool.poetry.group.typing.dependencies] mypy = "*" types-beautifulsoup4 = "*" types-docutils = ">=0.22.2.20251006" types-mock = "*" types-Flask-Cors = "*" types-Pillow = "*" types-PyYAML = "*" types-requests = "*" types-urllib3 = "*" [tool.poetry.group.release.dependencies] click = ">=8.1.7" packaging = ">=24.0" tomli = ">=2.0.1" [tool.poetry.extras] # inline comments note required external / non-python dependencies absubmit = ["requests"] # extractor binary from https://acousticbrainz.org/download aura = ["flask", "flask-cors", "Pillow"] autobpm = ["librosa", "resampy"] # badfiles # mp3val and flac beatport = ["requests-oauthlib"] bpd = ["PyGObject"] # gobject-introspection, gstreamer1.0-plugins-base, python3-gst-1.0 chroma = ["pyacoustid"] # chromaprint or fpcalc # convert # ffmpeg docs = [ "docutils", "pydata-sphinx-theme", "sphinx", "sphinx-lint", "sphinx-design", "sphinx-copybutton", ] discogs = ["python3-discogs-client"] embedart = ["Pillow"] # ImageMagick embyupdate = ["requests"] fetchart = ["beautifulsoup4", "langdetect", "Pillow", "requests"] import = ["py7zr", "rarfile"] # ipfs # go-ipfs # keyfinder # KeyFinder kodiupdate = ["requests"] lastgenre = ["pylast"] lastimport = ["pylast"] lyrics = ["beautifulsoup4", "langdetect", "requests"] metasync = ["dbus-python"] mpdstats = ["python-mpd2"] plexupdate = ["requests"] reflink = ["reflink"] replaygain = [ "PyGObject", ] # python-gi and GStreamer 1.0+ or mp3gain/aacgain or Python Audio Tools or ffmpeg scrub = ["mutagen"] sonosupdate = ["soco"] titlecase = ["titlecase"] thumbnails = ["Pillow", "pyxdg"] web = ["flask", "flask-cors"] [tool.poetry.scripts] beet = "beets.ui:main" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.pipx-install] poethepoet = ">=0.26" poetry = ">=1.8,<2" [tool.poe.tasks.build] help = "Build the package" shell = """ make -C docs man rm -rf man mv docs/_build/man . poetry build """ [tool.poe.tasks.bump] help = "Bump project version and update relevant files" cmd = "python ./extra/release.py bump $version" args = { version = { help = "The new version to set", positional = true, required = true } } [tool.poe.tasks.changelog] help = "Print the latest version's changelog in Markdown" cmd = "python ./extra/release.py changelog" [tool.poe.tasks.check-docs-links] help = "Check the documentation for broken URLs" cmd = "make -C docs linkcheck" [tool.poe.tasks.check-format] help = "Check the code for style issues" cmd = "ruff format --check --diff" [tool.poe.tasks.check-types] help = "Check the code for typing issues. Accepts mypy options." cmd = "mypy" [tool.poe.tasks.docs] help = "Build documentation" args = [{ name = "COMMANDS", positional = true, multiple = true, default = "html" }] cmd = "make -C docs $COMMANDS" [tool.poe.tasks.format] help = "Format the codebase" cmd = "ruff format" [tool.poe.tasks.format-docs] help = "Format the documentation" cmd = "docstrfmt docs *.rst" [tool.poe.tasks.lint] help = "Check the code for linting issues. Accepts ruff options." cmd = "ruff check" [tool.poe.tasks.lint-docs] help = "Lint the documentation" shell = "sphinx-lint --enable all --disable default-role $(git ls-files '*.rst')" [tool.poe.tasks.update-dependencies] help = "Update dependencies to their latest versions." cmd = "poetry update -vv" [tool.poe.tasks.test] help = "Run tests with pytest" cmd = "pytest $OPTS" env.OPTS.default = "-p no:cov" [tool.poe.tasks.test-with-coverage] help = "Run tests and record coverage" ref = "test" # record coverage in beets and beetsplug packages # save xml for coverage upload to coveralls # save html report for local dev use # measure coverage across logical branches # show which tests cover specific lines in the code (see the HTML report) env.OPTS = """ --cov=beets --cov=beetsplug --cov-report=xml:.reports/coverage.xml --cov-report=html:.reports/html --cov-branch --cov-context=test """ [tool.poe.tasks.check-temp-files] help = "Run each test module one by one and check for leftover temp files" shell = """ setopt nullglob for file in test/**/*.py; do print Temp files created by $file && poe test $file &>/dev/null tempfiles=(/tmp/**/tmp* /tmp/beets/**/*) if (( $#tempfiles )); then print -l $'\t'$^tempfiles rm -r --interactive=never $tempfiles &>/dev/null fi done """ interpreter = "zsh" [tool.docstrfmt] line-length = 80 extend-exclude = [ "docs/_templates/**/*", "docs/api/**/*", "README_kr.rst", ] [tool.ruff] target-version = "py39" line-length = 80 [tool.ruff.lint] select = [ # "ARG", # flake8-unused-arguments # "C4", # flake8-comprehensions "E", # pycodestyle "F", # pyflakes # "B", # flake8-bugbear "G", # flake8-logging-format "I", # isort "ISC", # flake8-implicit-str-concat "N", # pep8-naming "PT", # flake8-pytest-style # "RUF", # ruff "UP", # pyupgrade "TCH", # flake8-type-checking "W", # pycodestyle ] ignore = [ "TC006", # no need to quote 'cast's since we use 'from __future__ import annotations' ] [tool.ruff.lint.per-file-ignores] "beets/**" = ["PT"] "test/test_util.py" = ["E501"] [tool.ruff.lint.isort] split-on-trailing-comma = false [tool.ruff.lint.pycodestyle] max-line-length = 88 [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false parametrize-names-type = "csv" [tool.ruff.lint.flake8-unused-arguments] ignore-variadic-names = true [tool.ruff.lint.pep8-naming] classmethod-decorators = ["cached_classproperty"] extend-ignore-names = ["assert*", "cached_classproperty"]