Skip to content

Commit 2b60dbf

Browse files
committed
test: add Python 3 support
Python 2 support is EOL [1] and we should support to run our python tests using Python 3.x. test-run used as test-runner for memcached already supports Python 3, see [2]. Patch adds support of running tests with Python 3 without breaking compatibility with Python 2. Testing on CI switched to Python 3. 1. https://www.python.org/doc/sunset-python-2/ 2. tarantool/test-run#20 Closes #82
1 parent cdb99b7 commit 2b60dbf

21 files changed

+218
-150
lines changed

.github/workflows/testing.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ jobs:
3333

3434
- run: cmake .
3535

36-
- name: Setup Python 2 for tests
36+
- name: Setup Python 3 for tests
3737
uses: actions/setup-python@v2
3838
with:
39-
python-version: 2.7
39+
python-version: 3.8
4040

4141
- name: Install test requirements
4242
run: pip install -r test-run/requirements.txt

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ Memcached protocol 'wrapper' for Tarantool.
1414

1515
* Tarantol 1.6.8+ with header files (tarantool && tarantool-dev packages).
1616
* Cyrus SASL library (with header files)
17-
* Python >= 2.7, <3 with next packages (for testing only):
17+
* Python >= 2.7 with next packages (for testing only):
1818
- PyYAML
1919
- msgpack-python
2020
- six==1.9.0
21+
- gevent==1.1b5
2122

2223
### Installation
2324

@@ -149,7 +150,7 @@ Usual rules for memcached are applicable for this plugin:
149150
import bmemcached
150151
client = bmemcached.Client(('127.0.0.1:11211', ), 'testuser', 'testpasswd')
151152
client.set('key', 'value')
152-
print client.get('key')
153+
print(client.get('key'))
153154
```
154155

155156
For custom configuration file path, please, use `SASL_CONF_PATH` environment variable.

test-run

Submodule test-run updated 71 files

test/binary/binary-boundary.test.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
from internal.memcached_connection import MemcachedBinaryConnection
1111
from internal.memcached_connection import STATUS, COMMANDS
1212

13+
from internal.compat import string_types
14+
1315
mc = MemcachedBinaryConnection("127.0.0.1", iproto.py_con.port)
1416

1517
def iequal(left, right, level = 1):
1618
if (left != right):
1719
tb = traceback.extract_stack()[-(level + 1)]
18-
print "Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
19-
repr(left), repr(right))
20-
if (isinstance(left, basestring)):
20+
print("Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
21+
repr(left), repr(right)))
22+
if (isinstance(left, string_types)):
2123
if (len(left) != len(right)):
2224
print("length is different")
2325
return False
@@ -39,7 +41,7 @@ def check(key, flags, val, level = 0):
3941
val = "x" * i
4042
mc.setq(key, val, flags=82, nosend=True)
4143
mc.setq("alt_%s" % key, "blah", flags=82, nosend=True)
42-
data = "".join(mc.commands)
44+
data = b"".join(mc.commands)
4345
mc.commands = []
4446

4547
if (len(data) > 2024):

test/binary/binary-expire.test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
def iequal(left, right, level = 1):
1616
if (left != right):
1717
tb = traceback.extract_stack()[-(level + 1)]
18-
print "Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
19-
repr(left), repr(right))
18+
print("Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
19+
repr(left), repr(right)))
2020
return False
2121
return True
2222

2323
def issert(stmt, level = 1):
2424
if not bool(stmt):
2525
tb = traceback.extract_stack()[-(level + 1)]
26-
print "Error on line %s:%d: result is False" % (tb[0], tb[1])
26+
print("Error on line %s:%d: result is False" % (tb[0], tb[1]))
2727
return False
2828
return True
2929

@@ -41,15 +41,15 @@ def check(key, flags, val, level = 0):
4141

4242
stat = mc.stat("reset")
4343

44-
for i in xrange(10000):
44+
for i in range(10000):
4545
mc.set('key-%d' % i, 'value-%d' % i, expire=1)
4646

4747
stat = mc.stat()
48-
while int(stat.get('evictions', '0')) < 10000:
48+
while int(stat.get(b'evictions', '0')) < 10000:
4949
time.sleep(0.01)
5050
stat = mc.stat()
5151

52-
issert('evictions' in stat)
53-
iequal(int(mc.stat().get('evictions', 0)), 10000)
52+
issert(b'evictions' in stat)
53+
iequal(int(mc.stat().get(b'evictions', 0)), 10000)
5454

5555
sys.path = saved_path

test/binary/binary-toobig.test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@
1414
def iequal(left, right, level = 1):
1515
if (left != right):
1616
tb = traceback.extract_stack()[-(level + 1)]
17-
print "Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
18-
repr(left), repr(right))
17+
print("Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
18+
repr(left), repr(right)))
1919
return False
2020
return True
2121

2222
def inequal(left, right, level = 0):
2323
if (left == right):
2424
tb = traceback.extract_stack()[-(level + 1)]
25-
print "Error on line %s:%d: %s equal %s" % (tb[0], tb[1],
26-
repr(left), repr(right))
25+
print("Error on line %s:%d: %s equal %s" % (tb[0], tb[1],
26+
repr(left), repr(right)))
2727
return False
2828
return True
2929

3030
def issert(stmt, level = 0):
3131
if bool(stmt):
3232
tb = traceback.extract_stack()[-(level + 1)]
33-
print "Error on line %s:%d: is False" % (tb[0], tb[1],
34-
repr(left), repr(right))
33+
print("Error on line %s:%d: is False" % (tb[0], tb[1],
34+
repr(left), repr(right)))
3535
return False
3636
return True
3737

test/binary/binary.test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@
1414
def iequal(left, right, level = 1):
1515
if (left != right):
1616
tb = traceback.extract_stack()[-(level + 1)]
17-
print "Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
18-
repr(left), repr(right))
17+
print("Error on line %s:%d: %s not equal %s" % (tb[0], tb[1],
18+
repr(left), repr(right)))
1919
return False
2020
return True
2121

2222
def inequal(left, right, level = 0):
2323
if (left == right):
2424
tb = traceback.extract_stack()[-(level + 1)]
25-
print "Error on line %s:%d: %s equal %s" % (tb[0], tb[1],
26-
repr(left), repr(right))
25+
print("Error on line %s:%d: %s equal %s" % (tb[0], tb[1],
26+
repr(left), repr(right)))
2727
return False
2828
return True
2929

3030
def issert(stmt, level = 0):
3131
if bool(stmt):
3232
tb = traceback.extract_stack()[-(level + 1)]
33-
print "Error on line %s:%d: is False" % (tb[0], tb[1],
34-
repr(left), repr(right))
33+
print("Error on line %s:%d: is False" % (tb[0], tb[1],
34+
repr(left), repr(right)))
3535
return False
3636
return True
3737

@@ -251,7 +251,7 @@ def check_empty_response(con):
251251
empty("y")
252252

253253
stat2 = mc.stat()
254-
iequal(int(stat1['cmd_flush']) + 1, int(stat2['cmd_flush']))
254+
iequal(int(stat1[b'cmd_flush']) + 1, int(stat2[b'cmd_flush']))
255255

256256
print("""#----------------------------# diagnostics append #---------------------------#""")
257257
key, value, suffix = "appendkey", "some value", " more"

test/binary/suite.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
core = tarantool
33
script = binary.lua
44
description = memcached binary tests
5-
disabled = binary-expire.test.py binary-boundary.test.py
5+
disabled = binary-boundary.test.py

test/capable/capable-binary.test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from internal.memcached_connection import MemcachedBinaryConnection
1313
from internal.memcached_connection import STATUS, COMMANDS
1414

15+
from internal.compat import bytes_to_str
16+
1517
mc = MemcachedBinaryConnection("127.0.0.1", iproto.py_con.port)
1618
mc.flush()
1719

@@ -20,6 +22,7 @@
2022

2123
task = Popen(cmd, stdout=PIPE, stderr=STDOUT)
2224

23-
print task.communicate()[0]
25+
testcase = task.communicate()[0]
26+
print(bytes_to_str(testcase))
2427

2528
sys.path = saved_path

test/capable/capable-text.test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from internal.memcached_connection import MemcachedBinaryConnection
1313
from internal.memcached_connection import STATUS, COMMANDS
1414

15+
from internal.compat import bytes_to_str
16+
1517
mc = MemcachedBinaryConnection("127.0.0.1", iproto.py_con.port)
1618
mc.flush()
1719

@@ -20,6 +22,7 @@
2022

2123
task = Popen(cmd, stdout=PIPE, stderr=STDOUT)
2224

23-
print task.communicate()[0]
25+
testcase = task.communicate()[0]
26+
print(bytes_to_str(testcase))
2427

2528
sys.path = saved_path

test/internal/compat.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import sys
2+
3+
# Added for compatibility with Python 3.
4+
# Python 2 provided the class basestring, from which both str and unicode
5+
# derived. In Python 3, the concept of basestring makes no sense: text is only
6+
# represented by str.
7+
try:
8+
basestring
9+
except NameError:
10+
basestring = str
11+
12+
# Useful for very coarse version differentiation.
13+
PY3 = sys.version_info[0] == 3
14+
PY2 = sys.version_info[0] == 2
15+
16+
if PY3:
17+
string_types = str,
18+
integer_types = int,
19+
else:
20+
string_types = basestring, # noqa: F821
21+
integer_types = (int, long) # noqa: F821
22+
23+
def assert_bytes(b):
24+
""" Ensure given value is <bytes>.
25+
"""
26+
if type(b) != bytes:
27+
raise ValueError('Internal error: expected {}, got {}: {}'.format(
28+
str(bytes), str(type(b)), repr(b)))
29+
30+
def assert_str(s):
31+
""" Ensure given value is <str>.
32+
"""
33+
if type(s) != str:
34+
raise ValueError('Internal error: expected {}, got {}: {}'.format(
35+
str(str), str(type(s)), repr(s)))
36+
37+
def bytes_to_str(b):
38+
""" Convert <bytes> to <str>.
39+
40+
No-op on Python 2.
41+
"""
42+
assert_bytes(b)
43+
if PY2:
44+
return b
45+
return b.decode('utf-8')
46+
47+
def str_to_bytes(s):
48+
""" Convert <str> to <bytes>.
49+
50+
No-op on Python 2.
51+
"""
52+
assert_str(s)
53+
if PY2:
54+
return s
55+
return s.encode('utf-8')

test/internal/memcached_connection.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from struct import Struct
55

66
from lib.tarantool_connection import TarantoolConnection
7+
from internal.compat import bytes_to_str, str_to_bytes
78

89
HEADER = '!BBHBBHLLQ'
910
HEADER_STRUCT = Struct(HEADER)
@@ -165,7 +166,7 @@ def __init__(self, host, port):
165166
self.connect()
166167

167168
def send(self):
168-
commands = ''.join(self.commands)
169+
commands = b''.join(self.commands)
169170
self.socket.sendall(commands)
170171
self.commands = []
171172

@@ -202,8 +203,8 @@ def _read_and_parse_response(self):
202203
assert(magic == MAGIC['response'])
203204
if status == STATUS['OK']:
204205
assert(ext_len == ext_lenv)
205-
assert(((key_lenv > 0 or key_lenv is None) and key_len > 0) or key_len == 0)
206-
assert(((val_lenv > 0 or val_lenv is None) and val_len > 0) or val_len == 0)
206+
assert(((key_lenv is None or key_lenv > 0) and key_len > 0) or key_len == 0)
207+
assert(((val_lenv is None or val_lenv > 0) and val_len > 0) or val_len == 0)
207208
else:
208209
assert(val_len > 0)
209210

@@ -238,7 +239,10 @@ def _read_and_parse_response(self):
238239
if is_indecrq(op):
239240
val = INDECR_STRUCT.unpack_from(val)[0]
240241
if val is not None:
241-
retval['val'] = val
242+
if isinstance(val, bytes):
243+
retval['val'] = bytes_to_str(val)
244+
else:
245+
retval['val'] = val
242246

243247
return retval
244248

@@ -268,9 +272,9 @@ def append_query(self, cmd, args):
268272
retval = [
269273
struct.pack(MAGIC['request'], op, key_len, ext_len, dtype, 0,
270274
tot_len, opaque, cas, *extra),
271-
key, val
275+
str_to_bytes(key), str_to_bytes(val)
272276
]
273-
cmd = ''.join(retval)
277+
cmd = b''.join(retval)
274278
self.commands.append(cmd)
275279

276280
@binary_decorator
@@ -559,9 +563,9 @@ def execute_no_reconnect(self, commands, silent = False):
559563

560564
def send(self, commands, silent = False):
561565
self.commands = commands
562-
self.socket.sendall(commands)
566+
self.socket.sendall(str_to_bytes(commands))
563567
if not silent:
564-
print "<<" + '-' * 50
568+
print("<<" + '-' * 50)
565569
sys.stdout.write(self.commands.strip() + '\n')
566570
#sys.stdout.write(self.commands)
567571

@@ -595,7 +599,7 @@ def recv(self, silent = False):
595599
self.reply_unknown(cmd)
596600

597601
if not silent:
598-
print ">>" + '-'*50
602+
print(">>" + '-'*50)
599603
sys.stdout.write(self.reply.strip() + '\n')
600604
#sys.stdout.write(self.reply)
601605

@@ -643,7 +647,7 @@ def reply_retrieval(self, cmd):
643647
break
644648
else:
645649
# unknown
646-
print "error: unknown line: '%s'" % key
650+
print("error: unknown line: '%s'" % key)
647651
self.reply += "error: unknown line: '%s'" % key
648652
break
649653

@@ -699,7 +703,7 @@ def read_line(self):
699703
index = buf.find(MEMCACHED_SEPARATOR)
700704
if index > 0:
701705
break
702-
data = self.socket.recv(1048576)
706+
data = bytes_to_str(self.socket.recv(1048576))
703707
if not data:
704708
return None
705709
buf += data

0 commit comments

Comments
 (0)