diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3f067c..3fda04c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy3.9", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] + python-version: ["pypy3.9", "3.8", "3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest] steps: @@ -21,6 +21,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true cache: pip cache-dependency-path: pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5529b8d..0de09d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,9 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.4.0 hooks: - id: pyupgrade - args: [--py37-plus] + args: [--py38-plus] - repo: https://github.com/psf/black rev: 23.3.0 @@ -14,7 +14,7 @@ repos: rev: 1.13.0 hooks: - id: blacken-docs - args: [--target-version=py37] + args: [--target-version=py38] additional_dependencies: [black==23.3.0] - repo: https://github.com/PyCQA/isort @@ -46,12 +46,12 @@ repos: - id: requirements-txt-fixer - repo: https://github.com/tox-dev/pyproject-fmt - rev: 0.9.2 + rev: 0.10.0 hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.12.2 + rev: v0.13 hooks: - id: validate-pyproject diff --git a/README.md b/README.md index ce20f6b..3d9e882 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Or from requirements.txt: Note: -* pyLast 5.2+ supports Python 3.7-3.12. +* pyLast 5.2+ supports Python 3.8-3.12. * pyLast 5.1 supports Python 3.7-3.11. * pyLast 5.0 supports Python 3.7-3.10. * pyLast 4.3 - 4.5 supports Python 3.6-3.10. diff --git a/pyproject.toml b/pyproject.toml index fc658ff..d1224dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,30 +18,28 @@ keywords = [ license = {text = "Apache-2.0"} maintainers = [{name = "Hugo van Kemenade"}] authors = [{name = "Amr Hassan and Contributors", email = "amr.hassan@gmail.com"}] -requires-python = ">=3.7" +requires-python = ">=3.8" classifiers = [ - "Development Status :: 5 - Production/Stable", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Internet", - "Topic :: Multimedia :: Sound/Audio", - "Topic :: Software Development :: Libraries :: Python Modules", + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Internet", + "Topic :: Multimedia :: Sound/Audio", + "Topic :: Software Development :: Libraries :: Python Modules", ] dynamic = [ "version", ] dependencies = [ "httpx", - 'importlib-metadata; python_version < "3.8"', ] [project.optional-dependencies] tests = [ diff --git a/src/pylast/__init__.py b/src/pylast/__init__.py index c6cd8be..60f9b5f 100644 --- a/src/pylast/__init__.py +++ b/src/pylast/__init__.py @@ -23,6 +23,7 @@ from __future__ import annotations import collections import hashlib import html.entities +import importlib.metadata import logging import os import re @@ -36,18 +37,11 @@ from xml.dom import Node, minidom import httpx -try: - # Python 3.8+ - import importlib.metadata as importlib_metadata -except ImportError: - # Python 3.7 and lower - import importlib_metadata # type: ignore - __author__ = "Amr Hassan, hugovk, Mice Pápai" __copyright__ = "Copyright (C) 2008-2010 Amr Hassan, 2013-2021 hugovk, 2017 Mice Pápai" __license__ = "apache2" __email__ = "amr.hassan@gmail.com" -__version__ = importlib_metadata.version(__name__) +__version__ = importlib.metadata.version(__name__) # 1 : This error does not exist diff --git a/tests/test_album.py b/tests/test_album.py index ae2c1a0..a56faf1 100755 --- a/tests/test_album.py +++ b/tests/test_album.py @@ -94,8 +94,8 @@ class TestPyLastAlbum(TestPyLastWithLastFm): image = album.get_cover_image() # Assert - self.assert_startswith(image, "https://") - self.assert_endswith(image, ".gif") + assert image.startswith("https://") + assert image.endswith(".gif") or image.endswith(".png") def test_mbid(self) -> None: # Arrange diff --git a/tests/test_library.py b/tests/test_library.py index e37b771..cc35233 100755 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -16,7 +16,7 @@ class TestPyLastLibrary(TestPyLastWithLastFm): representation = repr(library) # Assert - self.assert_startswith(representation, "pylast.Library(") + assert representation.startswith("pylast.Library(") def test_str(self) -> None: # Arrange @@ -26,7 +26,7 @@ class TestPyLastLibrary(TestPyLastWithLastFm): string = str(library) # Assert - self.assert_endswith(string, "'s Library") + assert string.endswith("'s Library") def test_library_is_hashable(self) -> None: # Arrange diff --git a/tests/test_librefm.py b/tests/test_librefm.py index 0647976..01c43d9 100755 --- a/tests/test_librefm.py +++ b/tests/test_librefm.py @@ -6,11 +6,11 @@ from flaky import flaky import pylast -from .test_pylast import PyLastTestCase, load_secrets +from .test_pylast import load_secrets @flaky(max_runs=3, min_passes=1) -class TestPyLastWithLibreFm(PyLastTestCase): +class TestPyLastWithLibreFm: """Own class for Libre.fm because we don't need the Last.fm setUp""" def test_libre_fm(self) -> None: @@ -38,4 +38,4 @@ class TestPyLastWithLibreFm(PyLastTestCase): representation = repr(network) # Assert - self.assert_startswith(representation, "pylast.LibreFMNetwork(") + assert representation.startswith("pylast.LibreFMNetwork(") diff --git a/tests/test_network.py b/tests/test_network.py index d10cc66..51f0b18 100755 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -330,12 +330,12 @@ class TestPyLastNetwork(TestPyLastWithLastFm): # Assert assert len(images) == 4 - self.assert_startswith(images[pylast.SIZE_SMALL], "https://") - self.assert_endswith(images[pylast.SIZE_SMALL], ".png") + assert images[pylast.SIZE_SMALL].startswith("https://") + assert images[pylast.SIZE_SMALL].endswith(".png") assert "/34s/" in images[pylast.SIZE_SMALL] - self.assert_startswith(images[pylast.SIZE_EXTRA_LARGE], "https://") - self.assert_endswith(images[pylast.SIZE_EXTRA_LARGE], ".png") + assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://") + assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png") assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE] def test_artist_search(self) -> None: @@ -362,12 +362,12 @@ class TestPyLastNetwork(TestPyLastWithLastFm): # Assert assert len(images) == 5 - self.assert_startswith(images[pylast.SIZE_SMALL], "https://") - self.assert_endswith(images[pylast.SIZE_SMALL], ".png") + assert images[pylast.SIZE_SMALL].startswith("https://") + assert images[pylast.SIZE_SMALL].endswith(".png") assert "/34s/" in images[pylast.SIZE_SMALL] - self.assert_startswith(images[pylast.SIZE_EXTRA_LARGE], "https://") - self.assert_endswith(images[pylast.SIZE_EXTRA_LARGE], ".png") + assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://") + assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png") assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE] def test_track_search(self) -> None: @@ -396,12 +396,12 @@ class TestPyLastNetwork(TestPyLastWithLastFm): # Assert assert len(images) == 4 - self.assert_startswith(images[pylast.SIZE_SMALL], "https://") - self.assert_endswith(images[pylast.SIZE_SMALL], ".png") + assert images[pylast.SIZE_SMALL].startswith("https://") + assert images[pylast.SIZE_SMALL].endswith(".png") assert "/34s/" in images[pylast.SIZE_SMALL] - self.assert_startswith(images[pylast.SIZE_EXTRA_LARGE], "https://") - self.assert_endswith(images[pylast.SIZE_EXTRA_LARGE], ".png") + assert images[pylast.SIZE_EXTRA_LARGE].startswith("https://") + assert images[pylast.SIZE_EXTRA_LARGE].endswith(".png") assert "/300x300/" in images[pylast.SIZE_EXTRA_LARGE] def test_search_get_total_result_count(self) -> None: diff --git a/tests/test_pylast.py b/tests/test_pylast.py index cdaf50e..4beeed7 100755 --- a/tests/test_pylast.py +++ b/tests/test_pylast.py @@ -32,24 +32,17 @@ def load_secrets(): # pragma: no cover return doc -class PyLastTestCase: - def assert_startswith(self, s, prefix, start=None, end=None) -> None: - assert s.startswith(prefix, start, end) - - def assert_endswith(self, s, suffix, start=None, end=None) -> None: - assert s.endswith(suffix, start, end) - - def _no_xfail_rerun_filter(err, name, test, plugin) -> bool: for _ in test.iter_markers(name="xfail"): return False @flaky(max_runs=3, min_passes=1, rerun_filter=_no_xfail_rerun_filter) -class TestPyLastWithLastFm(PyLastTestCase): +class TestPyLastWithLastFm: secrets = None - def unix_timestamp(self): + @staticmethod + def unix_timestamp() -> int: return int(time.time()) @classmethod @@ -70,7 +63,8 @@ class TestPyLastWithLastFm(PyLastTestCase): password_hash=password_hash, ) - def helper_is_thing_hashable(self, thing) -> None: + @staticmethod + def helper_is_thing_hashable(thing) -> None: # Arrange things = set() @@ -81,7 +75,8 @@ class TestPyLastWithLastFm(PyLastTestCase): assert thing is not None assert len(things) == 1 - def helper_validate_results(self, a, b, c) -> None: + @staticmethod + def helper_validate_results(a, b, c) -> None: # Assert assert a is not None assert b is not None @@ -105,27 +100,31 @@ class TestPyLastWithLastFm(PyLastTestCase): # Assert self.helper_validate_results(result1, result2, result3) - def helper_at_least_one_thing_in_top_list(self, things, expected_type) -> None: + @staticmethod + def helper_at_least_one_thing_in_top_list(things, expected_type) -> None: # Assert assert len(things) > 1 assert isinstance(things, list) assert isinstance(things[0], pylast.TopItem) assert isinstance(things[0].item, expected_type) - def helper_only_one_thing_in_top_list(self, things, expected_type) -> None: + @staticmethod + def helper_only_one_thing_in_top_list(things, expected_type) -> None: # Assert assert len(things) == 1 assert isinstance(things, list) assert isinstance(things[0], pylast.TopItem) assert isinstance(things[0].item, expected_type) - def helper_only_one_thing_in_list(self, things, expected_type) -> None: + @staticmethod + def helper_only_one_thing_in_list(things, expected_type) -> None: # Assert assert len(things) == 1 assert isinstance(things, list) assert isinstance(things[0], expected_type) - def helper_two_different_things_in_top_list(self, things, expected_type) -> None: + @staticmethod + def helper_two_different_things_in_top_list(things, expected_type) -> None: # Assert assert len(things) == 2 thing1 = things[0] diff --git a/tests/test_user.py b/tests/test_user.py index 6e0f3ba..12308eb 100755 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -24,7 +24,7 @@ class TestPyLastUser(TestPyLastWithLastFm): representation = repr(user) # Assert - self.assert_startswith(representation, "pylast.User('RJ',") + assert representation.startswith("pylast.User('RJ',") def test_str(self) -> None: # Arrange @@ -345,7 +345,7 @@ class TestPyLastUser(TestPyLastWithLastFm): url = user.get_image() # Assert - self.assert_startswith(url, "https://") + assert url.startswith("https://") def test_user_get_library(self) -> None: # Arrange @@ -428,8 +428,8 @@ class TestPyLastUser(TestPyLastWithLastFm): image = user.get_image() # Assert - self.assert_startswith(image, "https://") - self.assert_endswith(image, ".png") + assert image.startswith("https://") + assert image.endswith(".png") def test_get_url(self) -> None: # Arrange diff --git a/tox.ini b/tox.ini index 641e732..b0d3588 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ requires = tox>=4.2 env_list = lint - py{py3, 312, 311, 310, 39, 38, 37} + py{py3, 312, 311, 310, 39, 38} [testenv] extras =