Merge pull request #281 from pylast/rm-2

Drop support for legacy Python 2
This commit is contained in:
Hugo 2018-12-29 23:44:36 +02:00 committed by GitHub
commit d63c0db472
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 80 deletions

View file

@ -15,12 +15,8 @@ env:
matrix: matrix:
include: include:
- python: 2.7
env: TOXENV=py2lint
- python: 2.7
env: TOXENV=py27
- python: 3.6 - python: 3.6
env: TOXENV=py3lint env: TOXENV=lint
- python: 3.7 - python: 3.7
env: TOXENV=py37 env: TOXENV=py37
dist: xenial dist: xenial
@ -33,13 +29,10 @@ matrix:
env: TOXENV=py34 env: TOXENV=py34
- python: pypy3 - python: pypy3
env: TOXENV=pypy3 env: TOXENV=pypy3
- python: pypy
env: TOXENV=pypy
- python: 3.8-dev - python: 3.8-dev
env: TOXENV=py38dev env: TOXENV=py38dev
dist: xenial dist: xenial
allow_failures: allow_failures:
- env: TOXENV=pypy
- env: TOXENV=pypy3 - env: TOXENV=pypy3
fast_finish: true fast_finish: true

View file

@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- This changelog file ([#273]) - This changelog file ([#273])
### Removed
* Support for Python 2.7 ([#265])
## [2.4.0] - 2018-08-08 ## [2.4.0] - 2018-08-08
### Deprecated ### Deprecated

View file

@ -29,12 +29,12 @@ Or from requirements.txt:
Note: Note:
* pylast >= 3.0.0 will likely only support Python 3.4+ ([#265](https://github.com/pylast/pylast/issues/265)) * pylast 3.0.0+ supports Python 3.4+ ([#265](https://github.com/pylast/pylast/issues/265))
* pyLast >= 2.2.0 supports Python 2.7.10+, 3.4, 3.5, 3.6, 3.7. * 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.2.0 supports Python 2.7.10+, 3.4, 3.5, 3.6. * pyLast 2.0.0 - 2.1.0 supports Python 2.7.10+, 3.4, 3.5, 3.6.
* pyLast >= 1.7.0 < 2.0.0 supports Python 2.7, 3.3, 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.7.0 supports Python 2.7, 3.3, 3.4. * pyLast 1.0.0 - 1.6.0 supports Python 2.7, 3.3, 3.4.
* pyLast >= 0.5 < 1.0.0 supports Python 2, 3. * pyLast 0.5 supports Python 2, 3.
* pyLast < 0.5 supports Python 2. * pyLast < 0.5 supports Python 2.
Features Features

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
clonedigger pylast
grep -E "Clones detected|lines are duplicates" output.html
exit 0

View file

@ -25,13 +25,13 @@ import collections
import hashlib import hashlib
import logging import logging
import shelve import shelve
import six
import ssl import ssl
import sys import sys
import tempfile import tempfile
import time import time
import warnings
import xml.dom import xml.dom
import html.entities
from http.client import HTTPSConnection
from . import version from . import version
@ -44,24 +44,33 @@ __email__ = "amr.hassan@gmail.com"
__version__ = version.__version__ __version__ = version.__version__
if sys.version_info < (3,): if sys.version_info < (3,):
warnings.warn( raise ImportError(
"You are using pylast with Python 2. " """pylast 3.0 and above are no longer compatible with Python 2.
"Pylast will soon be Python 3 only. "
"More info: https://github.com/pylast/pylast/issues/265", This is pylast {} and you are using Python {}.
UserWarning, Make sure you have pip >= 9.0 and setuptools >= 24.2 and retry:
$ pip install --upgrade pip setuptools
Other choices:
- Upgrade to Python 3.
- Install an older version of pylast:
$ pip install 'pylast<3.0'
For more information:
https://github.com/pylast/pylast/issues/265
""".format(
version, ".".join([str(v) for v in sys.version_info[:3]])
)
) )
if sys.version_info.major == 2:
import htmlentitydefs
from httplib import HTTPSConnection
from urllib import quote_plus as url_quote_plus
else: else:
import html.entities as htmlentitydefs # Keep importable on Python 2 for a while to show ImportError
from http.client import HTTPSConnection
from urllib.parse import quote_plus as url_quote_plus from urllib.parse import quote_plus as url_quote_plus
unichr = chr
STATUS_INVALID_SERVICE = 2 STATUS_INVALID_SERVICE = 2
STATUS_INVALID_METHOD = 3 STATUS_INVALID_METHOD = 3
@ -123,7 +132,7 @@ SCROBBLE_MODE_SKIPPED = "S"
# Delay time in seconds from section 4.4 of https://www.last.fm/api/tos # Delay time in seconds from section 4.4 of https://www.last.fm/api/tos
DELAY_TIME = 0.2 DELAY_TIME = 0.2
# Python >3.4 and >2.7.9 has sane defaults # Python >3.4 has sane defaults
SSL_CONTEXT = ssl.create_default_context() SSL_CONTEXT = ssl.create_default_context()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1118,10 +1127,10 @@ class _BaseObject(object):
def __hash__(self): def __hash__(self):
# Convert any ints (or whatever) into strings # Convert any ints (or whatever) into strings
values = map(six.text_type, self._get_params().values()) values = map(str, self._get_params().values())
return hash(self.network) + hash( return hash(self.network) + hash(
six.text_type(type(self)) str(type(self))
+ "".join(list(self._get_params().keys()) + list(values)).lower() + "".join(list(self._get_params().keys()) + list(values)).lower()
) )
@ -1649,7 +1658,7 @@ class Artist(_BaseObject, _Taggable):
return "pylast.Artist({}, {})".format(repr(self.get_name()), repr(self.network)) return "pylast.Artist({}, {})".format(repr(self.get_name()), repr(self.network))
def __unicode__(self): def __unicode__(self):
return six.text_type(self.get_name()) return str(self.get_name())
@_string_output @_string_output
def __str__(self): def __str__(self):
@ -2690,22 +2699,18 @@ def md5(text):
def _unicode(text): def _unicode(text):
if isinstance(text, six.binary_type): if isinstance(text, bytes):
return six.text_type(text, "utf-8") return str(text, "utf-8")
elif isinstance(text, six.text_type): elif isinstance(text, str):
return text return text
else: else:
return six.text_type(text) return str(text)
def _string(string): def _string(string):
"""For Python2 routines that can only process str type."""
if isinstance(string, str): if isinstance(string, str):
return string return string
casted = six.text_type(string) return str(string)
if sys.version_info.major == 2:
casted = casted.encode("utf-8")
return casted
def cleanup_nodes(doc): def cleanup_nodes(doc):
@ -2864,9 +2869,9 @@ def _unescape_htmlentity(string):
# string = _unicode(string) # string = _unicode(string)
mapping = htmlentitydefs.name2codepoint mapping = html.entities.name2codepoint
for key in mapping: for key in mapping:
string = string.replace("&%s;" % key, unichr(mapping[key])) string = string.replace("&%s;" % key, chr(mapping[key]))
return string return string

View file

@ -1,2 +1,2 @@
# Master version for pylast # Master version for pylast
__version__ = "2.5.0.dev0" __version__ = "3.0.0.dev0"

View file

@ -1,21 +1,50 @@
#!/usr/bin/env python #!/usr/bin/env python
from setuptools import find_packages, setup from __future__ import print_function
import sys
with open("README.md") as f: from setuptools import find_packages, setup
long_description = f.read()
version_dict = {} version_dict = {}
with open("pylast/version.py") as f: with open("pylast/version.py") as f:
exec(f.read(), version_dict) exec(f.read(), version_dict)
version = version_dict["__version__"] version = version_dict["__version__"]
if sys.version_info < (3, 4):
error = """pylast 3.0 and above are no longer compatible with Python 2.
This is pylast {} and you are using Python {}.
Make sure you have pip >= 9.0 and setuptools >= 24.2 and retry:
$ pip install --upgrade pip setuptools
Other choices:
- Upgrade to Python 3.
- Install an older version of pylast:
$ pip install 'pylast<3.0'
For more information:
https://github.com/pylast/pylast/issues/265
""".format(
version, ".".join([str(v) for v in sys.version_info[:3]])
)
print(error, file=sys.stderr)
sys.exit(1)
with open("README.md") as f:
long_description = f.read()
setup( setup(
name="pylast", name="pylast",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
version=version, version=version,
author="Amr Hassan <amr.hassan@gmail.com> and Contributors", author="Amr Hassan <amr.hassan@gmail.com> and Contributors",
install_requires=["six"],
tests_require=[ tests_require=[
"coverage", "coverage",
"flaky", "flaky",
@ -34,17 +63,16 @@ setup(
"Topic :: Internet", "Topic :: Internet",
"Topic :: Multimedia :: Sound/Audio", "Topic :: Multimedia :: Sound/Audio",
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
], ],
python_requires=">=2.7.10, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", python_requires=">=3.4",
keywords=["Last.fm", "music", "scrobble", "scrobbling"], keywords=["Last.fm", "music", "scrobble", "scrobbling"],
packages=find_packages(exclude=("tests*",)), packages=find_packages(exclude=("tests*",)),
license="Apache2", license="Apache2",

View file

@ -67,7 +67,7 @@ class TestPyLastUser(TestPyLastWithLastFm):
else: else:
# Old way # Old way
# Just check date because of timezones # Just check date because of timezones
self.assertIn(u"2002-11-20 ", registered) self.assertIn("2002-11-20 ", registered)
def test_get_user_unixtime_registration(self): def test_get_user_unixtime_registration(self):
# Arrange # Arrange

View file

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import mock import mock
import pytest import pytest
import six
import pylast import pylast
@ -13,9 +12,9 @@ def mock_network():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"artist", "artist",
[ [
u"\xe9lafdasfdsafdsa", "\xe9lafdasfdsafdsa",
u"ééééééé", "ééééééé",
pylast.Artist(u"B\xe9l", mock_network()), pylast.Artist("B\xe9l", mock_network()),
"fdasfdsafsaf not unicode", "fdasfdsafsaf not unicode",
], ],
) )
@ -24,7 +23,7 @@ def test_get_cache_key(artist):
request._get_cache_key() request._get_cache_key()
@pytest.mark.parametrize("obj", [pylast.Artist(u"B\xe9l", mock_network())]) @pytest.mark.parametrize("obj", [pylast.Artist("B\xe9l", mock_network())])
def test_cast_and_hash(obj): def test_cast_and_hash(obj):
assert type(six.text_type(obj)) is six.text_type assert type(str(obj)) is str
assert isinstance(hash(obj), int) assert isinstance(hash(obj), int)

18
tox.ini
View file

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27, py37, py36, py35, py34, pypy, pypy3, py38dev envlist = py37, py36, py35, py34, pypy3, py38dev
recreate = False recreate = False
[testenv] [testenv]
@ -25,21 +25,7 @@ commands = {posargs}
deps = deps =
flake8 flake8
pep8-naming pep8-naming
commands =
flake8 .
[testenv:py2lint]
deps =
{[testenv:lint]deps}
clonedigger
commands =
{[testenv:lint]commands}
./clonedigger.sh
[testenv:py3lint]
deps =
{[testenv:lint]deps}
black black
commands = commands =
{[testenv:lint]commands} flake8 .
black --check --diff . black --check --diff .