1
0
python-launchpadlib/0001-Remove-support-for-Python-2.patch

575 lines
18 KiB
Diff

From 27d18a156ea94fedb6556811b39dd6e9e349476e Mon Sep 17 00:00:00 2001
From: Colin Watson <cjwatson@ubuntu.com>
Date: Sun, 3 Mar 2024 16:07:27 +0000
Subject: [PATCH] Remove support for Python 2
I noticed that a number of the scripts in `contrib/` were Python-2-only,
so I did a basic untested port of those while I was here.
Apply pyupgrade --py3-plus
Simplify coverage testing
We no longer need the more complex arrangements after dropping Python 2
support.
---
NEWS.rst | 4 +
pyproject.toml | 2
setup.py | 4 -
src/launchpadlib/apps.py | 2
src/launchpadlib/credentials.py | 63 ++++++-----------------
src/launchpadlib/docs/conf.py | 16 ++---
src/launchpadlib/launchpad.py | 15 +----
src/launchpadlib/testing/helpers.py | 6 --
src/launchpadlib/testing/launchpad.py | 22 ++------
src/launchpadlib/testing/tests/test_launchpad.py | 4 -
src/launchpadlib/tests/test_credential_store.py | 8 --
src/launchpadlib/tests/test_http.py | 9 ---
src/launchpadlib/tests/test_launchpad.py | 12 +---
src/launchpadlib/uris.py | 7 --
14 files changed, 56 insertions(+), 118 deletions(-)
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -2,6 +2,10 @@
NEWS for launchpadlib
=====================
+2.0.0
+=====
+- Remove support for Python 2.
+
1.11.0 (2023-01-09)
===================
- Move the ``keyring`` dependency to a new ``keyring`` extra.
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,3 @@
[tool.black]
line-length = 79
-target-version = ['py27']
+target-version = ['py35']
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright 2008-2022 Canonical Ltd.
#
@@ -46,7 +46,6 @@ install_requires = [
'importlib-metadata; python_version < "3.8"',
"lazr.restfulclient>=0.14.2",
"lazr.uri",
- "six",
]
setup(
@@ -64,6 +63,7 @@ setup(
description=open("README.rst").readline().strip(),
long_description=generate("src/launchpadlib/docs/index.rst", "NEWS.rst"),
license="LGPL v3",
+ python_requires=">=3.5",
install_requires=install_requires,
url="https://help.launchpad.net/API/launchpadlib",
project_urls={
--- a/src/launchpadlib/apps.py
+++ b/src/launchpadlib/apps.py
@@ -30,7 +30,7 @@ from launchpadlib.credentials import Cre
from launchpadlib.uris import lookup_web_root
-class RequestTokenApp(object):
+class RequestTokenApp:
"""An application that creates request tokens."""
def __init__(self, web_root, consumer_name, context):
--- a/src/launchpadlib/credentials.py
+++ b/src/launchpadlib/credentials.py
@@ -14,11 +14,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with launchpadlib. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import print_function
-
"""launchpadlib credentials and authentication support."""
-__metaclass__ = type
__all__ = [
"AccessToken",
"AnonymousAccessToken",
@@ -29,40 +26,20 @@ __all__ = [
"Credentials",
]
-try:
- from cStringIO import StringIO
-except ImportError:
- from io import StringIO
-
+from base64 import (
+ b64decode,
+ b64encode,
+)
import httplib2
+from io import StringIO
import json
import os
from select import select
import stat
from sys import stdin
import time
-
-try:
- from urllib.parse import urlencode
-except ImportError:
- from urllib import urlencode
-try:
- from urllib.parse import urljoin
-except ImportError:
- from urlparse import urljoin
+from urllib.parse import urlencode, urljoin, parse_qs
import webbrowser
-from base64 import (
- b64decode,
- b64encode,
-)
-
-from six.moves.urllib.parse import parse_qs
-
-if bytes is str:
- # Python 2
- unicode_type = unicode # noqa: F821
-else:
- unicode_type = str
from lazr.restfulclient.errors import HTTPError
from lazr.restfulclient.authorize.oauth import (
@@ -135,7 +112,7 @@ class Credentials(OAuthAuthorizer):
sio = StringIO()
self.save(sio)
serialized = sio.getvalue()
- if isinstance(serialized, unicode_type):
+ if isinstance(serialized, str):
serialized = serialized.encode("utf-8")
return serialized
@@ -146,7 +123,7 @@ class Credentials(OAuthAuthorizer):
This should probably be moved into OAuthAuthorizer.
"""
credentials = cls()
- if not isinstance(value, unicode_type):
+ if not isinstance(value, str):
value = value.decode("utf-8")
credentials.load(StringIO(value))
return credentials
@@ -255,7 +232,7 @@ class AccessToken(_AccessToken):
@classmethod
def from_string(cls, query_string):
"""Create and return a new `AccessToken` from the given string."""
- if not isinstance(query_string, unicode_type):
+ if not isinstance(query_string, str):
query_string = query_string.decode("utf-8")
params = parse_qs(query_string, keep_blank_values=False)
key = params["oauth_token"]
@@ -280,10 +257,10 @@ class AnonymousAccessToken(_AccessToken)
"""
def __init__(self):
- super(AnonymousAccessToken, self).__init__("", "")
+ super().__init__("", "")
-class CredentialStore(object):
+class CredentialStore:
"""Store OAuth credentials locally.
This is a generic superclass. To implement a specific way of
@@ -369,7 +346,7 @@ class KeyringCredentialStore(CredentialS
B64MARKER = b"<B64>"
def __init__(self, credential_save_failed=None, fallback=False):
- super(KeyringCredentialStore, self).__init__(credential_save_failed)
+ super().__init__(credential_save_failed)
self._fallback = None
if fallback:
self._fallback = MemoryCredentialStore(credential_save_failed)
@@ -438,7 +415,7 @@ class KeyringCredentialStore(CredentialS
else:
raise
if credential_string is not None:
- if isinstance(credential_string, unicode_type):
+ if isinstance(credential_string, str):
credential_string = credential_string.encode("utf8")
if credential_string.startswith(self.B64MARKER):
try:
@@ -468,9 +445,7 @@ class UnencryptedFileCredentialStore(Cre
"""
def __init__(self, filename, credential_save_failed=None):
- super(UnencryptedFileCredentialStore, self).__init__(
- credential_save_failed
- )
+ super().__init__(credential_save_failed)
self.filename = filename
def do_save(self, credentials, unique_key):
@@ -495,7 +470,7 @@ class MemoryCredentialStore(CredentialSt
"""
def __init__(self, credential_save_failed=None):
- super(MemoryCredentialStore, self).__init__(credential_save_failed)
+ super().__init__(credential_save_failed)
self._credentials = {}
def do_save(self, credentials, unique_key):
@@ -507,7 +482,7 @@ class MemoryCredentialStore(CredentialSt
return self._credentials.get(unique_key)
-class RequestTokenAuthorizationEngine(object):
+class RequestTokenAuthorizationEngine:
"""The superclass of all request token authorizers.
This base class does not implement request token authorization,
@@ -774,15 +749,13 @@ class AuthorizeRequestTokenWithBrowser(A
# It doesn't look like we're doing anything here, but we
# are discarding the passed-in values for consumer_name and
# allow_access_levels.
- super(AuthorizeRequestTokenWithBrowser, self).__init__(
+ super().__init__(
service_root, application_name, None, credential_save_failed
)
def notify_end_user_authorization_url(self, authorization_url):
"""Notify the end-user of the URL."""
- super(
- AuthorizeRequestTokenWithBrowser, self
- ).notify_end_user_authorization_url(authorization_url)
+ super().notify_end_user_authorization_url(authorization_url)
try:
browser_obj = webbrowser.get()
--- a/src/launchpadlib/docs/conf.py
+++ b/src/launchpadlib/docs/conf.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
# launchpadlib documentation build configuration file, created by
# sphinx-quickstart on Tue Nov 5 23:48:15 2019.
#
@@ -47,9 +45,9 @@ source_suffix = ".rst"
master_doc = "index"
# General information about the project.
-project = u"launchpadlib"
-copyright = u"2008-2019, Canonical Ltd."
-author = u"LAZR Developers <lazr-developers@lists.launchpad.net>"
+project = "launchpadlib"
+copyright = "2008-2019, Canonical Ltd."
+author = "LAZR Developers <lazr-developers@lists.launchpad.net>"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -140,8 +138,8 @@ latex_documents = [
(
master_doc,
"launchpadlib.tex",
- u"launchpadlib Documentation",
- u"LAZR Developers \\textless{}lazr-developers@lists.launchpad.net\\textgreater{}", # noqa: E501
+ "launchpadlib Documentation",
+ "LAZR Developers \\textless{}lazr-developers@lists.launchpad.net\\textgreater{}", # noqa: E501
"manual",
),
]
@@ -152,7 +150,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, "launchpadlib", u"launchpadlib Documentation", [author], 1)
+ (master_doc, "launchpadlib", "launchpadlib Documentation", [author], 1)
]
@@ -165,7 +163,7 @@ texinfo_documents = [
(
master_doc,
"launchpadlib",
- u"launchpadlib Documentation",
+ "launchpadlib Documentation",
author,
"launchpadlib",
"One line description of project.",
--- a/src/launchpadlib/launchpad.py
+++ b/src/launchpadlib/launchpad.py
@@ -16,18 +16,13 @@
"""Root Launchpad API class."""
-__metaclass__ = type
__all__ = [
"Launchpad",
]
import errno
import os
-
-try:
- from urllib.parse import urlsplit
-except ImportError:
- from urlparse import urlsplit
+from urllib.parse import urlsplit
import warnings
try:
@@ -130,7 +125,7 @@ class LaunchpadOAuthAwareHttp(RestfulHtt
def __init__(self, launchpad, authorization_engine, *args):
self.launchpad = launchpad
self.authorization_engine = authorization_engine
- super(LaunchpadOAuthAwareHttp, self).__init__(*args)
+ super().__init__(*args)
def _bad_oauth_token(self, response, content):
"""Helper method to detect an error caused by a bad OAuth token."""
@@ -141,9 +136,7 @@ class LaunchpadOAuthAwareHttp(RestfulHtt
)
def _request(self, *args):
- response, content = super(LaunchpadOAuthAwareHttp, self)._request(
- *args
- )
+ response, content = super()._request(*args)
return self.retry_on_bad_token(response, content, *args)
def retry_on_bad_token(self, response, content, *args):
@@ -227,7 +220,7 @@ class Launchpad(ServiceRoot):
# case we need to authorize a new token during use.
self.authorization_engine = authorization_engine
- super(Launchpad, self).__init__(
+ super().__init__(
credentials, service_root, cache, timeout, proxy_info, version
)
--- a/src/launchpadlib/testing/helpers.py
+++ b/src/launchpadlib/testing/helpers.py
@@ -18,8 +18,6 @@
"""launchpadlib testing helpers."""
-
-__metaclass__ = type
__all__ = [
"BadSaveKeyring",
"fake_keyring",
@@ -64,7 +62,7 @@ class NoNetworkAuthorizationEngine(Reque
ACCESS_TOKEN_KEY = "access_key:84"
def __init__(self, *args, **kwargs):
- super(NoNetworkAuthorizationEngine, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
# Set up some instrumentation.
self.request_tokens_obtained = 0
self.access_tokens_obtained = 0
@@ -144,7 +142,7 @@ class TestableLaunchpad(Launchpad):
generally pass in fully-formed Credentials objects.
:param service_root: Defaults to 'test_dev'.
"""
- super(TestableLaunchpad, self).__init__(
+ super().__init__(
credentials,
authorization_engine,
credential_store,
--- a/src/launchpadlib/testing/launchpad.py
+++ b/src/launchpadlib/testing/launchpad.py
@@ -65,23 +65,15 @@ Where 'https://api.launchpad.net/devel/'
also in the WADL file itelf.
"""
+from collections.abc import Callable
from datetime import datetime
-try:
- from collections.abc import Callable
-except ImportError:
- from collections import Callable
-import sys
-
-if sys.version_info[0] >= 3:
- basestring = str
-
class IntegrityError(Exception):
"""Raised when bad sample data is used with a L{FakeLaunchpad} instance."""
-class FakeLaunchpad(object):
+class FakeLaunchpad:
"""A fake Launchpad API class for unit tests that depend on L{Launchpad}.
@param application: A C{wadllib.application.Application} instance for a
@@ -188,7 +180,7 @@ def wadl_tag(tag_name):
return "{http://research.sun.com/wadl/2006/10}" + tag_name
-class FakeResource(object):
+class FakeResource:
"""
Represents valid sample data on L{FakeLaunchpad} instances.
@@ -434,7 +426,7 @@ class FakeResource(object):
if param is None:
raise IntegrityError("%s not found" % name)
if param.type is None:
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
raise IntegrityError(
"%s is not a str or unicode for %s" % (value, name)
)
@@ -594,7 +586,7 @@ class FakeRoot(FakeResource):
resource_type = application.get_resource_type(
application.markup_url + "#service-root"
)
- super(FakeRoot, self).__init__(application, resource_type)
+ super().__init__(application, resource_type)
class FakeEntry(FakeResource):
@@ -612,9 +604,7 @@ class FakeCollection(FakeResource):
name=None,
child_resource_type=None,
):
- super(FakeCollection, self).__init__(
- application, resource_type, values
- )
+ super().__init__(application, resource_type, values)
self.__dict__.update(
{"_name": name, "_child_resource_type": child_resource_type}
)
--- a/src/launchpadlib/testing/tests/test_launchpad.py
+++ b/src/launchpadlib/testing/tests/test_launchpad.py
@@ -160,8 +160,8 @@ class FakeLaunchpadTest(ResourcedTestCas
dicts that represent objects. Plain string values can be represented
as C{unicode} strings.
"""
- self.launchpad.me = dict(name=u"foo")
- self.assertEqual(u"foo", self.launchpad.me.name)
+ self.launchpad.me = dict(name="foo")
+ self.assertEqual("foo", self.launchpad.me.name)
def test_datetime_property(self):
"""
--- a/src/launchpadlib/tests/test_credential_store.py
+++ b/src/launchpadlib/tests/test_credential_store.py
@@ -169,9 +169,7 @@ class TestKeyringCredentialStore(Credent
# handled correctly. (See bug lp:877374)
class UnicodeInMemoryKeyring(InMemoryKeyring):
def get_password(self, service, username):
- password = super(UnicodeInMemoryKeyring, self).get_password(
- service, username
- )
+ password = super().get_password(service, username)
if isinstance(password, unicode_type):
password = password.encode("utf-8")
return password
@@ -194,9 +192,7 @@ class TestKeyringCredentialStore(Credent
class UnencodedInMemoryKeyring(InMemoryKeyring):
def get_password(self, service, username):
- pw = super(UnencodedInMemoryKeyring, self).get_password(
- service, username
- )
+ pw = super().get_password(service, username)
return b64decode(pw[5:])
self.keyring = UnencodedInMemoryKeyring()
--- a/src/launchpadlib/tests/test_http.py
+++ b/src/launchpadlib/tests/test_http.py
@@ -17,15 +17,10 @@
"""Tests for the LaunchpadOAuthAwareHTTP class."""
from collections import deque
-from json import dumps
+from json import dumps, JSONDecodeError
import tempfile
import unittest
-try:
- from json import JSONDecodeError
-except ImportError:
- JSONDecodeError = ValueError
-
from launchpadlib.errors import Unauthorized
from launchpadlib.credentials import UnencryptedFileCredentialStore
from launchpadlib.launchpad import (
@@ -75,7 +70,7 @@ class SimulatedResponsesHttp(LaunchpadOA
:param responses: A list of HttpResponse objects to use
in response to requests.
"""
- super(SimulatedResponsesHttp, self).__init__(*args)
+ super().__init__(*args)
self.sent_responses = []
self.unsent_responses = responses
self.cache = None
--- a/src/launchpadlib/tests/test_launchpad.py
+++ b/src/launchpadlib/tests/test_launchpad.py
@@ -16,8 +16,6 @@
"""Tests for the Launchpad class."""
-__metaclass__ = type
-
from contextlib import contextmanager
import os
import shutil
@@ -25,11 +23,7 @@ import socket
import stat
import tempfile
import unittest
-
-try:
- from unittest.mock import patch
-except ImportError:
- from mock import patch
+from unittest.mock import patch
import warnings
from lazr.restfulclient.resource import ServiceRoot
@@ -351,11 +345,11 @@ class TestLaunchpadLoginWith(KeyringTest
"""Tests for Launchpad.login_with()."""
def setUp(self):
- super(TestLaunchpadLoginWith, self).setUp()
+ super().setUp()
self.temp_dir = tempfile.mkdtemp()
def tearDown(self):
- super(TestLaunchpadLoginWith, self).tearDown()
+ super().tearDown()
shutil.rmtree(self.temp_dir)
def test_dirs_created(self):
--- a/src/launchpadlib/uris.py
+++ b/src/launchpadlib/uris.py
@@ -20,18 +20,15 @@ The code in this module lets users say "
"https://api.staging.launchpad.net/".
"""
-__metaclass__ = type
__all__ = [
"lookup_service_root",
"lookup_web_root",
"web_root_for_service_root",
]
-try:
- from urllib.parse import urlparse
-except ImportError:
- from urlparse import urlparse
+from urllib.parse import urlparse
import warnings
+
from lazr.uri import URI
LPNET_SERVICE_ROOT = "https://api.launchpad.net/"