diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 13f3f43..fd4c7e6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,12 @@ name: Lint on: [push, pull_request] +env: + FORCE_COLOR: 1 + jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ddbb33..8f833d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,33 +3,28 @@ repos: rev: v2.7.2 hooks: - id: pyupgrade - args: ["--py3-plus"] + args: ["--py36-plus"] - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 20.8b1 hooks: - id: black - args: ["--target-version", "py35"] + args: ["--target-version", "py36"] # override until resolved: https://github.com/psf/black/issues/402 files: \.pyi?$ types: [] + - repo: https://github.com/PyCQA/isort + rev: 5.5.2 + hooks: + - id: isort + - repo: https://gitlab.com/pycqa/flake8 rev: 3.8.3 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] - - repo: https://github.com/asottile/seed-isort-config - rev: v2.2.0 - hooks: - - id: seed-isort-config - - - repo: https://github.com/timothycrosley/isort - rev: 5.4.2 - hooks: - - id: isort - - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.6.0 hooks: diff --git a/.travis.yml b/.travis.yml index f904565..688e4e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,6 @@ matrix: - python: 3.8 - python: 3.7 - python: 3.6 - - python: 3.5 - python: 3.9-dev - python: 3.10-dev - python: pypy3 diff --git a/README.md b/README.md index 806c718..6ddcf9f 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,13 @@ Or from requirements.txt: Note: -* pylast 3.0.0+ supports Python 3.5+ ([#265](https://github.com/pylast/pylast/issues/265)) -* pyLast 2.2.0 - 2.4.0 supports Python 2.7.10+, 3.4, 3.5, 3.6, 3.7. -* pyLast 2.0.0 - 2.1.0 supports Python 2.7.10+, 3.4, 3.5, 3.6. -* pyLast 1.7.0 - 1.9.0 supports Python 2.7, 3.3, 3.4, 3.5, 3.6. -* pyLast 1.0.0 - 1.6.0 supports Python 2.7, 3.3, 3.4. +* pyLast 4.0.0+ supports Python 3.6+. +* pyLast 3.2.0 - 3.3.0 supports Python 3.5-3.8. +* pyLast 3.0.0 - 3.1.0 supports Python 3.5-3.7. +* pyLast 2.2.0 - 2.4.0 supports Python 2.7.10+, 3.4-3.7. +* pyLast 2.0.0 - 2.1.0 supports Python 2.7.10+, 3.4-3.6. +* pyLast 1.7.0 - 1.9.0 supports Python 2.7, 3.3-3.6. +* pyLast 1.0.0 - 1.6.0 supports Python 2.7, 3.3-3.4. * pyLast 0.5 supports Python 2, 3. * pyLast < 0.5 supports Python 2. @@ -49,7 +51,6 @@ Features * Proxy support. * Internal caching support for some web services calls (disabled by default). * Support for other API-compatible networks like Libre.fm. - * Python 3-friendly (Starting from 0.5). Getting started diff --git a/setup.cfg b/setup.cfg index bf5350d..191fac9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,5 @@ [flake8] -ignore = W503 max_line_length = 88 [tool:isort] -known_third_party = flaky,pkg_resources,pylast,pytest,setuptools +profile = black diff --git a/setup.py b/setup.py index 7238518..171283d 100755 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( extras_require={ "tests": ["flaky", "pytest", "pytest-cov", "pytest-random-order", "pyyaml"] }, - python_requires=">=3.5", + python_requires=">=3.6", classifiers=[ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", @@ -35,7 +35,6 @@ setup( "Topic :: Multimedia :: Sound/Audio", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", diff --git a/src/pylast/__init__.py b/src/pylast/__init__.py index fb2de22..6e3c226 100644 --- a/src/pylast/__init__.py +++ b/src/pylast/__init__.py @@ -146,29 +146,29 @@ class _Network: token=None, ): """ - name: the name of the network - homepage: the homepage URL - ws_server: the URL of the webservices server - api_key: a provided API_KEY - api_secret: a provided API_SECRET - session_key: a generated session_key or None - username: a username of a valid user - password_hash: the output of pylast.md5(password) where password is - the user's password - domain_names: a dict mapping each DOMAIN_* value to a string domain - name - urls: a dict mapping types to URLs - token: an authentication token to retrieve a session + name: the name of the network + homepage: the homepage URL + ws_server: the URL of the webservices server + api_key: a provided API_KEY + api_secret: a provided API_SECRET + session_key: a generated session_key or None + username: a username of a valid user + password_hash: the output of pylast.md5(password) where password is + the user's password + domain_names: a dict mapping each DOMAIN_* value to a string domain + name + urls: a dict mapping types to URLs + token: an authentication token to retrieve a session - if username and password_hash were provided and not session_key, - session_key will be generated automatically when needed. + if username and password_hash were provided and not session_key, + session_key will be generated automatically when needed. - Either a valid session_key or a combination of username and - password_hash must be present for scrobbling. + Either a valid session_key or a combination of username and + password_hash must be present for scrobbling. - You should use a preconfigured network object through a - get_*_network(...) method instead of creating an object - of this class, unless you know what you're doing. + You should use a preconfigured network object through a + get_*_network(...) method instead of creating an object + of this class, unless you know what you're doing. """ self.name = name @@ -209,56 +209,56 @@ class _Network: def get_artist(self, artist_name): """ - Return an Artist object + Return an Artist object """ return Artist(artist_name, self) def get_track(self, artist, title): """ - Return a Track object + Return a Track object """ return Track(artist, title, self) def get_album(self, artist, title): """ - Return an Album object + Return an Album object """ return Album(artist, title, self) def get_authenticated_user(self): """ - Returns the authenticated user + Returns the authenticated user """ return AuthenticatedUser(self) def get_country(self, country_name): """ - Returns a country object + Returns a country object """ return Country(country_name, self) def get_user(self, username): """ - Returns a user object + Returns a user object """ return User(username, self) def get_tag(self, name): """ - Returns a tag object + Returns a tag object """ return Tag(name, self) def _get_language_domain(self, domain_language): """ - Returns the mapped domain name of the network to a DOMAIN_* value + Returns the mapped domain name of the network to a DOMAIN_* value """ if domain_language in self.domain_names: @@ -271,13 +271,13 @@ class _Network: def _get_ws_auth(self): """ - Returns an (API_KEY, API_SECRET, SESSION_KEY) tuple. + Returns an (API_KEY, API_SECRET, SESSION_KEY) tuple. """ return self.api_key, self.api_secret, self.session_key def _delay_call(self): """ - Makes sure that web service calls are at least 0.2 seconds apart. + Makes sure that web service calls are at least 0.2 seconds apart. """ now = time.time() @@ -1408,31 +1408,31 @@ class WSError(Exception): def get_id(self): """Returns the exception ID, from one of the following: - STATUS_INVALID_SERVICE = 2 - STATUS_INVALID_METHOD = 3 - STATUS_AUTH_FAILED = 4 - STATUS_INVALID_FORMAT = 5 - STATUS_INVALID_PARAMS = 6 - STATUS_INVALID_RESOURCE = 7 - STATUS_OPERATION_FAILED = 8 - STATUS_INVALID_SK = 9 - STATUS_INVALID_API_KEY = 10 - STATUS_OFFLINE = 11 - STATUS_SUBSCRIBERS_ONLY = 12 - STATUS_TOKEN_UNAUTHORIZED = 14 - STATUS_TOKEN_EXPIRED = 15 - STATUS_TEMPORARILY_UNAVAILABLE = 16 - STATUS_LOGIN_REQUIRED = 17 - STATUS_TRIAL_EXPIRED = 18 - STATUS_NOT_ENOUGH_CONTENT = 20 - STATUS_NOT_ENOUGH_MEMBERS = 21 - STATUS_NOT_ENOUGH_FANS = 22 - STATUS_NOT_ENOUGH_NEIGHBOURS = 23 - STATUS_NO_PEAK_RADIO = 24 - STATUS_RADIO_NOT_FOUND = 25 - STATUS_API_KEY_SUSPENDED = 26 - STATUS_DEPRECATED = 27 - STATUS_RATE_LIMIT_EXCEEDED = 29 + STATUS_INVALID_SERVICE = 2 + STATUS_INVALID_METHOD = 3 + STATUS_AUTH_FAILED = 4 + STATUS_INVALID_FORMAT = 5 + STATUS_INVALID_PARAMS = 6 + STATUS_INVALID_RESOURCE = 7 + STATUS_OPERATION_FAILED = 8 + STATUS_INVALID_SK = 9 + STATUS_INVALID_API_KEY = 10 + STATUS_OFFLINE = 11 + STATUS_SUBSCRIBERS_ONLY = 12 + STATUS_TOKEN_UNAUTHORIZED = 14 + STATUS_TOKEN_EXPIRED = 15 + STATUS_TEMPORARILY_UNAVAILABLE = 16 + STATUS_LOGIN_REQUIRED = 17 + STATUS_TRIAL_EXPIRED = 18 + STATUS_NOT_ENOUGH_CONTENT = 20 + STATUS_NOT_ENOUGH_MEMBERS = 21 + STATUS_NOT_ENOUGH_FANS = 22 + STATUS_NOT_ENOUGH_NEIGHBOURS = 23 + STATUS_NO_PEAK_RADIO = 24 + STATUS_RADIO_NOT_FOUND = 25 + STATUS_API_KEY_SUSPENDED = 26 + STATUS_DEPRECATED = 27 + STATUS_RATE_LIMIT_EXCEEDED = 29 """ return self.status @@ -2937,8 +2937,8 @@ def _url_safe(text): def _number(string): """ - Extracts an int from a string. - Returns a 0 if None or an empty string was passed. + Extracts an int from a string. + Returns a 0 if None or an empty string was passed. """ if not string: diff --git a/tests/test_artist.py b/tests/test_artist.py index 802a5e2..309537f 100755 --- a/tests/test_artist.py +++ b/tests/test_artist.py @@ -2,9 +2,10 @@ """ Integration (not unit) tests for pylast.py """ -import pylast import pytest +import pylast + from .test_pylast import WRITE_TEST, TestPyLastWithLastFm diff --git a/tests/test_librefm.py b/tests/test_librefm.py index cb8ddcc..6b0f3dd 100755 --- a/tests/test_librefm.py +++ b/tests/test_librefm.py @@ -2,9 +2,10 @@ """ Integration (not unit) tests for pylast.py """ -import pylast from flaky import flaky +import pylast + from .test_pylast import PyLastTestCase, load_secrets diff --git a/tests/test_network.py b/tests/test_network.py index bad8c54..3416260 100755 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -5,9 +5,10 @@ Integration (not unit) tests for pylast.py import re import time -import pylast import pytest +import pylast + from .test_pylast import WRITE_TEST, TestPyLastWithLastFm diff --git a/tests/test_pylast.py b/tests/test_pylast.py index 9ed1c7f..789afad 100755 --- a/tests/test_pylast.py +++ b/tests/test_pylast.py @@ -6,10 +6,11 @@ import os import sys import time -import pylast import pytest from flaky import flaky +import pylast + WRITE_TEST = sys.version_info[:2] == (3, 8) diff --git a/tests/test_track.py b/tests/test_track.py index 3bfe995..523498e 100755 --- a/tests/test_track.py +++ b/tests/test_track.py @@ -4,9 +4,10 @@ Integration (not unit) tests for pylast.py """ import time -import pylast import pytest +import pylast + from .test_pylast import WRITE_TEST, TestPyLastWithLastFm diff --git a/tests/test_user.py b/tests/test_user.py index 7f7e2e9..2428e69 100755 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -8,9 +8,10 @@ import os import re import warnings -import pylast import pytest +import pylast + from .test_pylast import TestPyLastWithLastFm diff --git a/tests/unicode_test.py b/tests/unicode_test.py index 7efcfea..7b3c271 100644 --- a/tests/unicode_test.py +++ b/tests/unicode_test.py @@ -1,8 +1,9 @@ from unittest import mock -import pylast import pytest +import pylast + def mock_network(): return mock.Mock(_get_ws_auth=mock.Mock(return_value=("", "", "")))