diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08820fb9..eb16eb18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,14 +15,12 @@ jobs: fail-fast: false matrix: python-version: - - "2.7" - "3.4" - "3.5" - "3.6" - "3.7" - "3.8" - "3.9" - - "pypy2" - "pypy3" steps: - uses: actions/checkout@v2 diff --git a/README.rst b/README.rst index c64c4a87..692b8935 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ library. ========================= ======================================== Current version 2.2.0 -Supported Python versions 2.7, 3.4 - 3.9 +Supported Python versions 3.4 - 3.9 License New BSD Project home https://github.com/mjs/imapclient/ PyPI https://pypi.python.org/pypi/IMAPClient diff --git a/doc/src/concepts.rst b/doc/src/concepts.rst index 66f878b5..c69297ea 100644 --- a/doc/src/concepts.rst +++ b/doc/src/concepts.rst @@ -102,13 +102,6 @@ When constructing a custom context it is usually best to start with the default context, created by the ``ssl`` module, and modify it to suit your needs. -.. warning:: - - Users of Python 2.7.0 - 2.7.8 can use TLS but cannot configure - the settings via an ``ssl.SSLContext``. These Python versions are - also not capable of proper certification verification. It is highly - encouraged to upgrade to a more recent version of Python. - The following example shows how to to disable certification verification and certificate host name checks if required. diff --git a/doc/src/index.rst b/doc/src/index.rst index f0847b76..11d9c7cf 100644 --- a/doc/src/index.rst +++ b/doc/src/index.rst @@ -30,7 +30,7 @@ explains IMAP in detail. Other RFCs also apply to various extensions to the base protocol. These are referred to in the documentation below where relevant. -Python versions 2.7 and 3.4 through 3.9 are officially supported. +Python versions 3.4 through 3.9 are officially supported. Getting Started --------------- diff --git a/imapclient/config.py b/imapclient/config.py index 78a02043..6469fc80 100644 --- a/imapclient/config.py +++ b/imapclient/config.py @@ -8,10 +8,9 @@ from os import environ, path import ssl -from six import iteritems -from six.moves.configparser import SafeConfigParser, NoOptionError -from six.moves.urllib.request import urlopen -from six.moves.urllib.parse import urlencode +import configparser +import urllib.parse +import urllib.request import imapclient @@ -45,7 +44,7 @@ def parse_config_file(filename): Used by livetest.py and interact.py """ - parser = SafeConfigParser(get_string_config_defaults()) + parser = configparser.SafeConfigParser(get_string_config_defaults()) with open(filename, "r") as fh: parser.readfp(fh) @@ -62,7 +61,7 @@ def parse_config_file(filename): def get_string_config_defaults(): out = {} - for k, v in iteritems(get_config_defaults()): + for k, v in get_config_defaults().items(): if v is True: v = "true" elif v is False: @@ -80,7 +79,7 @@ def _read_config_section(parser, section): def get_allowing_none(name, typefunc): try: v = parser.get(section, name) - except NoOptionError: + except configparser.NoOptionError: return None if not v: return None @@ -133,7 +132,8 @@ def refresh_oauth2_token(hostname, client_id, client_secret, refresh_token): refresh_token=refresh_token.encode("ascii"), grant_type=b"refresh_token", ) - response = urlopen(url, urlencode(post).encode("ascii")).read() + response = urllib.request.urlopen( + url, urllib.parse.urlencode(post).encode("ascii")).read() return json.loads(response.decode("ascii"))["access_token"] diff --git a/imapclient/imap_utf7.py b/imapclient/imap_utf7.py index 1e1780fc..e92dddc0 100644 --- a/imapclient/imap_utf7.py +++ b/imapclient/imap_utf7.py @@ -9,7 +9,6 @@ from __future__ import unicode_literals import binascii -from six import binary_type, text_type, byte2int, iterbytes, unichr def encode(s): @@ -18,7 +17,7 @@ def encode(s): Input is unicode; output is bytes (Python 3) or str (Python 2). If non-unicode input is provided, the input is returned unchanged. """ - if not isinstance(s, text_type): + if not isinstance(s, str): return s res = bytearray() @@ -56,8 +55,8 @@ def consume_b64_buffer(buf): return bytes(res) -AMPERSAND_ORD = byte2int(b"&") -DASH_ORD = byte2int(b"-") +AMPERSAND_ORD = ord("&") +DASH_ORD = ord("-") def decode(s): @@ -67,13 +66,13 @@ def decode(s): unicode. If non-bytes/str input is provided, the input is returned unchanged. """ - if not isinstance(s, binary_type): + if not isinstance(s, bytes): return s res = [] # Store base64 substring that will be decoded once stepping on end shift character b64_buffer = bytearray() - for c in iterbytes(s): + for c in s: # Shift character without anything in buffer -> starts storing base64 substring if c == AMPERSAND_ORD and not b64_buffer: b64_buffer.append(c) @@ -90,7 +89,7 @@ def decode(s): b64_buffer.append(c) # No buffer initialized yet, should be an ASCII printable char else: - res.append(unichr(c)) + res.append(chr(c)) # Decode the remaining buffer if any if b64_buffer: diff --git a/imapclient/imapclient.py b/imapclient/imapclient.py index 029da4d8..959a4eaa 100644 --- a/imapclient/imapclient.py +++ b/imapclient/imapclient.py @@ -17,8 +17,6 @@ from operator import itemgetter from logging import LoggerAdapter, getLogger -from six import moves, iteritems, text_type, integer_types, PY3, binary_type, iterbytes - from . import exceptions from . import imap4 from . import response_lexer @@ -28,8 +26,6 @@ from .response_parser import parse_response, parse_message_list, parse_fetch_response from .util import to_bytes, to_unicode, assert_imap_protocol, chunk -xrange = moves.xrange - try: from select import poll @@ -38,9 +34,6 @@ # Fallback to select() on systems that don't support poll() POLL_SUPPORT = False -if PY3: - long = int # long is just int in python3 - logger = getLogger(__name__) @@ -744,11 +737,11 @@ def _proc_folder_list(self, folder_data): ret = [] parsed = parse_response(folder_data) for flags, delim, name in chunk(parsed, size=3): - if isinstance(name, (int, long)): + if isinstance(name, int): # Some IMAP implementations return integer folder names # with quotes. These get parsed to ints so convert them # back to strings. - name = text_type(name) + name = str(name) elif self.folder_encode: name = decode_utf7(name) @@ -838,7 +831,7 @@ def _process_select_response(self, resp): if key == b"PERMANENTFLAGS": out[key] = tuple(match.group("data").split()) - for key, value in iteritems(untagged): + for key, value in untagged.items(): key = key.upper() if key in (b"OK", b"PERMANENTFLAGS"): continue # already handled above @@ -1190,7 +1183,7 @@ def sort(self, sort_criteria, criteria="ALL", charset="UTF-8"): ] args.extend(_normalise_search_criteria(criteria, charset)) ids = self._raw_command_untagged(b"SORT", args, unpack=True) - return [long(i) for i in ids.split()] + return [int(i) for i in ids.split()] def thread(self, algorithm="REFERENCES", criteria="ALL", charset="UTF-8"): """Return a list of messages threads from the currently @@ -1276,7 +1269,7 @@ def get_gmail_labels(self, messages): response = self.fetch(messages, [b"X-GM-LABELS"]) response = self._filter_fetch_dict(response, b"X-GM-LABELS") return { - msg: utf7_decode_sequence(labels) for msg, labels in iteritems(response) + msg: utf7_decode_sequence(labels) for msg, labels in response.items() } def add_gmail_labels(self, messages, labels, silent=False): @@ -1405,10 +1398,7 @@ def append(self, folder, msg, flags=(), msg_time=None): """ if msg_time: time_val = '"%s"' % datetime_to_INTERNALDATE(msg_time) - if PY3: - time_val = to_unicode(time_val) - else: - time_val = to_bytes(time_val) + time_val = to_unicode(time_val) else: time_val = None return self._command_and_check( @@ -1528,7 +1518,7 @@ def getacl(self, folder): data = self._command_and_check("getacl", self._normalise_folder(folder)) parts = list(response_lexer.TokenSource(data)) parts = parts[1:] # First item is folder name - return [(parts[i], parts[i + 1]) for i in xrange(0, len(parts), 2)] + return [(parts[i], parts[i + 1]) for i in range(0, len(parts), 2)] @require_capability("ACL") def setacl(self, folder, who, what): @@ -1730,8 +1720,7 @@ def _command_and_check(self, command, *args, **kwargs): assert not kwargs, "unexpected keyword args: " + ", ".join(kwargs) if uid and self.use_uid: - if PY3: - command = to_unicode(command) # imaplib must die + command = to_unicode(command) # imaplib must die typ, data = self._imap.uid(command, *args) else: meth = getattr(self._imap, to_unicode(command)) @@ -1749,7 +1738,7 @@ def _gm_label_store(self, cmd, messages, labels, silent): cmd, messages, self._normalise_labels(labels), b"X-GM-LABELS", silent=silent ) return ( - {msg: utf7_decode_sequence(labels) for msg, labels in iteritems(response)} + {msg: utf7_decode_sequence(labels) for msg, labels in response.items()} if response else None ) @@ -1772,17 +1761,17 @@ def _store(self, cmd, messages, flags, fetch_key, silent): return self._filter_fetch_dict(parse_fetch_response(data), fetch_key) def _filter_fetch_dict(self, fetch_dict, key): - return dict((msgid, data[key]) for msgid, data in iteritems(fetch_dict)) + return dict((msgid, data[key]) for msgid, data in fetch_dict.items()) def _normalise_folder(self, folder_name): - if isinstance(folder_name, binary_type): + if isinstance(folder_name, bytes): folder_name = folder_name.decode("ascii") if self.folder_encode: folder_name = encode_utf7(folder_name) return _quote(folder_name) def _normalise_labels(self, labels): - if isinstance(labels, (text_type, binary_type)): + if isinstance(labels, (str, bytes)): labels = (labels,) return [_quote(encode_utf7(l)) for l in labels] @@ -1796,7 +1785,7 @@ def welcome(self): def _quote(arg): - if isinstance(arg, text_type): + if isinstance(arg, str): arg = arg.replace("\\", "\\\\") arg = arg.replace('"', '\\"') q = '"' @@ -1813,7 +1802,7 @@ def _normalise_search_criteria(criteria, charset=None): if not charset: charset = "us-ascii" - if isinstance(criteria, (text_type, binary_type)): + if isinstance(criteria, (str, bytes)): return [to_bytes(criteria, charset)] out = [] @@ -1834,7 +1823,7 @@ def _normalise_search_criteria(criteria, charset=None): def _normalise_sort_criteria(criteria, charset=None): - if isinstance(criteria, (text_type, binary_type)): + if isinstance(criteria, (str, bytes)): criteria = [criteria] return b"(" + b" ".join(to_bytes(item).upper() for item in criteria) + b")" @@ -1845,7 +1834,7 @@ class _literal(bytes): pass -class _quoted(binary_type): +class _quoted(bytes): """ This class holds a quoted bytes value which provides access to the unquoted value via the *original* attribute. @@ -1892,7 +1881,7 @@ def _join_and_paren(items): def _normalise_text_list(items): - if isinstance(items, (text_type, binary_type)): + if isinstance(items, (str, bytes)): items = (items,) return (to_unicode(c) for c in items) @@ -1901,14 +1890,14 @@ def join_message_ids(messages): """Convert a sequence of messages ids or a single integer message id into an id byte string for use with IMAP commands """ - if isinstance(messages, (text_type, binary_type, integer_types)): + if isinstance(messages, (str, bytes, int)): messages = (to_bytes(messages),) return b",".join(_maybe_int_to_bytes(m) for m in messages) def _maybe_int_to_bytes(val): - if isinstance(val, integer_types): - return str(val).encode("us-ascii") if PY3 else str(val) + if isinstance(val, int): + return str(val).encode("us-ascii") return to_bytes(val) @@ -1943,7 +1932,7 @@ def as_triplets(items): def _is8bit(data): - return isinstance(data, _literal) or any(b > 127 for b in iterbytes(data)) + return isinstance(data, _literal) or any(b > 127 for b in data) def _iter_with_last(items): @@ -1964,7 +1953,7 @@ def __init__(self, d): self._d = d def iteritems(self): - for key, value in iteritems(self._d): + for key, value in self._d.items(): yield to_bytes(key), value # For Python 3 compatibility. @@ -1998,7 +1987,7 @@ def pop(self, ink, default=_not_present): def _gen_keys(self, k): yield k - if isinstance(k, binary_type): + if isinstance(k, bytes): yield to_unicode(k) else: yield to_bytes(k) @@ -2037,7 +2026,7 @@ class IMAPlibLoggerAdapter(LoggerAdapter): def process(self, msg, kwargs): # msg is usually unicode but see #367. Convert bytes to # unicode if required. - if isinstance(msg, binary_type): + if isinstance(msg, bytes): msg = msg.decode("ascii", "ignore") for command in ("LOGIN", "AUTHENTICATE"): diff --git a/imapclient/interact.py b/imapclient/interact.py index 33e40005..44a48474 100644 --- a/imapclient/interact.py +++ b/imapclient/interact.py @@ -10,8 +10,6 @@ from getpass import getpass from optparse import OptionParser -from six import iteritems - from .config import parse_config_file, create_client_from_config, get_config_defaults @@ -93,7 +91,7 @@ def command_line(): # Scan through options, filling in defaults and prompting when # a compulsory option wasn't provided. compulsory_opts = ("host", "username", "password") - for name, default_value in iteritems(get_config_defaults()): + for name, default_value in get_config_defaults().items(): value = getattr(opts, name, default_value) if name in compulsory_opts and value is None: value = getpass(name + ": ") diff --git a/imapclient/response_lexer.py b/imapclient/response_lexer.py index bb1ffcaa..9d221c8f 100644 --- a/imapclient/response_lexer.py +++ b/imapclient/response_lexer.py @@ -11,17 +11,15 @@ from __future__ import unicode_literals -import six - from .util import assert_imap_protocol __all__ = ["TokenSource"] CTRL_CHARS = frozenset(c for c in range(32)) ALL_CHARS = frozenset(c for c in range(256)) -SPECIALS = frozenset(c for c in six.iterbytes(b' ()%"[')) +SPECIALS = frozenset(c for c in b' ()%"[') NON_SPECIALS = ALL_CHARS - SPECIALS - CTRL_CHARS -WHITESPACE = frozenset(c for c in six.iterbytes(b" \t\r\n")) +WHITESPACE = frozenset(c for c in b" \t\r\n") BACKSLASH = ord("\\") OPEN_SQUARE = ord("[") @@ -146,7 +144,7 @@ def __init__(self, resp_record): self.literal = None def __iter__(self): - return PushableIterator(six.iterbytes(self.src_text)) + return PushableIterator(self.src_text) class PushableIterator(object): diff --git a/imapclient/response_parser.py b/imapclient/response_parser.py index 9fbfeec8..1a72f744 100644 --- a/imapclient/response_parser.py +++ b/imapclient/response_parser.py @@ -17,15 +17,11 @@ import sys from collections import defaultdict -import six - from .datetime_util import parse_to_datetime from .response_lexer import TokenSource from .response_types import BodyData, Envelope, Address, SearchIds from .exceptions import ProtocolError -xrange = six.moves.xrange - __all__ = ["parse_response", "parse_message_list"] @@ -60,7 +56,7 @@ def parse_message_list(data): if not data: return SearchIds() - if six.PY3 and isinstance(data, six.binary_type): + if isinstance(data, bytes): data = data.decode("ascii") m = _msg_id_pattern.match(data) @@ -133,7 +129,7 @@ def parse_fetch_response(text, normalise_times=True, uid_is_key=True): # always return the sequence of the message, so it is available # even if we return keyed by UID. msg_data = {b"SEQ": seq} - for i in xrange(0, len(msg_response), 2): + for i in range(0, len(msg_response), 2): word = msg_response[i].upper() value = msg_response[i + 1] diff --git a/imapclient/response_types.py b/imapclient/response_types.py index b634d2aa..fe96024e 100644 --- a/imapclient/response_types.py +++ b/imapclient/response_types.py @@ -7,8 +7,6 @@ from collections import namedtuple from email.utils import formataddr -import six - from .util import to_unicode @@ -120,7 +118,7 @@ def create(cls, response): if isinstance(response[0], tuple): # Multipart, find where the message part tuples stop for i, part in enumerate(response): - if isinstance(part, six.binary_type): + if isinstance(part, bytes): break return cls(([cls.create(part) for part in response[:i]],) + response[i:]) else: diff --git a/imapclient/util.py b/imapclient/util.py index 3773ac83..0f578501 100644 --- a/imapclient/util.py +++ b/imapclient/util.py @@ -4,9 +4,7 @@ from __future__ import unicode_literals -import six import logging -from six import binary_type, text_type from . import exceptions @@ -14,7 +12,7 @@ def to_unicode(s): - if isinstance(s, binary_type): + if isinstance(s, bytes): try: return s.decode("ascii") except UnicodeDecodeError: @@ -28,7 +26,7 @@ def to_unicode(s): def to_bytes(s, charset="ascii"): - if isinstance(s, text_type): + if isinstance(s, str): return s.encode(charset) return s @@ -42,5 +40,5 @@ def assert_imap_protocol(condition, message=None): def chunk(lst, size): - for i in six.moves.range(0, len(lst), size): + for i in range(0, len(lst), size): yield lst[i : i + size] diff --git a/livetest.py b/livetest.py index 61f3c876..1434a3a5 100644 --- a/livetest.py +++ b/livetest.py @@ -19,8 +19,6 @@ from datetime import datetime from email.utils import make_msgid -from six import binary_type, text_type, PY3, iteritems - from imapclient.config import parse_config_file, create_client_from_config from imapclient.exceptions import IMAPClientError from imapclient.fixed_offset import FixedOffset @@ -170,7 +168,7 @@ def clear_folder(self, folder): self.client.expunge() def add_prefix_to_folder(self, folder): - if isinstance(folder, binary_type): + if isinstance(folder, bytes): return ( self.base_folder.encode("ascii") + self.folder_delimiter.encode("ascii") @@ -219,7 +217,7 @@ class TestGeneral(_TestBase): def test_alternates(self): # Check alternate connection/login configurations. - for name, conf in iteritems(self.conf.alternates): + for name, conf in self.conf.alternates.items(): try: client = create_client_from_config(conf) client.logout() @@ -446,13 +444,13 @@ def test_rename_folder(self): for folder in folders: self.client.create_folder(folder) - if isinstance(folder, binary_type): + if isinstance(folder, bytes): new_folder = folder + b"x" else: new_folder = folder + "x" resp = self.client.rename_folder(folder, new_folder) - self.assertIsInstance(resp, binary_type) + self.assertIsInstance(resp, bytes) self.assertTrue(len(resp) > 0) self.assertFalse(self.client.folder_exists(folder)) @@ -510,7 +508,7 @@ def test_idle(self): finally: text, more_responses = self.client.idle_done() self.assertIn((1, b"EXISTS"), responses) - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) self.assertTrue(isinstance(more_responses, list)) @@ -529,7 +527,7 @@ def test_idle(self): return self.assertIn((2, b"EXISTS"), responses) - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) def test_noop(self): @@ -537,7 +535,7 @@ def test_noop(self): # Initially there should be no responses text, resps = self.client.noop() - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) self.assertEqual(resps, []) @@ -549,7 +547,7 @@ def test_noop(self): # Check for this addition in the NOOP data msg, resps = self.client.noop() - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) self.assertTrue(isinstance(resps, list)) self.assertIn((1, b"EXISTS"), resps) @@ -614,7 +612,7 @@ def check_append(self, in_message, out_message): resp = self.client.append( self.base_folder, in_message, ("abc", "def"), msg_time ) - self.assertIsInstance(resp, binary_type) + self.assertIsInstance(resp, bytes) # Retrieve the just added message and check that all looks well self.assertEqual(self.client.select_folder(self.base_folder)[b"EXISTS"], 1) @@ -1068,7 +1066,7 @@ def test_expunge(self): # Test empty mailbox text, resps = self.client.expunge() - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) # Some servers return nothing while others (e.g. Exchange) return (0, 'EXISTS') self.assertIn(resps, ([], [(0, b"EXISTS")])) @@ -1078,7 +1076,7 @@ def test_expunge(self): msg, resps = self.client.expunge() - self.assertTrue(isinstance(text, binary_type)) + self.assertTrue(isinstance(text, bytes)) self.assertGreater(len(text), 0) self.assertTrue(isinstance(resps, list)) if not self.is_gmail(): @@ -1138,7 +1136,7 @@ def quiet_logout(client): def maybe_lower(val): - if isinstance(val, (text_type, binary_type)): + if isinstance(val, (str, bytes)): return val.lower() return val @@ -1197,8 +1195,6 @@ def add_test_class(klass, name=None): if name is None: name = klass.__name__ else: - if not PY3: - name = name.encode("ascii") klass.__name__ = name setattr(live_test_mod, name, klass) diff --git a/setup.py b/setup.py index cc0efb6e..28e6239b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2017, Menno Smits # Released subject to the New BSD License @@ -9,17 +9,11 @@ from setuptools import setup -MAJ_MIN_MIC = sys.version_info[:3] -IS_PY3 = MAJ_MIN_MIC >= (3, 0, 0) - # Read version info here = path.dirname(__file__) version_file = path.join(here, "imapclient", "version.py") info = {} -if IS_PY3: - exec(open(version_file).read(), {}, info) -else: - execfile(version_file, {}, info) +exec(open(version_file).read(), {}, info) desc = """\ IMAPClient is an easy-to-use, Pythonic and complete IMAP client library. @@ -33,14 +27,12 @@ * Convenience methods are provided for commonly used functionality. * Exceptions are raised when errors occur. -Python versions 2.7 and 3.4 through 3.9 are officially supported. +Python versions 3.4 through 3.9 are officially supported. IMAPClient includes comprehensive units tests and automated functional tests that can be run against a live IMAP server. """ -main_deps = ["six"] -test_deps = ['mock>=1.3.0; python_version < "3.4"'] doc_deps = ["sphinx"] setup( diff --git a/tests/test_imap_utf7.py b/tests/test_imap_utf7.py index 295aada1..a70f4857 100644 --- a/tests/test_imap_utf7.py +++ b/tests/test_imap_utf7.py @@ -4,8 +4,6 @@ from __future__ import unicode_literals -from six import text_type, binary_type, int2byte, unichr - from imapclient.imap_utf7 import decode, encode from tests.util import unittest @@ -30,13 +28,13 @@ class IMAP4UTF7TestCase(unittest.TestCase): def test_encode(self): for (input, output) in self.tests: encoded = encode(input) - self.assertIsInstance(encoded, binary_type) + self.assertIsInstance(encoded, bytes) self.assertEqual(encoded, output) def test_decode(self): for (input, output) in self.tests: decoded = decode(output) - self.assertIsInstance(decoded, text_type) + self.assertIsInstance(decoded, str) self.assertEqual(input, decoded) def test_printable_singletons(self): @@ -46,8 +44,8 @@ def test_printable_singletons(self): """ # All printables represent themselves for o in list(range(0x20, 0x26)) + list(range(0x27, 0x7F)): - self.assertEqual(int2byte(o), encode(unichr(o))) - self.assertEqual(unichr(o), decode(int2byte(o))) + self.assertEqual(bytes((o,)), encode(chr(o))) + self.assertEqual(chr(o), decode(bytes((o,)))) self.assertEqual(encode("&"), b"&-") self.assertEqual(encode("&"), b"&-") self.assertEqual(decode(b"&-"), "&") diff --git a/tests/test_imapclient.py b/tests/test_imapclient.py index f3b8e51b..be1c7b73 100644 --- a/tests/test_imapclient.py +++ b/tests/test_imapclient.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals +import io import itertools import socket import sys @@ -11,7 +12,6 @@ from datetime import datetime import logging -import six from select import POLLIN from imapclient.exceptions import CapabilityError, IMAPClientError, ProtocolError @@ -738,7 +738,7 @@ def test_IMAP_is_patched(self): # prevent basicConfig from being executed for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) - log_stream = six.StringIO() + log_stream = io.StringIO() logging.basicConfig(stream=log_stream, level=logging.DEBUG) self.client._imap._mesg("two") @@ -750,23 +750,16 @@ def test_redacted_password(self): logger_mock.getEffectiveLevel.return_value = logging.DEBUG adapter = IMAPlibLoggerAdapter(logger_mock, dict()) - if six.PY3: - adapter.info("""> b'ICHH1 LOGIN foo@bar.org "secret"'""") - if sys.version_info >= (3, 6, 4): - # LoggerAdapter in Python 3.6.4+ calls logger.log() - logger_mock.log.assert_called_once_with( - logging.INFO, "> b'ICHH1 LOGIN **REDACTED**", extra={} - ) - else: - # LoggerAdapter in Python 3.4 to 3.6 calls logger._log() - logger_mock._log.assert_called_once_with( - logging.INFO, "> b'ICHH1 LOGIN **REDACTED**", (), extra={} - ) + adapter.info("""> b'ICHH1 LOGIN foo@bar.org "secret"'""") + if sys.version_info >= (3, 6, 4): + # LoggerAdapter in Python 3.6.4+ calls logger.log() + logger_mock.log.assert_called_once_with( + logging.INFO, "> b'ICHH1 LOGIN **REDACTED**", extra={} + ) else: - # LoggerAdapter in Python 2.7 calls logger.info() - adapter.info('> ICHH1 LOGIN foo@bar.org "secret"') - logger_mock.info.assert_called_once_with( - "> ICHH1 LOGIN **REDACTED**", extra={} + # LoggerAdapter in Python 3.4 to 3.6 calls logger._log() + logger_mock._log.assert_called_once_with( + logging.INFO, "> b'ICHH1 LOGIN **REDACTED**", (), extra={} ) diff --git a/tests/test_response_lexer.py b/tests/test_response_lexer.py index b89a0646..90976029 100644 --- a/tests/test_response_lexer.py +++ b/tests/test_response_lexer.py @@ -4,8 +4,6 @@ from __future__ import unicode_literals -from six import next - from imapclient.response_lexer import TokenSource from tests.util import unittest diff --git a/tests/test_store.py b/tests/test_store.py index 66cd8e9c..549f16eb 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals -import six - from imapclient.imapclient import DELETED, SEEN, ANSWERED, FLAGGED, DRAFT, RECENT from .imapclient_test import IMAPClientTest from .util import patch, sentinel, Mock @@ -15,7 +13,7 @@ class TestFlagsConsts(IMAPClientTest): def test_flags_are_bytes(self): for flag in DELETED, SEEN, ANSWERED, FLAGGED, DRAFT, RECENT: - if not isinstance(flag, six.binary_type): + if not isinstance(flag, bytes): self.fail("%r flag is not bytes" % flag) diff --git a/tox.ini b/tox.ini index cad2c2bf..ac9c7576 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,10 @@ [tox] -envlist=py27,py34,py35,py36,py37,py38,py39 +envlist=py34,py35,py36,py37,py38,py39 requires=sphinx [testenv] commands=python -m unittest -[testenv:py27] -setenv= - VIRTUALENV_PIP=19.0.1 - VIRTUALENV_SETUPTOOLS=43.0.0 -deps=mock -commands=python -m unittest discover - [testenv:py34] setenv= VIRTUALENV_PIP=19.0.1