Skip to content

Commit

Permalink
Remove more Python 2 compatibility code.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamadden committed Jun 27, 2023
1 parent bbabb1d commit 749807f
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 151 deletions.
108 changes: 30 additions & 78 deletions src/relstorage/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@
'loads',

# Constants
'PY3',
'PY2',
'PY36',
'PYPY',
'WIN',
'MAC',
Expand Down Expand Up @@ -76,46 +73,31 @@
'perf_counter',
]

PY3 = sys.version_info[0] == 3
PY36 = sys.version_info[:2] >= (3, 6)
PY2 = not PY3
PYPY = platform.python_implementation() == 'PyPy'
WIN = sys.platform.startswith('win')
MAC = sys.platform.startswith('darwin')

try:
# Python 3.3+ (PEP 418)

# The value (in fractional seconds) of a performance counter, i.e.
# a clock with the highest available resolution to measure a short
# duration. It does include time elapsed during sleep and is
# system-wide. The reference point of the returned value is
# undefined, so that only the difference between the results of
# consecutive calls is valid.
from time import perf_counter
except ImportError:
import time

if sys.platform == "win32":
perf_counter = time.clock # pylint: disable=no-member
else:
perf_counter = time.time
# Python 3.3+ (PEP 418)

# The value (in fractional seconds) of a performance counter, i.e.
# a clock with the highest available resolution to measure a short
# duration. It does include time elapsed during sleep and is
# system-wide. The reference point of the returned value is
# undefined, so that only the difference between the results of
# consecutive calls is valid.
from time import perf_counter

del time

# Dict support
# This exists for legacy code only; new code can assume the
# methods.
def list_values(d):
return list(d.values())
iteritems = dict.items
iterkeys = dict.keys
itervalues = dict.values

if PY3:
def list_values(d):
return list(d.values())
iteritems = dict.items
iterkeys = dict.keys
itervalues = dict.values
else:
list_values = dict.values
iteritems = dict.iteritems # pylint:disable=no-member
iterkeys = dict.iterkeys # pylint:disable=no-member
itervalues = dict.itervalues # pylint:disable=no-member

###
# OID and TID data structures.
Expand Down Expand Up @@ -300,27 +282,11 @@ def iteroiditems(d):

# Types

if PY3:
string_types = (str,)
number_types = (int, float)
from io import StringIO as NStringIO
from functools import wraps
else:
string_types = (basestring,) # pylint:disable=undefined-variable
number_types = (int, long, float) # pylint:disable=undefined-variable
from io import BytesIO as NStringIO
# On Python 2, functools.update_wrapper doesn't set the '__wrapped__'
# attribute, and we need that.
from functools import wraps as _wraps
class wraps(object):
def __init__(self, func):
self._orig = func
self._wrapper = _wraps(func)

def __call__(self, replacement):
replacement = self._wrapper(replacement)
replacement.__wrapped__ = self._orig
return replacement
string_types = (str,)
number_types = (int, float)
from io import StringIO as NStringIO
from functools import wraps



IN_TESTRUNNER = (
Expand All @@ -339,29 +305,15 @@ def __call__(self, replacement):
del abc

# Functions
if PY3:
xrange = range
intern = sys.intern
from base64 import encodebytes as base64_encodebytes
from base64 import decodebytes as base64_decodebytes
casefold = str.casefold
from traceback import clear_frames
clear_frames = clear_frames # pylint:disable=self-assigning-variable
from functools import update_wrapper
else:
xrange = xrange # pylint:disable=self-assigning-variable
intern = intern # pylint:disable=self-assigning-variable
from base64 import encodestring as base64_encodebytes # pylint:disable=no-name-in-module
from base64 import decodestring as base64_decodebytes # pylint:disable=no-name-in-module
casefold = str.lower
def clear_frames(tb): # pylint:disable=unused-argument
"Does nothing on Py2."

from functools import update_wrapper as _update_wrapper
def update_wrapper(wrapper, wrapped, *args, **kwargs):
wrapper = _update_wrapper(wrapper, wrapped, *args, **kwargs)
wrapper.__wrapped__ = wrapped
return wrapped
xrange = range
intern = sys.intern
from base64 import encodebytes as base64_encodebytes
from base64 import decodebytes as base64_decodebytes
casefold = str.casefold
from traceback import clear_frames
clear_frames = clear_frames # pylint:disable=self-assigning-variable
from functools import update_wrapper


# In FIPS enabled environments, we need to use usedforsecurity=False
# if we want to use md5() for hashing on non security related usage,
Expand Down
19 changes: 6 additions & 13 deletions src/relstorage/adapters/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from zope.interface import implementer

from .._compat import PYPY
from .._compat import PY3
from .._compat import casefold
from .._util import get_positive_integer_from_environ
from .._util import consume
Expand Down Expand Up @@ -315,18 +314,12 @@ class MemoryViewBlobDriverMixin(object):
# are forced to make the copy. Plus there are tests that like to
# directly compare bytes.

if PY3:
def binary_column_as_state_type(self, data):
if data:
# Calling 'bytes()' on a memoryview in Python 3 does
# nothing useful.
data = data.tobytes()
return data
else:
def binary_column_as_state_type(self, data):
if data:
data = bytes(data)
return data
def binary_column_as_state_type(self, data):
if data:
# Calling 'bytes()' on a memoryview in Python 3 does
# nothing useful.
data = data.tobytes()
return data


@implementer(IDBDriverFactory)
Expand Down
18 changes: 3 additions & 15 deletions src/relstorage/adapters/mysql/drivers/mysqlconnector.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

from zope.interface import implementer

from relstorage._compat import PY2
from relstorage._compat import PYPY
from relstorage.adapters.interfaces import IDBDriver

Expand Down Expand Up @@ -117,25 +116,14 @@ def _get_converter_class(cls):

# The Python implementation calls row_to_python(), the C version
# calls to_python()

# XXX: It's possible this area is where the problems on
# mysql.connector >= 8.0.33 are introduced.
class BlobConverter(mysql.connector.conversion.MySQLConverter):

def __init__(self, charset_name, use_unicode):
if PY2:
use_unicode = False
super().__init__(charset_name, use_unicode)

if PY2:
def _STRING_to_python(self, value, dsc=None):
# This is also supposed to handle SET, but we don't use that type
# anywhere
if isinstance(value, bytearray):
# The Python version tends to return bytearray values.
return bytes(value)
# The C extension tends to return byte (aka str) values.
return super()._STRING_to_python(value, dsc) # pylint:disable=no-member

_VAR_STRING_to_python = _STRING_to_python

# There are a few places we get into trouble on
# Python 2/3 with bytearrays comping back: they
# can't be hashed for the local_client compression
Expand Down
3 changes: 0 additions & 3 deletions src/relstorage/adapters/postgresql/drivers/pg8000.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@ def __init__(self,
max_prepared_statements=max_prepared_statements,
tcp_keepalive=tcp_keepalive
)
if str is bytes: # PY2
del kwargs['ssl_context']
kwargs['ssl'] = ssl_context
try:
super().__init__(**kwargs)
except TypeError:
Expand Down
50 changes: 17 additions & 33 deletions src/relstorage/adapters/sqlite/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@

from zope.interface import implementer

from relstorage._compat import PY3
from relstorage._compat import PY2
from relstorage._compat import PY36
from relstorage._util import log_timed

from ..drivers import implement_db_driver_options
Expand Down Expand Up @@ -212,17 +209,6 @@ def __repr__(self):
def _at_transaction_end(self):
pass

if not PY3:
# This helpful attribute is Python 3 only.
assert not hasattr(sqlite3.Connection, 'in_transaction')
__in_transaction = None
@property
def in_transaction(self):
return self.__in_transaction

def _at_transaction_end(self):
self.__in_transaction = None

def register_before_commit_cleanup(self, func):
self.before_commit_functions.append(func)

Expand Down Expand Up @@ -374,9 +360,8 @@ class Sqlite3Driver(MemoryViewBlobDriverMixin,
# Tested to work on Python 2.7 and Python 3.6+;
# seen some strange ``ProgrammingError: closed``
# on Python 3.5
(PY2 or PY36)
# 3.11 is the oldest version tested on CI
and sqlite3.sqlite_version_info[:2] >= (3, 11)
sqlite3.sqlite_version_info[:2] >= (3, 11)
)

CONNECTION_FACTORY = Connection
Expand All @@ -395,23 +380,22 @@ def __init__(self):
# module's connect() method so we get our preferred goodies.
self._connect = self.connect_to_file

if PY3:
# in_transaction doesn't work on Py2, so assume the worst
# by inheriting the functions.
def connection_may_need_rollback(self, conn):
try:
return conn.in_transaction
except sqlite3.ProgrammingError:
# we're closed. We do need to attempt the rollback so
# we catch the error and know to drop the connection.
return True

connection_may_need_commit = connection_may_need_rollback

# Py2 returns buffer for blobs, hence the Mixin.
# But Py3 returns plain bytes.
def binary_column_as_state_type(self, data):
return data
# in_transaction doesn't work on Py2, so assume the worst
# by inheriting the functions.
def connection_may_need_rollback(self, conn):
try:
return conn.in_transaction
except sqlite3.ProgrammingError:
# we're closed. We do need to attempt the rollback so
# we catch the error and know to drop the connection.
return True

connection_may_need_commit = connection_may_need_rollback

# Py2 returns buffer for blobs, hence the Mixin.
# But Py3 returns plain bytes.
def binary_column_as_state_type(self, data):
return data



Expand Down
5 changes: 2 additions & 3 deletions src/relstorage/adapters/sqlite/tests/test_sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from relstorage.tests.util import RUNNING_ON_TRAVIS
from relstorage.tests.util import RUNNING_ON_APPVEYOR
from relstorage._compat import PYPY
from relstorage._compat import PY3

class Sqlite3AdapterMixin(object):

Expand Down Expand Up @@ -203,9 +202,9 @@ def _compute_large_blob_size(self, use_small_blobs):
__BASE_SKIPPED_TESTS = (
# These were both seen on Travis with PyPy3.6 7.1.1, sqlite 3.11.
# I can't reproduce locally.
('checkAutoReconnect', PYPY and PY3 and RUNNING_ON_TRAVIS,
('checkAutoReconnect', PYPY and RUNNING_ON_TRAVIS,
"Somehow still winds up closed"),
('checkAutoReconnectOnSync', PYPY and PY3 and RUNNING_ON_TRAVIS,
('checkAutoReconnectOnSync', PYPY and RUNNING_ON_TRAVIS,
"Somehow still winds up closed"),
)

Expand Down
6 changes: 0 additions & 6 deletions src/relstorage/blobhelper/tests/test_cached.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import os

from relstorage._compat import PY3

from . import test_blobhelper
from .test_blobhelper import write_file
from .test_blobhelper import read_file
Expand Down Expand Up @@ -102,8 +100,6 @@ def test_loadBlob_unshared_missing(self):
def test_openCommittedBlobFile_as_file(self):
blobhelper = self._make_default()
with blobhelper.openCommittedBlobFile(None, test_oid, test_tid) as f:
if not PY3:
self.assertEqual(f.__class__, file) # pylint:disable=undefined-variable
self.assertEqual(f.read(), b'blob here')

def test_openCommittedBlobFile_as_blobfile(self):
Expand All @@ -129,8 +125,6 @@ def loadBlob_wrapper(cursor, oid, serial, blob_lock):
blobhelper._loadBlobInternal = loadBlob_wrapper
with blobhelper.openCommittedBlobFile(None, test_oid, test_tid) as f:
self.assertEqual(loadBlob_calls, [1])
if not PY3:
self.assertEqual(f.__class__, file) # pylint:disable=undefined-variable
self.assertEqual(f.read(), b'blob here')

def test_openCommittedBlobFile_retry_as_blobfile(self):
Expand Down

0 comments on commit 749807f

Please sign in to comment.