diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ca9b4d3ab..8a0c73672 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,10 +14,10 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.9"] + python-version: ["3.9"] runs-on: ${{ matrix.platform }} env: - IS_MAIN_PYTHON: ${{ matrix.python-version == '3.8' && matrix.platform == 'ubuntu-latest' }} + IS_MAIN_PYTHON: ${{ matrix.python-version == '3.9' && matrix.platform == 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 - name: Install Python tools diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c803e66cd..9e2552ab1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ on: - master env: - PYTHON_VERSION: 3.8 + PYTHON_VERSION: 3.9 jobs: changed-files: @@ -131,7 +131,7 @@ jobs: run: echo "::add-matcher::.github/sphinx-problem-matcher.json" - name: Build docs - run: | + run: |- poe docs |& tee /tmp/output # fail the job if there are issues grep -q " WARNING:" /tmp/output && exit 1 || exit 0 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ee4edbdcb..f44e89b6e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -118,10 +118,10 @@ command. Instead, you can activate the virtual environment in your shell with:: $ poetry shell -You should see ``(beets-py38)`` prefix in your shell prompt. Now you can run +You should see ``(beets-py3.9)`` prefix in your shell prompt. Now you can run commands directly, for example:: - $ (beets-py38) pytest + $ (beets-py3.9) pytest Additionally, `poethepoet`_ task runner assists us with the most common operations. Formatting, linting, testing are defined as ``poe`` tasks in diff --git a/docs/changelog.rst b/docs/changelog.rst index bec25ece4..34c1e6413 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,9 @@ Changelog goes here! Please add your entry to the bottom of one of the lists bel Unreleased ---------- +Beets now requires Python 3.9 or later since support for EOL Python 3.8 has +been dropped. + New features: Bug fixes: @@ -19,6 +22,8 @@ Bug fixes: For packagers: +* The minimum supported Python version is now 3.9. + Other changes: * Release workflow: fix the issue where the new release tag is created for the @@ -46,8 +51,6 @@ Bug fixes: * Bring back test files and the manual to the source distribution tarball. :bug:`5513` -For packagers: - Other changes: * Changed `bitesize` label to `good first issue`. Our `contribute`_ page is now diff --git a/pyproject.toml b/pyproject.toml index e929f7275..a65184837 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -240,7 +240,7 @@ done interpreter = "zsh" [tool.ruff] -target-version = "py38" +target-version = "py39" line-length = 80 [tool.ruff.lint] diff --git a/test/plugins/test_advancedrewrite.py b/test/plugins/test_advancedrewrite.py index 756d6d5fa..d2be1fa6c 100644 --- a/test/plugins/test_advancedrewrite.py +++ b/test/plugins/test_advancedrewrite.py @@ -87,23 +87,29 @@ class AdvancedRewritePluginTest(PluginTestCase): assert item.artists == ["유빈", "미미"] def test_fail_when_replacements_empty(self): - with pytest.raises( - UserError, - match="Advanced rewrites must have at least one replacement", - ), self.configure_plugin([{"match": "artist:A", "replacements": {}}]): + with ( + pytest.raises( + UserError, + match="Advanced rewrites must have at least one replacement", + ), + self.configure_plugin([{"match": "artist:A", "replacements": {}}]), + ): pass def test_fail_when_rewriting_single_valued_field_with_list(self): - with pytest.raises( - UserError, - match="Field artist is not a multi-valued field but a list was given: C, D", - ), self.configure_plugin( - [ - { - "match": "artist:'A & B'", - "replacements": {"artist": ["C", "D"]}, - }, - ] + with ( + pytest.raises( + UserError, + match="Field artist is not a multi-valued field but a list was given: C, D", # noqa: E501 + ), + self.configure_plugin( + [ + { + "match": "artist:'A & B'", + "replacements": {"artist": ["C", "D"]}, + }, + ] + ), ): pass diff --git a/test/plugins/test_zero.py b/test/plugins/test_zero.py index e21e2cabb..51913c8e0 100644 --- a/test/plugins/test_zero.py +++ b/test/plugins/test_zero.py @@ -102,9 +102,12 @@ class ZeroPluginTest(PluginTestCase): item.write() item_id = item.id - with self.configure_plugin( - {"fields": ["comments"], "update_database": True, "auto": False} - ), control_stdin("y"): + with ( + self.configure_plugin( + {"fields": ["comments"], "update_database": True, "auto": False} + ), + control_stdin("y"), + ): self.run_command("zero") mf = MediaFile(syspath(item.path)) @@ -122,9 +125,16 @@ class ZeroPluginTest(PluginTestCase): item.write() item_id = item.id - with self.configure_plugin( - {"fields": ["comments"], "update_database": False, "auto": False} - ), control_stdin("y"): + with ( + self.configure_plugin( + { + "fields": ["comments"], + "update_database": False, + "auto": False, + } + ), + control_stdin("y"), + ): self.run_command("zero") mf = MediaFile(syspath(item.path)) @@ -193,9 +203,12 @@ class ZeroPluginTest(PluginTestCase): item_id = item.id - with self.configure_plugin( - {"fields": ["year"], "keep_fields": ["comments"]} - ), control_stdin("y"): + with ( + self.configure_plugin( + {"fields": ["year"], "keep_fields": ["comments"]} + ), + control_stdin("y"), + ): self.run_command("zero") item = self.lib.get_item(item_id) @@ -242,9 +255,12 @@ class ZeroPluginTest(PluginTestCase): ) item.write() item_id = item.id - with self.configure_plugin( - {"fields": ["comments"], "update_database": True, "auto": False} - ), control_stdin("n"): + with ( + self.configure_plugin( + {"fields": ["comments"], "update_database": True, "auto": False} + ), + control_stdin("n"), + ): self.run_command("zero") mf = MediaFile(syspath(item.path)) diff --git a/test/test_config_command.py b/test/test_config_command.py index e30fde382..b68c4f042 100644 --- a/test/test_config_command.py +++ b/test/test_config_command.py @@ -112,9 +112,10 @@ class ConfigCommandTest(BeetsTestCase): def test_config_editor_not_found(self): msg_match = "Could not edit configuration.*here is problem" - with patch( - "os.execlp", side_effect=OSError("here is problem") - ), pytest.raises(ui.UserError, match=msg_match): + with ( + patch("os.execlp", side_effect=OSError("here is problem")), + pytest.raises(ui.UserError, match=msg_match), + ): self.run_command("config", "-e") def test_edit_invalid_config_file(self):