Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop python support for 2.7, 3.4 and 3.5 #321

Merged
merged 4 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [2.7, 3.6, 3.7, 3.8, 3.9, pypy2, pypy3]
python-version: [3.6, 3.7, 3.8, 3.9, pypy3]

steps:
- uses: actions/checkout@v2
Expand All @@ -26,7 +26,7 @@ jobs:
python -m pip install --upgrade pip
sudo apt-get install libmemcached-dev
- name: Lint
if: matrix.python-version == '2.7' || matrix.python-version == '3.9'
if: matrix.python-version == '3.9'
run: |
pip install tox tox-gh-actions
tox -e flake8
Expand Down
62 changes: 29 additions & 33 deletions pymemcache/client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import errno
import platform
import socket
import six

from pymemcache import pool

Expand Down Expand Up @@ -71,16 +70,16 @@ def _parse_hex(value):

STAT_TYPES = {
# General stats
b'version': six.binary_type,
b'version': bytes,
b'rusage_user': _parse_float,
b'rusage_system': _parse_float,
b'hash_is_expanding': _parse_bool_int,
b'slab_reassign_running': _parse_bool_int,

# Settings stats
b'inter': six.binary_type,
b'inter': bytes,
b'growth_factor': float,
b'stat_key_prefix': six.binary_type,
b'stat_key_prefix': bytes,
b'umask': _parse_hex,
b'detail_enabled': _parse_bool_int,
b'cas_enabled': _parse_bool_int,
Expand All @@ -96,14 +95,11 @@ def _parse_hex(value):
def check_key_helper(key, allow_unicode_keys, key_prefix=b''):
"""Checks key and add key_prefix."""
if allow_unicode_keys:
if isinstance(key, six.text_type):
if isinstance(key, str):
key = key.encode('utf8')
elif isinstance(key, six.string_types):
elif isinstance(key, str):
try:
if isinstance(key, six.binary_type):
key = key.decode().encode('ascii')
else:
key = key.encode('ascii')
key = key.encode('ascii')
except (UnicodeEncodeError, UnicodeDecodeError):
raise MemcacheIllegalInputError("Non-ASCII key: %r" % key)

Expand All @@ -126,7 +122,7 @@ def normalize_server_spec(server):
return server
if isinstance(server, list):
return tuple(server) # Assume [host, port] provided.
if not isinstance(server, six.string_types):
if not isinstance(server, str):
raise ValueError('Unknown server provided: %r' % server)
if server.startswith('unix:'):
return server[5:]
Expand All @@ -142,7 +138,7 @@ def normalize_server_spec(server):
return (host, port)


class KeepaliveOpts(object):
class KeepaliveOpts:
"""
A configuration structure to define the socket keepalive.

Expand Down Expand Up @@ -176,7 +172,7 @@ def __init__(self, idle=1, intvl=1, cnt=5):
self.cnt = cnt


class Client(object):
class Client:
"""
A client for a single memcached server.

Expand Down Expand Up @@ -358,9 +354,9 @@ def __init__(self,
"of structure."
)
self.sock = None
if isinstance(key_prefix, six.text_type):
if isinstance(key_prefix, str):
key_prefix = key_prefix.encode('ascii')
if not isinstance(key_prefix, six.binary_type):
if not isinstance(key_prefix, bytes):
raise TypeError("key_prefix should be bytes.")
self.key_prefix = key_prefix
self.default_noreply = default_noreply
Expand Down Expand Up @@ -483,7 +479,7 @@ def set_many(self, values, expire=0, noreply=None, flags=None):
if noreply is None:
noreply = self.default_noreply
result = self._store_cmd(b'set', values, expire, noreply, flags=flags)
return [k for k, v in six.iteritems(result) if not v]
return [k for k, v in result.items() if not v]

set_multi = set_many

Expand Down Expand Up @@ -824,7 +820,7 @@ def stats(self, *args):
"""
result = self._fetch_cmd(b'stats', args, False)

for key, value in six.iteritems(result):
for key, value in result.items():
converter = STAT_TYPES.get(key, int)
try:
result[key] = converter(value)
Expand Down Expand Up @@ -941,12 +937,12 @@ def _raise_errors(self, line, name):

def _check_integer(self, value, name):
"""Check that a value is an integer and encode it as a binary string"""
if not isinstance(value, six.integer_types):
if not isinstance(value, int):
raise MemcacheIllegalInputError(
'%s must be integer, got bad value: %r' % (name, value)
f'{name} must be integer, got bad value: {value!r}'
)

return six.text_type(value).encode(self.encoding)
return str(value).encode(self.encoding)
jparise marked this conversation as resolved.
Show resolved Hide resolved

def _check_cas(self, cas):
"""Check that a value is a valid input for 'cas' -- either an int or a
Expand All @@ -955,13 +951,13 @@ def _check_cas(self, cas):
The value will be (re)encoded so that we can accept strings or bytes.
"""
# convert non-binary values to binary
if isinstance(cas, (six.integer_types, six.string_types)):
if isinstance(cas, (int, str)):
try:
cas = six.text_type(cas).encode(self.encoding)
cas = str(cas).encode(self.encoding)
except UnicodeEncodeError:
raise MemcacheIllegalInputError(
'non-ASCII cas value: %r' % cas)
elif not isinstance(cas, six.binary_type):
elif not isinstance(cas, bytes):
raise MemcacheIllegalInputError(
'cas must be integer, string, or bytes, got bad value: %r' % cas
)
Expand All @@ -987,7 +983,7 @@ def _extract_value(self, expect_cas, line, buf, remapped_keys,
try:
_, key, flags, size = line.split()
except Exception as e:
raise ValueError("Unable to parse line %s: %s" % (line, e))
raise ValueError(f"Unable to parse line {line}: {e}")

value = None
try:
Expand Down Expand Up @@ -1062,7 +1058,7 @@ def _store_cmd(self, name, values, expire, noreply, flags=None, cas=None):
extra += b' noreply'
expire = self._check_integer(expire, "expire")

for key, data in six.iteritems(values):
for key, data in values.items():
# must be able to reliably map responses back to the original order
keys.append(key)

Expand All @@ -1074,17 +1070,17 @@ def _store_cmd(self, name, values, expire, noreply, flags=None, cas=None):
if flags is not None:
data_flags = flags

if not isinstance(data, six.binary_type):
if not isinstance(data, bytes):
try:
data = six.text_type(data).encode(self.encoding)
data = str(data).encode(self.encoding)
except UnicodeEncodeError as e:
raise MemcacheIllegalInputError(
"Data values must be binary-safe: %s" % e)

cmds.append(name + b' ' + key + b' ' +
six.text_type(data_flags).encode(self.encoding) +
str(data_flags).encode(self.encoding) +
b' ' + expire +
b' ' + six.text_type(len(data)).encode(self.encoding) +
b' ' + str(len(data)).encode(self.encoding) +
extra + b'\r\n' + data + b'\r\n')

if self.sock is None:
Expand Down Expand Up @@ -1155,7 +1151,7 @@ def __delitem__(self, key):
self.delete(key, noreply=True)


class PooledClient(object):
class PooledClient:
"""A thread-safe pool of clients (with the same client api).

Args:
Expand Down Expand Up @@ -1208,9 +1204,9 @@ def __init__(self,
self.socket_keepalive = socket_keepalive
self.default_noreply = default_noreply
self.allow_unicode_keys = allow_unicode_keys
if isinstance(key_prefix, six.text_type):
if isinstance(key_prefix, str):
key_prefix = key_prefix.encode('ascii')
if not isinstance(key_prefix, six.binary_type):
if not isinstance(key_prefix, bytes):
raise TypeError("key_prefix should be bytes.")
self.key_prefix = key_prefix
self.client_pool = pool.ObjectPool(
Expand Down Expand Up @@ -1490,6 +1486,6 @@ def _recv(sock, size):
while True:
try:
return sock.recv(size)
except IOError as e:
except OSError as e:
if e.errno != errno.EINTR:
raise
15 changes: 7 additions & 8 deletions pymemcache/client/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import socket
import time
import logging
import six

from pymemcache.client.base import (
Client,
Expand All @@ -16,7 +15,7 @@
logger = logging.getLogger(__name__)


class HashClient(object):
class HashClient:
"""
A client for communicating with a cluster of memcached servers
"""
Expand Down Expand Up @@ -125,7 +124,7 @@ def add_server(self, server, port=None):
# To maintain backward compatibility, if a port is provided, assume
# that server wasn't provided as a (host, port) tuple.
if port is not None:
if not isinstance(server, six.string_types):
if not isinstance(server, str):
raise TypeError('Server must be a string when passing port.')
server = (server, port)

Expand All @@ -142,7 +141,7 @@ def remove_server(self, server, port=None):
# To maintain backward compatibility, if a port is provided, assume
# that server wasn't provided as a (host, port) tuple.
if port is not None:
if not isinstance(server, six.string_types):
if not isinstance(server, str):
raise TypeError('Server must be a string when passing port.')
server = (server, port)

Expand Down Expand Up @@ -216,7 +215,7 @@ def _safely_run_func(self, client, func, default_val, *args, **kwargs):

# Connecting to the server fail, we should enter
# retry mode
except socket.error:
except OSError:
self._mark_failed_server(client.server)

# if we haven't enabled ignore_exc, don't move on gracefully, just
Expand Down Expand Up @@ -275,7 +274,7 @@ def _safely_run_set_many(self, client, values, *args, **kwargs):

# Connecting to the server fail, we should enter
# retry mode
except socket.error:
except OSError:
self._mark_failed_server(client.server)

# if we haven't enabled ignore_exc, don't move on gracefully, just
Expand Down Expand Up @@ -345,7 +344,7 @@ def _set_many(self, client, values, *args, **kwargs):
if not self.ignore_exc:
return succeeded, failed, e

succeeded = [key for key in six.iterkeys(values) if key not in failed]
succeeded = [key for key in values if key not in failed]
return succeeded, failed, None

def close(self):
Expand All @@ -370,7 +369,7 @@ def set_many(self, values, *args, **kwargs):
client_batches = collections.defaultdict(dict)
failed = []

for key, value in six.iteritems(values):
for key, value in values.items():
client = self._get_client(key)

if client is None:
Expand Down
5 changes: 2 additions & 3 deletions pymemcache/client/rendezvous.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pymemcache.client.murmur3 import murmur3_32


class RendezvousHash(object):
class RendezvousHash:
"""
Implements the Highest Random Weight (HRW) hashing algorithm most
commonly referred to as rendezvous hashing.
Expand Down Expand Up @@ -35,8 +35,7 @@ def get_node(self, key):
winner = None

for node in self.nodes:
score = self.hash_function(
"%s-%s" % (node, key))
score = self.hash_function(f"{node}-{key}")

if score > high_score:
(high_score, winner) = (score, node)
Expand Down
2 changes: 1 addition & 1 deletion pymemcache/fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"""


class FallbackClient(object):
class FallbackClient:
def __init__(self, caches):
assert len(caches) > 0
self.caches = caches
Expand Down
8 changes: 3 additions & 5 deletions pymemcache/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
import threading
import time

import six


class ObjectPool(object):
class ObjectPool:
"""A pool of objects that release/creates/destroys as needed."""

def __init__(self, obj_creator,
Expand All @@ -37,7 +35,7 @@ def __init__(self, obj_creator,
self._lock = lock_generator()
self._after_remove = after_remove
max_size = max_size or 2 ** 31
if not isinstance(max_size, six.integer_types) or max_size < 0:
if not isinstance(max_size, int) or max_size < 0:
raise ValueError('"max_size" must be a positive integer')
self.max_size = max_size
self.idle_timeout = idle_timeout
Expand All @@ -62,7 +60,7 @@ def get_and_release(self, destroy_on_fail=False):
self.release(obj)
else:
self.destroy(obj)
six.reraise(exc_info[0], exc_info[1], exc_info[2])
raise exc_info[1].with_traceback(exc_info[2])
self.release(obj)

def get(self):
Expand Down
Loading