Skip to content

Commit

Permalink
Remove support for Python 2
Browse files Browse the repository at this point in the history
Python 2.7 (the last Python 2 release) was End-Of-Life on 1-Jan-2020.

  * Add requirement to `setup.py` for python_requires=">=3.4.0"
  * Remove all usage of the `six` library
  * Remove declared support for Python 2
  * Update tox.ini to remove `py27` environment
  * Update Github workflows to no longer test against Python 2.7
  * Remove `from __future__ import print_function`
  * Remove `from __future__ unicode_literals`
  * Update tests to import `unittest` and `unittest.mock` directly

Closes: mjs#401
  • Loading branch information
JohnVillalovos committed Jun 18, 2022
1 parent 5bccbad commit 9c50200
Show file tree
Hide file tree
Showing 40 changed files with 106 additions and 262 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 0 additions & 7 deletions doc/src/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion doc/src/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------
Expand Down
2 changes: 0 additions & 2 deletions imapclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# version_info provides the version number in programmer friendly way.
# The 4th part will be either alpha, beta or final.

from __future__ import unicode_literals

from .imapclient import *
from .response_parser import *
from .tls import *
Expand Down
18 changes: 8 additions & 10 deletions imapclient/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

from __future__ import unicode_literals

import json
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

Expand Down Expand Up @@ -45,7 +42,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)

Expand All @@ -62,7 +59,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:
Expand All @@ -80,7 +77,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
Expand Down Expand Up @@ -133,7 +130,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"]


Expand Down
2 changes: 0 additions & 2 deletions imapclient/datetime_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

from __future__ import unicode_literals

import re
from datetime import datetime
from email.utils import parsedate_tz
Expand Down
2 changes: 0 additions & 2 deletions imapclient/fixed_offset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses

from __future__ import unicode_literals

import time
from datetime import tzinfo, timedelta

Expand Down
14 changes: 6 additions & 8 deletions imapclient/imap_utf7.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
# base64 encoding context), which is & in this modified UTF-7 convention,
# since + is considered as mainly used in mailbox names.
# Other variations and examples can be found in the RFC 3501, section 5.1.3.
from __future__ import unicode_literals

import binascii
from six import binary_type, text_type, byte2int, iterbytes, unichr


def encode(s):
Expand All @@ -18,7 +16,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()
Expand Down Expand Up @@ -56,8 +54,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):
Expand All @@ -67,13 +65,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)
Expand All @@ -90,7 +88,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:
Expand Down
Loading

0 comments on commit 9c50200

Please sign in to comment.