From 38d657374d9ab5d5321cdd42fd64609052b9e075 Mon Sep 17 00:00:00 2001 From: Thomas Scholtes Date: Mon, 1 Apr 2024 13:01:23 +0200 Subject: [PATCH] Move to Ruff for linting and modernize code base We replace basic flake8 for linting and isort and black for formatting with Ruff. We enable a couple of linters and fix the offending code. --- .github/workflows/main.yaml | 5 +- DEVELOPING.md | 4 +- beetsplug/alternatives.py | 98 +++++----- poetry.lock | 352 +++++++++++------------------------- pyproject.toml | 41 ++++- test/cli_test.py | 87 +++++---- test/conftest.py | 1 - test/helper.py | 101 +++++------ 8 files changed, 274 insertions(+), 415 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a593361..e2cebe0 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -23,9 +23,8 @@ jobs: cache: poetry - run: poetry env use $(which python) - run: poetry install - - run: poetry run black --check . - - run: poetry run isort --check . - - run: poetry run flake8 + - run: poetry run ruff format --check + - run: poetry run ruff check - run: poetry run pyright --warnings - run: poetry run pytest - uses: coverallsapp/github-action@v2 diff --git a/DEVELOPING.md b/DEVELOPING.md index c989295..1100e4d 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -4,9 +4,7 @@ This project uses [Poetry][] for packaging and dependency management. We’re using the following tools to ensure consistency and quality -- [black](https://github.com/psf/black) -- [isort](https://github.com/PyCQA/isort) -- [flake8](https://github.com/PyCQA/flake8) +- [ruff](https://docs.astral.sh/ruff/) - [pytest](https://docs.pytest.org/) [poetry]: https://python-poetry.org/ diff --git a/beetsplug/alternatives.py b/beetsplug/alternatives.py index 09fe055..0f646ba 100644 --- a/beetsplug/alternatives.py +++ b/beetsplug/alternatives.py @@ -1,5 +1,4 @@ # Copyright (c) 2014 Thomas Scholtes -# -*- coding: utf-8 -*- # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), to @@ -42,13 +41,13 @@ def _remove(path, soft=True): return try: os.remove(path) - except (OSError, IOError) as exc: - raise FilesystemError(exc, "delete", (path,), traceback.format_exc()) + except OSError as exc: + raise FilesystemError(exc, "delete", (path,), traceback.format_exc()) from exc class AlternativesPlugin(BeetsPlugin): def __init__(self): - super(AlternativesPlugin, self).__init__() + super().__init__() def commands(self): return [AlternativesCommand(self)] @@ -58,15 +57,15 @@ def update(self, lib: Library, options: argparse.Namespace): if not options.all: raise UserError("Please specify a collection name or the --all flag") - for name in self.config.keys(): + for name in self.config.keys(): # noqa: SIM118 self.alternative(name, lib).update(create=options.create) else: try: alt = self.alternative(options.name, lib) except KeyError as e: raise UserError( - "Alternative collection '{0}' not found.".format(e.args[0]) - ) + f"Alternative collection '{e.args[0]}' not found." + ) from e alt.update(create=options.create) def list_tracks(self, lib, options): @@ -147,7 +146,7 @@ def __init__(self, plugin): Path Formats for more information.""", ) - super(AlternativesCommand, self).__init__(self.name, parser, self.help) + super().__init__(self.name, parser, self.help) def func(self, lib, opts, _): opts.func(lib, opts) @@ -175,12 +174,12 @@ class Action(Enum): SYNC_ART = 5 -class External(object): +class External: def __init__(self, log, name, lib, config): self._log = log self.name = name self.lib = lib - self.path_key = "alt.{0}".format(name) + self.path_key = f"alt.{name}" self.max_workers = int(str(beets.config["convert"]["threads"])) self.parse_config(config) @@ -218,13 +217,12 @@ def item_change_actions(self, item: Item, path: bytes, dest: bytes) -> List[Acti actions.append(Action.WRITE) album = item.get_album() - if album: - if ( - album.artpath - and os.path.isfile(syspath(album.artpath)) - and (item_mtime_alt < os.path.getmtime(syspath(album.artpath))) - ): - actions.append(Action.SYNC_ART) + if album and ( + album.artpath + and os.path.isfile(syspath(album.artpath)) + and (item_mtime_alt < os.path.getmtime(syspath(album.artpath))) + ): + actions.append(Action.SYNC_ART) return actions @@ -234,7 +232,7 @@ def _matched_item_action(self, item: Item) -> List[Action]: dest = self.destination(item) _, path_ext = os.path.splitext(path) _, dest_ext = os.path.splitext(dest) - if not path_ext == dest_ext: + if path_ext != dest_ext: # formats config option changed return [Action.REMOVE, Action.ADD] else: @@ -262,17 +260,15 @@ def ask_create(self, create: Optional[bool] = None) -> bool: return create msg = ( - "Collection at '{0}' does not exists. " + f"Collection at '{displayable_path(self.directory)}' does not exists. " "Maybe you forgot to mount it.\n" - "Do you want to create the collection? (y/n)".format( - displayable_path(self.directory) - ) + "Do you want to create the collection? (y/n)" ) return input_yn(msg, require=True) def update(self, create: Optional[bool] = None): if not os.path.isdir(syspath(self.directory)) and not self.ask_create(create): - print_("Skipping creation of {0}".format(displayable_path(self.directory))) + print_(f"Skipping creation of {displayable_path(self.directory)}") return converter = self._converter() @@ -281,11 +277,7 @@ def update(self, create: Optional[bool] = None): path = self._get_stored_path(item) for action in actions: if action == Action.MOVE: - print_( - ">{0} -> {1}".format( - displayable_path(path), displayable_path(dest) - ) - ) + print_(f">{displayable_path(path)} -> {displayable_path(dest)}") util.mkdirall(dest) util.move(path, dest) assert path is not None @@ -294,17 +286,17 @@ def update(self, create: Optional[bool] = None): item.store() path = dest elif action == Action.WRITE: - print_("*{0}".format(displayable_path(path))) + print_(f"*{displayable_path(path)}") item.write(path=path) elif action == Action.SYNC_ART: - print_("~{0}".format(displayable_path(path))) + print_(f"~{displayable_path(path)}") assert path is not None self._sync_art(item, path) elif action == Action.ADD: - print_("+{0}".format(displayable_path(dest))) + print_(f"+{displayable_path(dest)}") converter.submit(item) elif action == Action.REMOVE: - print_("-{0}".format(displayable_path(path))) + print_(f"-{displayable_path(path)}") self._remove_file(item) item.store() @@ -351,19 +343,16 @@ def _convert(item): def _sync_art(self, item: Item, path: bytes): """Embed artwork in the file at `path`.""" album = item.get_album() - if album: - if album.artpath and os.path.isfile(syspath(album.artpath)): - self._log.debug( - "Embedding art from {} into {}".format( - displayable_path(album.artpath), displayable_path(path) - ) - ) - art.embed_item(self._log, item, album.artpath, itempath=path) + if album and album.artpath and os.path.isfile(syspath(album.artpath)): + self._log.debug( + f"Embedding art from {displayable_path(album.artpath)} into {displayable_path(path)}" + ) + art.embed_item(self._log, item, album.artpath, itempath=path) class ExternalConvert(External): def __init__(self, log, name, formats, lib, config): - super(ExternalConvert, self).__init__(log, name, lib, config) + super().__init__(log, name, lib, config) convert_plugin = convert.ConvertPlugin() self._encode = convert_plugin.encode self._embed = convert_plugin.config["embed"].get(bool) @@ -385,7 +374,7 @@ def _convert(item): # Don't rely on the converter to write correct/complete tags. item.write(path=dest) else: - self._log.debug("copying {0}".format(displayable_path(dest))) + self._log.debug(f"copying {displayable_path(dest)}") util.copy(item.path, dest, replace=True) if self._embed: self._sync_art(item, dest) @@ -419,11 +408,12 @@ def parse_config(self, config): # Default as absolute so it doesn't break previous implementation config["link_type"] = "absolute" - self.relativelinks = config["link_type"].as_choice( - {"relative": SymlinkType.RELATIVE, "absolute": SymlinkType.ABSOLUTE} - ) + self.relativelinks = config["link_type"].as_choice({ + "relative": SymlinkType.RELATIVE, + "absolute": SymlinkType.ABSOLUTE, + }) - super(SymlinkView, self).parse_config(config) + super().parse_config(config) @override def item_change_actions(self, item: Item, path: bytes, dest: bytes): @@ -432,7 +422,7 @@ def item_change_actions(self, item: Item, path: bytes, dest: bytes): """ actions = [] - if not path == dest: + if path != dest: # The path of the link itself changed actions.append(Action.MOVE) elif not util.samefile(path, item.path): @@ -448,20 +438,16 @@ def update(self, create=None): path = self._get_stored_path(item) for action in actions: if action == Action.MOVE: - print_( - ">{0} -> {1}".format( - displayable_path(path), displayable_path(dest) - ) - ) + print_(f">{displayable_path(path)} -> {displayable_path(dest)}") self._remove_file(item) self._create_symlink(item) self._set_stored_path(item, dest) elif action == Action.ADD: - print_("+{0}".format(displayable_path(dest))) + print_(f"+{displayable_path(dest)}") self._create_symlink(item) self._set_stored_path(item, dest) elif action == Action.REMOVE: - print_("-{0}".format(displayable_path(path))) + print_(f"-{displayable_path(path)}") self._remove_file(item) else: continue @@ -484,12 +470,12 @@ def _sync_art(self, item: Item, path: bytes): class Worker(futures.ThreadPoolExecutor): def __init__(self, fn, max_workers: Optional[int]): - super(Worker, self).__init__(max_workers) + super().__init__(max_workers) self._tasks = set() self._fn = fn def submit(self, *args, **kwargs): - fut = super(Worker, self).submit(self._fn, *args, **kwargs) + fut = super().submit(self._fn, *args, **kwargs) self._tasks.add(fut) return fut diff --git a/poetry.lock b/poetry.lock index f5b60cd..baafdab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -45,66 +45,6 @@ test = ["beautifulsoup4", "coverage", "flask", "mock", "py7zr", "pylast", "pytes thumbnails = ["Pillow", "pyxdg"] web = ["flask", "flask-cors"] -[[package]] -name = "black" -version = "24.2.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, - {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, - {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, - {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, - {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, - {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, - {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, - {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, - {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, - {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, - {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, - {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, - {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, - {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, - {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, - {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, - {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, - {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, - {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, - {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, - {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, - {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "colorama" version = "0.4.6" @@ -132,63 +72,63 @@ pyyaml = "*" [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.dependencies] @@ -211,31 +151,15 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "flake8" -version = "7.0.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.2.0,<3.3.0" - [[package]] name = "importlib-metadata" -version = "7.0.2" +version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, - {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] @@ -244,7 +168,7 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -257,20 +181,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "jellyfish" version = "1.0.3" @@ -345,17 +255,6 @@ files = [ {file = "jellyfish-1.0.3.tar.gz", hash = "sha256:ddb22b7155f208e088352283ee78cb4ef2d2067a76e148a8bb43d177f32b37d2"}, ] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - [[package]] name = "mediafile" version = "0.12.0" @@ -423,17 +322,6 @@ files = [ {file = "mutagen-1.47.0.tar.gz", hash = "sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99"}, ] -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nodeenv" version = "1.8.0" @@ -450,41 +338,15 @@ setuptools = "*" [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - [[package]] name = "pluggy" version = "1.4.0" @@ -500,37 +362,15 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - [[package]] name = "pyright" -version = "1.1.352" +version = "1.1.356" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.352-py3-none-any.whl", hash = "sha256:0040cf173c6a60704e553bfd129dfe54de59cc76d0b2b80f77cfab4f50701d64"}, - {file = "pyright-1.1.352.tar.gz", hash = "sha256:a621c0dfbcf1291b3610641a07380fefaa1d0e182890a1b2a7f13b446e8109a9"}, + {file = "pyright-1.1.356-py3-none-any.whl", hash = "sha256:a101b0f375f93d7082f9046cfaa7ba15b7cf8e1939ace45e984c351f6e8feb99"}, + {file = "pyright-1.1.356.tar.gz", hash = "sha256:f05b8b29d06b96ed4a0885dad5a31d9dff691ca12b2f658249f583d5f2754021"}, ] [package.dependencies] @@ -542,13 +382,13 @@ dev = ["twine (>=3.4.1)"] [[package]] name = "pytest" -version = "8.0.2" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, - {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -556,11 +396,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.3.0,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -640,20 +480,46 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "ruff" +version = "0.3.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, + {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, + {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, + {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, + {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, +] + [[package]] name = "setuptools" -version = "69.1.1" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, - {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -680,18 +546,18 @@ files = [ [[package]] name = "typeguard" -version = "4.1.5" +version = "4.2.1" description = "Run-time type checker for Python" optional = false python-versions = ">=3.8" files = [ - {file = "typeguard-4.1.5-py3-none-any.whl", hash = "sha256:8923e55f8873caec136c892c3bed1f676eae7be57cdb94819281b3d3bc9c0953"}, - {file = "typeguard-4.1.5.tar.gz", hash = "sha256:ea0a113bbc111bcffc90789ebb215625c963411f7096a7e9062d4e4630c155fd"}, + {file = "typeguard-4.2.1-py3-none-any.whl", hash = "sha256:7da3bd46e61f03e0852f8d251dcbdc2a336aa495d7daff01e092b55327796eb8"}, + {file = "typeguard-4.2.1.tar.gz", hash = "sha256:c556a1b95948230510070ca53fa0341fb0964611bd05d598d87fb52115d65fee"}, ] [package.dependencies] importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} -typing-extensions = {version = ">=4.7.0", markers = "python_version < \"3.12\""} +typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] @@ -721,20 +587,20 @@ files = [ [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "efebe9940e5871340e08e18c3fd7f72b5159efd62148034ff7285db7ef70f966" +content-hash = "1fff367a10bd7a64e61496cd3adb18afbd94bf38ed77a6a32f0390463c860b06" diff --git a/pyproject.toml b/pyproject.toml index 9311725..d572d2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + [tool.poetry] name = "beets-alternatives" version = "0.11.1" @@ -25,15 +29,13 @@ python = "^3.8.1" beets = "^1.6.0" [tool.poetry.group.dev.dependencies] -black = "^24.2.0" confuse = "^2.0.1" -flake8 = "^7.0.0" -isort = "^5.12.0" mediafile = "^0.12.0" mock = "^5.0.2" pyright = "^1.1.340" pytest = "^8.0.2" pytest-cov = "^4.0.0" +ruff = "^0.3.4" typeguard = "^4.1.5" typing-extensions = "^4.9.0" @@ -46,12 +48,33 @@ filterwarnings = [ "ignore:.*pkgutil.get_loader.*:DeprecationWarning:confuse.util", ] -[tool.isort] -profile = "black" - [tool.pyright] typeCheckingMode = "basic" -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +[tool.ruff] +target-version = "py38" +unsafe-fixes = true +preview = true + +[tool.ruff.lint] +extend-select = [ + "I", # Sort imports + "C", # Pyflakes conventions + "UP", # Enforce modern Python syntx + "PIE", # Misc. lints + "FURB", # Elso enforce more modern Python syntax + "PT", # Pytest style + "B", # Bugbear, avoid common sources of bugs + "SIM", # Simplify + "T20", # Warn about `print()` + "RUF", + "C4", # List comprehension +] +ignore = [ + # Pyright checks for unused imports and does it better. + "F401", + # ternary can be less readable + "SIM108", +] + +allowed-confusables = ["’"] diff --git a/test/cli_test.py b/test/cli_test.py index eca3609..457a99d 100644 --- a/test/cli_test.py +++ b/test/cli_test.py @@ -2,6 +2,7 @@ import os.path import shutil +import pytest from beets import util from beets.ui import UserError from beets.util import bytestring_path, syspath @@ -56,25 +57,28 @@ def test_external(self): self.runcli("modify", "--yes", "onplayer=true", "artist:Bach") with control_stdin("y"): out = self.runcli("alt", "update", "myplayer") - self.assertIn("Do you want to create the collection?", out) + assert "Do you want to create the collection?" in out self.assertNotFileTag(external_from_mp3, b"ISAAC") self.assertNotFileTag(external_from_m4a, b"ISAAC") self.assertFileTag(external_from_ogg, b"ISAAC") - self.assertFalse(os.path.isfile(external_beet)) + assert not os.path.isfile(external_beet) self.runcli("modify", "--yes", "composer=JSB", "artist:Bach") list_output = self.runcli( "alt", "list-tracks", "myplayer", "--format", "$artist $title" ) - self.assertEqual( - list_output, "\n".join(["Bach was mp3", "Bach was m4a", "Bach was ogg", ""]) - ) + assert list_output == "\n".join([ + "Bach was mp3", + "Bach was m4a", + "Bach was ogg", + "", + ]) self.runcli("alt", "update", "myplayer") mediafile = MediaFile(syspath(external_from_ogg)) - self.assertEqual(mediafile.composer, "JSB") + assert mediafile.composer == "JSB" self.runcli("modify", "--yes", "onplayer!", "artist:Bach") self.runcli( @@ -85,11 +89,11 @@ def test_external(self): list_output = self.runcli( "alt", "list-tracks", "myplayer", "--format", "$artist" ) - self.assertEqual(list_output, "Beethoven\n") + assert list_output == "Beethoven\n" - self.assertFalse(os.path.isfile(external_from_mp3)) - self.assertFalse(os.path.isfile(external_from_m4a)) - self.assertFalse(os.path.isfile(external_from_ogg)) + assert not os.path.isfile(external_from_mp3) + assert not os.path.isfile(external_from_m4a) + assert not os.path.isfile(external_from_ogg) self.assertFileTag(external_beet, b"ISAAC") @@ -97,7 +101,7 @@ class SymlinkViewTest(TestHelper): """Test alternatives with the ``link`` format producing symbolic links.""" def setUp(self): - super(SymlinkViewTest, self).setUp() + super().setUp() self.set_paths_config({"default": "$artist/$album/$title"}) self.config["alternatives"] = { "by-year": { @@ -213,7 +217,8 @@ def test_valid_options(self): original_year="1982", ) - self.assertRaises(ConfigValueError, self.runcli, "alt", "update", "by-year") + with pytest.raises(ConfigValueError): + self.runcli("alt", "update", "by-year") class ExternalCopyTest(TestHelper): @@ -222,7 +227,7 @@ class ExternalCopyTest(TestHelper): """ def setUp(self): - super(ExternalCopyTest, self).setUp() + super().setUp() external_dir = self.mkdtemp() self.config["alternatives"] = { "myexternal": { @@ -261,7 +266,7 @@ def test_add_replace(self): self.runcli("alt", "update", "myexternal") item.load() - self.assertIn("alt.myexternal", item) + assert "alt.myexternal" in item def test_update_older(self): item = self.add_external_track("myexternal") @@ -272,7 +277,7 @@ def test_update_older(self): self.runcli("alt", "update", "myexternal") item.load() mediafile = MediaFile(syspath(self.get_path(item))) - self.assertEqual(mediafile.composer, "JSB") + assert mediafile.composer == "JSB" def test_no_update_newer(self): item = self.add_external_track("myexternal") @@ -283,7 +288,7 @@ def test_no_update_newer(self): self.runcli("alt", "update", "myexternal") item.load() mediafile = MediaFile(syspath(self.get_path(item))) - self.assertNotEqual(mediafile.composer, "JSB") + assert mediafile.composer != "JSB" def test_move_after_path_format_update(self): item = self.add_external_track("myexternal") @@ -313,18 +318,18 @@ def test_move_and_write_after_tags_changed(self): self.assertIsNotFile(old_path) self.assertIsFile(new_path) mediafile = MediaFile(syspath(new_path)) - self.assertEqual(mediafile.title, "a new title") + assert mediafile.title == "a new title" def test_prune_after_move(self): item = self.add_external_track("myexternal") artist_dir = os.path.dirname(check_type(self.get_path(item), bytes)) - self.assertTrue(os.path.isdir(artist_dir)) + assert os.path.isdir(artist_dir) item["artist"] = "a new artist" item.store() self.runcli("alt", "update", "myexternal") - self.assertFalse(os.path.exists(syspath(artist_dir))) + assert not os.path.exists(syspath(artist_dir)) def test_remove_item(self): item = self.add_external_track("myexternal") @@ -336,7 +341,7 @@ def test_remove_item(self): self.runcli("alt", "update", "myexternal") item.load() - self.assertNotIn("alt.myexternal", item) + assert "alt.myexternal" not in item self.assertIsNotFile(old_path) def test_remove_album(self): @@ -350,13 +355,13 @@ def test_remove_album(self): self.runcli("alt", "update", "myexternal") item.load() - self.assertNotIn("alt.myexternal", item) + assert "alt.myexternal" not in item self.assertIsNotFile(old_path) def test_unkown_collection(self): - with self.assertRaises(UserError) as c: + with pytest.raises(UserError) as e: self.runcli("alt", "update", "unkown") - assert str(c.exception) == "Alternative collection 'unkown' not found." + assert str(e.value) == "Alternative collection 'unkown' not found." def test_embed_art(self): """Test that artwork is embedded and updated to match the source file. @@ -430,19 +435,21 @@ def test_update_all(self): }, } - with self.assertRaises(UserError) as c: + with pytest.raises(UserError) as e: self.runcli("alt", "update") - assert str(c.exception) == "Please specify a collection name or the --all flag" + assert str(e.value) == "Please specify a collection name or the --all flag" item = self.add_track(title="a", myexternal="true") self.runcli("alt", "update", "--all") item.load() path_a = self.get_path(item, path_key="alt.a") - assert path_a and dir_a in path_a.decode() + assert path_a + assert dir_a in path_a.decode() self.assertIsFile(path_a) path_b = self.get_path(item, path_key="alt.b") - assert path_b and dir_b in path_b.decode() + assert path_b + assert dir_b in path_b.decode() self.assertIsFile(path_b) # Don’t update files on second run @@ -455,7 +462,7 @@ class ExternalConvertTest(TestHelper): """ def setUp(self): - super(ExternalConvertTest, self).setUp() + super().setUp() external_dir = self.mkdtemp() self.config["convert"]["formats"] = { "ogg": "bash -c \"cp '$source' '$dest';" + "printf ISOGG >> '$dest'\"" @@ -500,7 +507,7 @@ def test_convert_write_tags(self): item.load() alt_mediafile = MediaFile(syspath(self.get_path(item))) - self.assertEqual(alt_mediafile.title, "TITLE") + assert alt_mediafile.title == "TITLE" def test_skip_convert_for_same_format(self): item = self.add_track(myexternal="true") @@ -543,7 +550,7 @@ class ExternalConvertWorkerTest(TestHelper): """ def setUp(self): - super(ExternalConvertWorkerTest, self).setUp(mock_worker=False) + super().setUp(mock_worker=False) external_dir = self.mkdtemp() self.config["convert"]["formats"] = { "ogg": "bash -c \"cp '{source}' '$dest'\"".format( @@ -562,7 +569,7 @@ def setUp(self): def test_convert_multiple(self): items = [ self.add_track( - title="track {}".format(i), + title=f"track {i}", myexternal="true", format="m4a", ) @@ -581,7 +588,7 @@ class ExternalRemovableTest(TestHelper): """ def setUp(self): - super(ExternalRemovableTest, self).setUp() + super().setUp() external_dir = os.path.join(self.mkdtemp(), "\u00e9xt") self.config["alternatives"] = { "myexternal": { @@ -595,38 +602,38 @@ def test_ask_create_yes(self): item = self.add_track() with control_stdin("y"): out = self.runcli("alt", "update", "myexternal") - self.assertIn("Do you want to create the collection?", out) + assert "Do you want to create the collection?" in out item.load() - self.assertIn("alt.myexternal", item) + assert "alt.myexternal" in item def test_ask_create_no(self): item = self.add_track() with control_stdin("n"): out = self.runcli("alt", "update", "myexternal") - self.assertIn("Skipping creation of", out) + assert "Skipping creation of" in out item.load() - self.assertNotIn("alt.myexternal", item) + assert "alt.myexternal" not in item def test_create_option(self): item = self.add_track() self.runcli("alt", "update", "--create", "myexternal") item.load() - self.assertIn("alt.myexternal", item) + assert "alt.myexternal" in item def test_no_create_option(self): item = self.add_track() self.runcli("alt", "update", "--no-create", "myexternal") item.load() - self.assertNotIn("alt.myexternal", item) + assert "alt.myexternal" not in item def test_not_removable(self): item = self.add_track() self.external_config["removable"] = False with control_stdin("y"): out = self.runcli("alt", "update", "myexternal") - self.assertNotIn("Do you want to create the collection?", out) + assert "Do you want to create the collection?" not in out item.load() - self.assertIn("alt.myexternal", item) + assert "alt.myexternal" in item class CompletionTest(TestHelper): diff --git a/test/conftest.py b/test/conftest.py index 8437cf5..78081d5 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,4 +1,3 @@ import typeguard typeguard.install_import_hook("beetsplug.alternatives") -print("install import hooks") diff --git a/test/helper.py b/test/helper.py index c893930..c14cd7d 100644 --- a/test/helper.py +++ b/test/helper.py @@ -5,8 +5,10 @@ from concurrent import futures from contextlib import contextmanager from io import StringIO +from pathlib import Path from typing import Optional from unittest import TestCase +from unittest.mock import patch from zlib import crc32 import beets @@ -15,7 +17,6 @@ from beets.library import Item from beets.util import MoveOperation, bytestring_path, displayable_path, syspath from mediafile import MediaFile -from mock import patch import beetsplug.alternatives as alternatives import beetsplug.convert as convert @@ -25,7 +26,7 @@ class LogCapture(logging.Handler): def __init__(self): - super(LogCapture, self).__init__() + super().__init__() self.messages = [] def emit(self, record): @@ -54,12 +55,11 @@ def capture_stdout(): 'spam' """ org = sys.stdout - sys.stdout = capture = StringIO() + sys.stdout = StringIO() try: yield sys.stdout finally: sys.stdout = org - print(capture.getvalue()) @contextmanager @@ -83,97 +83,77 @@ def assertFileTag(self, path, tag): self.assertIsFile(path) with open(syspath(path), "rb") as f: f.seek(-5, os.SEEK_END) - self.assertEqual(f.read(), tag) + assert f.read() == tag def assertNotFileTag(self, path, tag): self.assertIsFile(path) with open(syspath(path), "rb") as f: f.seek(-5, os.SEEK_END) - self.assertNotEqual(f.read(), tag) + assert f.read() != tag def assertIsFile(self, path): - self.assertTrue( - os.path.isfile(syspath(path)), - msg="Path is not a file: {0}".format(displayable_path(path)), - ) + assert os.path.isfile( + syspath(path) + ), f"Path is not a file: {displayable_path(path)}" def assertIsNotFile(self, path): """Asserts that `path` is neither a regular file (``os.path.isfile``, follows symlinks and returns False for a broken symlink) nor a symlink (``os.path.islink``, returns True for both valid and broken symlinks). """ - self.assertFalse( - os.path.isfile(syspath(path)), - msg="Path is a file: {0}".format(displayable_path(path)), - ) - self.assertFalse( - os.path.islink(syspath(path)), - msg="Path is a symlink: {0}".format(displayable_path(path)), - ) + assert not os.path.isfile( + syspath(path) + ), f"Path is a file: {displayable_path(path)}" + assert not os.path.islink( + syspath(path) + ), f"Path is a symlink: {displayable_path(path)}" def assertSymlink(self, link, target, absolute=True): - self.assertTrue( - os.path.islink(syspath(link)), - msg="Path is not a symbolic link: {0}".format(displayable_path(link)), - ) - self.assertTrue( - os.path.isfile(syspath(target)), - msg="Path is not a file: {0}".format(displayable_path(link)), - ) + assert os.path.islink( + syspath(link) + ), f"Path is not a symbolic link: {displayable_path(link)}" + assert os.path.isfile( + syspath(target) + ), f"Path is not a file: {displayable_path(link)}" pre_link_target = bytestring_path(os.readlink(syspath(link))) link_target = os.path.join(os.path.dirname(link), pre_link_target) - self.assertTrue( - util.samefile(target, link_target), - msg="Symlink points to {} instead of {}".format( - displayable_path(link_target), displayable_path(target) - ), - ) + assert util.samefile( + target, link_target + ), f"Symlink points to {displayable_path(link_target)} instead of {displayable_path(target)}" if absolute: - self.assertTrue( - os.path.isabs(pre_link_target), - msg="Symlink {} is not absolute".format( - displayable_path(pre_link_target) - ), - ) + assert os.path.isabs( + pre_link_target + ), f"Symlink {displayable_path(pre_link_target)} is not absolute" else: - self.assertFalse( - os.path.isabs(pre_link_target), - msg="Symlink {} is not relative".format( - displayable_path(pre_link_target) - ), - ) + assert not os.path.isabs( + pre_link_target + ), f"Symlink {displayable_path(pre_link_target)} is not relative" class MediaFileAssertions(TestCase): def assertHasEmbeddedArtwork(self, path, compare_file=None): mediafile = MediaFile(syspath(path)) - self.assertIsNotNone(mediafile.art, msg="MediaFile has no embedded artwork") + assert mediafile.art is not None, "MediaFile has no embedded artwork" if compare_file: - with open(syspath(compare_file), "rb") as compare_fh: + with open(syspath(compare_file), "rb") as compare_fh: # noqa: FURB101 crc_is = crc32(mediafile.art) # pyright: ignore[reportArgumentType] crc_expected = crc32(compare_fh.read()) - self.assertEqual( - crc_is, - crc_expected, - msg="MediaFile has embedded artwork, but " - "content (CRC32: {}) doesn't match " - "expectations (CRC32: {}).".format(crc_is, crc_expected), + assert crc_is == crc_expected, ( + "MediaFile has embedded artwork, but " + f"content (CRC32: {crc_is}) doesn't match " + f"expectations (CRC32: {crc_expected})." ) def assertHasNoEmbeddedArtwork(self, path): mediafile = MediaFile(syspath(path)) - self.assertIsNone(mediafile.art, msg="MediaFile has embedded artwork") + assert mediafile.art is None, "MediaFile has embedded artwork" def assertMediaFileFields(self, path, **kwargs): mediafile = MediaFile(syspath(path)) for k, v in kwargs.items(): actual = getattr(mediafile, k) - self.assertTrue( - actual == v, - msg="MediaFile has tag {k}='{actual}' " - "instead of '{expected}'".format(k=k, actual=actual, expected=v), - ) + assert actual == v, f"MediaFile has tag {k}='{actual}' " f"instead of '{v}'" class TestHelper(Assertions, MediaFileAssertions): @@ -193,7 +173,7 @@ def setUp(self, mock_worker=True): self.addCleanup(patcher.stop) self._tempdirs = [] - plugins._classes = set([alternatives.AlternativesPlugin, convert.ConvertPlugin]) + plugins._classes = {alternatives.AlternativesPlugin, convert.ConvertPlugin} self.setup_beets() def tearDown(self): @@ -227,7 +207,8 @@ def setup_beets(self): self.libdir = bytestring_path(libdir) self.lib = beets.library.Library( - ":memory:", self.libdir # pyright: ignore[reportArgumentType] + ":memory:", + self.libdir, # pyright: ignore[reportArgumentType] ) self.fixture_dir = os.path.join( bytestring_path(os.path.dirname(__file__)), b"fixtures"