Merge pull request #430 from pylast/rm-3.7
This commit is contained in:
commit
5f302e0813
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -18,30 +18,28 @@ keywords = [
|
|||
license = {text = "Apache-2.0"}
|
||||
maintainers = [{name = "Hugo van Kemenade"}]
|
||||
authors = [{name = "Amr Hassan <amr.hassan@gmail.com> 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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue