From 6f60fe29e6c6906e93943d5853fbf15d53c81e5d Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sat, 2 Jan 2021 16:58:19 +0900 Subject: [PATCH 1/4] Remove Python 2 and 3.5 support. --- pymysql/__init__.py | 6 +-- pymysql/_auth.py | 9 +---- pymysql/charset.py | 2 +- pymysql/connections.py | 59 ++++++++------------------- pymysql/converters.py | 68 ++++++-------------------------- pymysql/cursors.py | 46 ++++++--------------- pymysql/optionfile.py | 7 +--- pymysql/protocol.py | 26 +++++------- pymysql/tests/test_connection.py | 8 ++-- 9 files changed, 59 insertions(+), 172 deletions(-) diff --git a/pymysql/__init__.py b/pymysql/__init__.py index 29e6b87c..1e126dcd 100644 --- a/pymysql/__init__.py +++ b/pymysql/__init__.py @@ -23,7 +23,6 @@ """ import sys -from ._compat import PY2 from .constants import FIELD_TYPE from .converters import escape_dict, escape_sequence, escape_string from .err import ( @@ -79,10 +78,7 @@ def __hash__(self): def Binary(x): """Return x as a binary type.""" - if PY2: - return bytearray(x) - else: - return bytes(x) + return bytes(x) def Connect(*args, **kwargs): diff --git a/pymysql/_auth.py b/pymysql/_auth.py index 57f9abb1..77caeafd 100644 --- a/pymysql/_auth.py +++ b/pymysql/_auth.py @@ -1,7 +1,6 @@ """ Implements auth methods """ -from ._compat import PY2 from .err import OperationalError from .util import byte2int, int2byte @@ -46,8 +45,6 @@ def scramble_native_password(password, message): def _my_crypt(message1, message2): result = bytearray(message1) - if PY2: - message2 = bytearray(message2) for i in range(len(result)): result[i] ^= message2[i] @@ -61,7 +58,7 @@ def _my_crypt(message1, message2): SCRAMBLE_LENGTH_323 = 8 -class RandStruct_323(object): +class RandStruct_323: def __init__(self, seed1, seed2): self.max_value = 0x3FFFFFFF @@ -188,7 +185,7 @@ def _xor_password(password, salt): # See https://github.com/mysql/mysql-server/blob/7d10c82196c8e45554f27c00681474a9fb86d137/sql/auth/sha2_password.cc#L939-L945 salt = salt[:SCRAMBLE_LENGTH] password_bytes = bytearray(password) - salt = bytearray(salt) # for PY2 compat. + #salt = bytearray(salt) # for PY2 compat. salt_len = len(salt) for i in range(len(password_bytes)): password_bytes[i] ^= salt[i % salt_len] @@ -259,8 +256,6 @@ def scramble_caching_sha2(password, nonce): p3 = hashlib.sha256(p2 + nonce).digest() res = bytearray(p1) - if PY2: - p3 = bytearray(p3) for i in range(len(p3)): res[i] ^= p3[i] diff --git a/pymysql/charset.py b/pymysql/charset.py index d3ced67c..3ef3ea46 100644 --- a/pymysql/charset.py +++ b/pymysql/charset.py @@ -6,7 +6,7 @@ } -class Charset(object): +class Charset: def __init__(self, id, name, collation, is_default): self.id, self.name, self.collation = id, name, collation self.is_default = is_default == 'Yes' diff --git a/pymysql/connections.py b/pymysql/connections.py index 9e87e0b0..3faed512 100644 --- a/pymysql/connections.py +++ b/pymysql/connections.py @@ -2,9 +2,6 @@ # http://dev.mysql.com/doc/internals/en/client-server-protocol.html # Error codes: # https://dev.mysql.com/doc/refman/5.5/en/error-handling.html -from __future__ import print_function -from ._compat import PY2, range_type, text_type, str_type, JYTHON, IRONPYTHON - import errno import io import os @@ -47,32 +44,11 @@ _py_version = sys.version_info[:2] -if PY2: - pass -elif _py_version < (3, 6): - # See http://bugs.python.org/issue24870 - _surrogateescape_table = [chr(i) if i < 0x80 else chr(i + 0xdc00) for i in range(256)] - - def _fast_surrogateescape(s): - return s.decode('latin1').translate(_surrogateescape_table) -else: - def _fast_surrogateescape(s): - return s.decode('ascii', 'surrogateescape') - -# socket.makefile() in Python 2 is not usable because very inefficient and -# bad behavior about timeout. -# XXX: ._socketio doesn't work under IronPython. -if PY2 and not IRONPYTHON: - # read method of file-like returned by sock.makefile() is very slow. - # So we copy io-based one from Python 3. - from ._socketio import SocketIO - - def _makefile(sock, mode): - return io.BufferedReader(SocketIO(sock, mode)) -else: - # socket.makefile in Python 3 is nice. - def _makefile(sock, mode): - return sock.makefile(mode) +def _fast_surrogateescape(s): + return s.decode('ascii', 'surrogateescape') + +def _makefile(sock, mode): + return sock.makefile(mode) TEXT_TYPES = { @@ -113,7 +89,7 @@ def lenenc_int(i): raise ValueError("Encoding %x is larger than %x - no representation in LengthEncodedInteger" % (i, (1 << 64))) -class Connection(object): +class Connection: """ Representation of a socket with a mysql server. @@ -258,7 +234,7 @@ def _config(key, arg): raise ValueError("port should be of type int") self.user = user or DEFAULT_USER self.password = password or b"" - if isinstance(self.password, text_type): + if isinstance(self.password, str): self.password = self.password.encode('latin1') self.db = database self.unix_socket = unix_socket @@ -459,7 +435,7 @@ def escape(self, obj, mapping=None): Non-standard, for internal use; do not use this in your applications. """ - if isinstance(obj, str_type): + if isinstance(obj, str): return "'" + self.escape_string(obj) + "'" if isinstance(obj, (bytes, bytearray)): ret = self._quote_bytes(obj) @@ -503,11 +479,8 @@ def cursor(self, cursor=None): def query(self, sql, unbuffered=False): # if DEBUG: # print("DEBUG: sending query:", sql) - if isinstance(sql, text_type) and not (JYTHON or IRONPYTHON): - if PY2: - sql = sql.encode(self.encoding) - else: - sql = sql.encode(self.encoding, 'surrogateescape') + if isinstance(sql, str): + sql = sql.encode(self.encoding, 'surrogateescape') self._execute_command(COMMAND.COM_QUERY, sql) self._affected_rows = self._read_query_result(unbuffered=unbuffered) return self._affected_rows @@ -758,7 +731,7 @@ def _execute_command(self, command, sql): self.next_result() self._result = None - if isinstance(sql, text_type): + if isinstance(sql, str): sql = sql.encode(self.encoding) packet_size = min(MAX_PACKET_LEN, len(sql) + 1) # +1 is for command @@ -791,7 +764,7 @@ def _request_authentication(self): raise ValueError("Did not specify a username") charset_id = charset_by_name(self.charset).id - if isinstance(self.user, text_type): + if isinstance(self.user, str): self.user = self.user.encode(self.encoding) data_init = struct.pack(' max_stmt_length: rows += self.execute(sql + postfix) sql = bytearray(prefix) @@ -265,7 +245,7 @@ def callproc(self, procname, args=()): q = "CALL %s(%s)" % (procname, ','.join(['@_%s_%d' % (procname, i) - for i in range_type(len(args))])) + for i in range(len(args))])) self._query(q) self._executed = q return args @@ -356,7 +336,7 @@ def __iter__(self): NotSupportedError = err.NotSupportedError -class DictCursorMixin(object): +class DictCursorMixin: # You can override this to use OrderedDict or other dict-like types. dict_type = dict @@ -469,7 +449,7 @@ def fetchmany(self, size=None): size = self.arraysize rows = [] - for i in range_type(size): + for i in range(size): row = self.read_next() if row is None: break @@ -485,7 +465,7 @@ def scroll(self, value, mode='relative'): raise err.NotSupportedError( "Backwards scrolling not supported by this cursor") - for _ in range_type(value): + for _ in range(value): self.read_next() self.rownumber += value elif mode == 'absolute': @@ -494,7 +474,7 @@ def scroll(self, value, mode='relative'): "Backwards scrolling not supported by this cursor") end = value - self.rownumber - for _ in range_type(end): + for _ in range(end): self.read_next() self.rownumber = value else: diff --git a/pymysql/optionfile.py b/pymysql/optionfile.py index 91e2dfe3..79810ef3 100644 --- a/pymysql/optionfile.py +++ b/pymysql/optionfile.py @@ -1,9 +1,4 @@ -from ._compat import PY2 - -if PY2: - import ConfigParser as configparser -else: - import configparser +import configparser class Parser(configparser.RawConfigParser): diff --git a/pymysql/protocol.py b/pymysql/protocol.py index e302edab..541475ad 100644 --- a/pymysql/protocol.py +++ b/pymysql/protocol.py @@ -1,9 +1,7 @@ # Python implementation of low level MySQL client-server protocol # http://dev.mysql.com/doc/internals/en/client-server-protocol.html -from __future__ import print_function from .charset import MBLENGTH -from ._compat import PY2, range_type from .constants import FIELD_TYPE, SERVER_STATUS from . import err from .util import byte2int @@ -37,7 +35,7 @@ def printable(data): print("-" * 66) except ValueError: pass - dump_data = [data[i:i+16] for i in range_type(0, min(len(data), 256), 16)] + dump_data = [data[i:i+16] for i in range(0, min(len(data), 256), 16)] for d in dump_data: print(' '.join("{:02X}".format(byte2int(x)) for x in d) + ' ' * (16 - len(d)) + ' ' * 2 + @@ -46,7 +44,7 @@ def printable(data): print() -class MysqlPacket(object): +class MysqlPacket: """Representation of a MySQL response packet. Provides an interface for reading/parsing the packet results. @@ -108,16 +106,10 @@ def get_bytes(self, position, length=1): """ return self._data[position:(position+length)] - if PY2: - def read_uint8(self): - result = ord(self._data[self._position]) - self._position += 1 - return result - else: - def read_uint8(self): - result = self._data[self._position] - self._position += 1 - return result + def read_uint8(self): + result = self._data[self._position] + self._position += 1 + return result def read_uint16(self): result = struct.unpack_from(' Date: Sat, 2 Jan 2021 17:03:46 +0900 Subject: [PATCH 2/4] Remove PY2 support --- pymysql/_compat.py | 21 ------------- pymysql/tests/base.py | 30 ------------------- pymysql/tests/test_SSCursor.py | 1 + pymysql/tests/test_basic.py | 1 - pymysql/tests/test_connection.py | 3 +- pymysql/tests/test_converters.py | 9 ------ pymysql/tests/test_issues.py | 15 ---------- pymysql/tests/test_optionfile.py | 14 ++------- .../test_MySQLdb/test_MySQLdb_nonstandard.py | 22 +++++--------- 9 files changed, 12 insertions(+), 104 deletions(-) delete mode 100644 pymysql/_compat.py diff --git a/pymysql/_compat.py b/pymysql/_compat.py deleted file mode 100644 index 252789ec..00000000 --- a/pymysql/_compat.py +++ /dev/null @@ -1,21 +0,0 @@ -import sys - -PY2 = sys.version_info[0] == 2 -PYPY = hasattr(sys, 'pypy_translation_info') -JYTHON = sys.platform.startswith('java') -IRONPYTHON = sys.platform == 'cli' -CPYTHON = not PYPY and not JYTHON and not IRONPYTHON - -if PY2: - import __builtin__ - range_type = xrange - text_type = unicode - long_type = long - str_type = basestring - unichr = __builtin__.unichr -else: - range_type = range - text_type = str - long_type = int - str_type = str - unichr = chr diff --git a/pymysql/tests/base.py b/pymysql/tests/base.py index 22bed9d8..16d14c03 100644 --- a/pymysql/tests/base.py +++ b/pymysql/tests/base.py @@ -6,27 +6,6 @@ import unittest import pymysql -from .._compat import CPYTHON - - -if CPYTHON: - import atexit - gc.set_debug(gc.DEBUG_UNCOLLECTABLE) - - @atexit.register - def report_uncollectable(): - import gc - if not gc.garbage: - print("No garbages!") - return - print('uncollectable objects') - for obj in gc.garbage: - print(obj) - if hasattr(obj, '__dict__'): - print(obj.__dict__) - for ref in gc.get_referrers(obj): - print("referrer:", ref) - print('---') class PyMySQLTestCase(unittest.TestCase): @@ -111,12 +90,3 @@ def drop_table(self, connection, tablename): warnings.simplefilter("ignore") cursor.execute("drop table if exists `%s`" % (tablename,)) cursor.close() - - def safe_gc_collect(self): - """Ensure cycles are collected via gc. - - Runs additional times on non-CPython platforms. - """ - gc.collect() - if not CPYTHON: - gc.collect() diff --git a/pymysql/tests/test_SSCursor.py b/pymysql/tests/test_SSCursor.py index 3bbfcfa4..2b0de78a 100644 --- a/pymysql/tests/test_SSCursor.py +++ b/pymysql/tests/test_SSCursor.py @@ -11,6 +11,7 @@ import pymysql.cursors from pymysql.constants import CLIENT + class TestSSCursor(base.PyMySQLTestCase): def test_SSCursor(self): affected_rows = 18446744073709551615 diff --git a/pymysql/tests/test_basic.py b/pymysql/tests/test_basic.py index aa23e065..840c4860 100644 --- a/pymysql/tests/test_basic.py +++ b/pymysql/tests/test_basic.py @@ -1,4 +1,3 @@ -# coding: utf-8 import datetime import json import time diff --git a/pymysql/tests/test_connection.py b/pymysql/tests/test_connection.py index db34ae1c..54537af7 100644 --- a/pymysql/tests/test_connection.py +++ b/pymysql/tests/test_connection.py @@ -4,7 +4,6 @@ import pytest import pymysql from pymysql.tests import base -from pymysql._compat import text_type from pymysql.constants import CLIENT import pytest @@ -523,7 +522,7 @@ def test_escape_fallback_encoder(self): class Custom(str): pass - mapping = {text_type: pymysql.escape_string} + mapping = {str: pymysql.escape_string} self.assertEqual(con.escape(Custom('foobar'), mapping), "'foobar'") def test_escape_no_default(self): diff --git a/pymysql/tests/test_converters.py b/pymysql/tests/test_converters.py index b7b5a984..c2c9b6bf 100644 --- a/pymysql/tests/test_converters.py +++ b/pymysql/tests/test_converters.py @@ -1,7 +1,5 @@ import datetime from unittest import TestCase - -from pymysql._compat import PY2 from pymysql import converters @@ -16,13 +14,6 @@ def test_escape_string(self): u"foo\\nbar" ) - if PY2: - def test_escape_string_bytes(self): - self.assertEqual( - converters.escape_string(b"foo\nbar"), - b"foo\\nbar" - ) - def test_convert_datetime(self): expected = datetime.datetime(2007, 2, 24, 23, 6, 20) dt = converters.convert_datetime('2007-02-24 23:06:20') diff --git a/pymysql/tests/test_issues.py b/pymysql/tests/test_issues.py index 604aeaff..2e11ddb5 100644 --- a/pymysql/tests/test_issues.py +++ b/pymysql/tests/test_issues.py @@ -7,16 +7,8 @@ import pymysql from pymysql import cursors -from pymysql._compat import text_type from pymysql.tests import base -try: - import imp - reload = imp.reload -except AttributeError: - pass - - __all__ = ["TestOldIssues", "TestNewIssues", "TestGitHubIssues"] class TestOldIssues(base.PyMySQLTestCase): @@ -90,13 +82,6 @@ def test_issue_8(self): finally: c.execute("drop table test") - def test_issue_9(self): - """ sets DeprecationWarning in Python 2.6 """ - try: - reload(pymysql) - except DeprecationWarning: - self.fail() - def test_issue_13(self): """ can't handle large result fields """ conn = self.connect() diff --git a/pymysql/tests/test_optionfile.py b/pymysql/tests/test_optionfile.py index 3ee519e2..81bd1fe4 100644 --- a/pymysql/tests/test_optionfile.py +++ b/pymysql/tests/test_optionfile.py @@ -1,11 +1,6 @@ -from pymysql.optionfile import Parser +from io import StringIO from unittest import TestCase -from pymysql._compat import PY2 - -try: - from cStringIO import StringIO -except ImportError: - from io import StringIO +from pymysql.optionfile import Parser __all__ = ['TestParser'] @@ -24,10 +19,7 @@ class TestParser(TestCase): def test_string(self): parser = Parser() - if PY2: - parser.readfp(StringIO(_cfg_file)) - else: - parser.read_file(StringIO(_cfg_file)) + parser.read_file(StringIO(_cfg_file)) self.assertEqual(parser.get("default", "string"), "foo") self.assertEqual(parser.get("default", "quoted"), "bar") self.assertEqual(parser.get("default", "single_quoted"), "foobar") diff --git a/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py b/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py index 5c739a42..747ea4b0 100644 --- a/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +++ b/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py @@ -5,10 +5,6 @@ _mysql = pymysql from pymysql.constants import FIELD_TYPE from pymysql.tests import base -from pymysql._compat import PY2, long_type - -if not PY2: - basestring = str class TestDBAPISet(unittest.TestCase): @@ -34,13 +30,13 @@ def test_NULL(self): def test_version(self): """Version information sanity.""" - self.assertTrue(isinstance(_mysql.__version__, basestring)) + self.assertTrue(isinstance(_mysql.__version__, str)) self.assertTrue(isinstance(_mysql.version_info, tuple)) self.assertEqual(len(_mysql.version_info), 5) def test_client_info(self): - self.assertTrue(isinstance(_mysql.get_client_info(), basestring)) + self.assertTrue(isinstance(_mysql.get_client_info(), str)) def test_thread_safe(self): self.assertTrue(isinstance(_mysql.thread_safe(), int)) @@ -59,7 +55,7 @@ def tearDown(self): def test_thread_id(self): tid = self.conn.thread_id() - self.assertTrue(isinstance(tid, (int, long_type)), + self.assertTrue(isinstance(tid, int), "thread_id didn't return an integral value.") self.assertRaises(TypeError, self.conn.thread_id, ('evil',), @@ -76,23 +72,19 @@ def test_affected_rows(self): #self.conn.dump_debug_info) def test_charset_name(self): - self.assertTrue(isinstance(self.conn.character_set_name(), basestring), + self.assertTrue(isinstance(self.conn.character_set_name(), str), "Should return a string.") def test_host_info(self): - assert isinstance(self.conn.get_host_info(), basestring), "should return a string" + assert isinstance(self.conn.get_host_info(), str), "should return a string" def test_proto_info(self): self.assertTrue(isinstance(self.conn.get_proto_info(), int), "Should return an int.") def test_server_info(self): - if sys.version_info[0] == 2: - self.assertTrue(isinstance(self.conn.get_server_info(), basestring), - "Should return an str.") - else: - self.assertTrue(isinstance(self.conn.get_server_info(), basestring), - "Should return an str.") + self.assertTrue(isinstance(self.conn.get_server_info(), str), + "Should return an str.") if __name__ == "__main__": unittest.main() From 971c995a7eb6acd1dd64b57f0164aeedce9ab5b7 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sat, 2 Jan 2021 18:10:41 +0900 Subject: [PATCH 3/4] fix --- pymysql/connections.py | 1 - pymysql/converters.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pymysql/connections.py b/pymysql/connections.py index 3faed512..abc59345 100644 --- a/pymysql/connections.py +++ b/pymysql/connections.py @@ -3,7 +3,6 @@ # Error codes: # https://dev.mysql.com/doc/refman/5.5/en/error-handling.html import errno -import io import os import socket import struct diff --git a/pymysql/converters.py b/pymysql/converters.py index d8226b8a..0e40eab7 100644 --- a/pymysql/converters.py +++ b/pymysql/converters.py @@ -288,7 +288,7 @@ def through(x): int: escape_int, float: escape_float, str: escape_str, - bytes: escape_bytes + bytes: escape_bytes, tuple: escape_sequence, list: escape_sequence, set: escape_sequence, From 501570d05f7c28f3594b5888d22a92656a3f21c5 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Sat, 2 Jan 2021 18:11:36 +0900 Subject: [PATCH 4/4] fix --- pymysql/cursors.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pymysql/cursors.py b/pymysql/cursors.py index f55099d9..6f72ba35 100644 --- a/pymysql/cursors.py +++ b/pymysql/cursors.py @@ -1,4 +1,3 @@ -from functools import partial import re from . import err @@ -104,8 +103,6 @@ def _ensure_bytes(self, x, encoding=None): return x def _escape_args(self, args, conn): - ensure_bytes = partial(self._ensure_bytes, encoding=conn.encoding) - if isinstance(args, (tuple, list)): return tuple(conn.literal(arg) for arg in args) elif isinstance(args, dict):