Skip to content

Commit c793b89

Browse files
committed
Fix issues 130 and 131: Support Lua in Python 3 and improve mock signatures
1 parent ba41d90 commit c793b89

File tree

5 files changed

+75
-13
lines changed

5 files changed

+75
-13
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ Use pip:
1414
## Usage
1515

1616
Both `mockredis.mock_redis_client` and `mockredis.mock_strict_redis_client` can be
17-
used to patch instances of the *redis client*.
17+
used to patch instances of the *redis client*, and `get_mock_redis_client_creator`
18+
can be used to create a generator for more flexible mocks.
1819

1920
For example, using the [mock][mock] library:
2021

@@ -24,6 +25,11 @@ Or:
2425

2526
@patch('redis.StrictRedis', mock_strict_redis_client)
2627

28+
Or, for more control:
29+
30+
@patch('redis.Redis', get_mock_redis_client_creator(load_lua_dependencies=False))
31+
@patch('redis.StrictRedis', get_mock_redis_client_creator(strict=True, clock=my_frozen_clock))
32+
2733
## Testing
2834

2935
Many unit tests exist to verify correctness of mock functionality. In addition, most

mockredis/client.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,7 +1561,7 @@ def get_total_milliseconds(td):
15611561
return int((td.days * 24 * 60 * 60 + td.seconds) * 1000 + td.microseconds / 1000.0)
15621562

15631563

1564-
def mock_redis_client(**kwargs):
1564+
def mock_redis_client(*_, **__):
15651565
"""
15661566
Mock common.util.redis_client so we
15671567
can return a MockRedis object
@@ -1572,7 +1572,7 @@ def mock_redis_client(**kwargs):
15721572
mock_redis_client.from_url = mock_redis_client
15731573

15741574

1575-
def mock_strict_redis_client(**kwargs):
1575+
def mock_strict_redis_client(*_, **__):
15761576
"""
15771577
Mock common.util.redis_client so we
15781578
can return a MockRedis object
@@ -1581,3 +1581,22 @@ def mock_strict_redis_client(**kwargs):
15811581
return MockRedis(strict=True)
15821582

15831583
mock_strict_redis_client.from_url = mock_strict_redis_client
1584+
1585+
1586+
def get_mock_redis_client_creator(**kwargs):
1587+
"""
1588+
Generate a getter for a MockRedis
1589+
object that passes the given kwargs
1590+
to each MockRedis object instantiated
1591+
by the getter returned. Sample usage:
1592+
1593+
@mock.patch('redis.Redis', get_mock_redis_client_creator(load_lua_dependencies=False))
1594+
@mock.patch('redis.StrictRedis', get_mock_redis_client_creator(strict=True, clock=frozen_clock))
1595+
"""
1596+
1597+
def _getter(*_, **__):
1598+
return MockRedis(**kwargs)
1599+
1600+
_getter.from_url = _getter
1601+
1602+
return _getter

mockredis/script.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,26 @@
22
import threading
33
from mockredis.exceptions import ResponseError
44

5+
56
LuaLock = threading.Lock()
67

8+
if sys.version_info[0] == 3:
9+
_string_types = (str, )
10+
_integer_types = (int, )
11+
_number_types = (int, float)
12+
_string_or_binary_types = (str, bytes)
13+
_binary_type = bytes
14+
_long_type = int
15+
_iteritems = lambda d, **kw: iter(d.items(**kw))
16+
else:
17+
_string_types = (basestring, )
18+
_integer_types = (int, long)
19+
_number_types = (int, long, float)
20+
_string_or_binary_types = (basestring, )
21+
_binary_type = str
22+
_long_type = long
23+
_iteritems = lambda d, **kw: d.iteritems(**kw)
24+
725

826
class Script(object):
927
"""
@@ -47,7 +65,14 @@ def _call(*call_args):
4765
response = client.call(*call_args)
4866
return self._python_to_lua(response)
4967

50-
lua_globals.redis = {"call": _call}
68+
def _reply_table(field, message):
69+
return lua.eval("{{{field}='{message}'}}".format(field=field, message=message))
70+
71+
lua_globals.redis = {
72+
'call': _call,
73+
'status_reply': lambda status: _reply_table('ok', status),
74+
'error_reply': lambda error: _reply_table('err', error),
75+
}
5176
return self._lua_to_python(lua.execute(self.script), return_status=True)
5277

5378
@staticmethod
@@ -117,9 +142,9 @@ def _lua_to_python(lval, return_status=False):
117142
raise ResponseError(lval[i])
118143
pval.append(Script._lua_to_python(lval[i]))
119144
return pval
120-
elif isinstance(lval, long):
145+
elif isinstance(lval, _integer_types):
121146
# Lua number --> Python long
122-
return long(lval)
147+
return _long_type(lval)
123148
elif isinstance(lval, float):
124149
# Lua number --> Python float
125150
return float(lval)
@@ -161,17 +186,17 @@ def _python_to_lua(pval):
161186
# in Lua returns: {k1, v1, k2, v2, k3, v3}
162187
lua_dict = lua.eval("{}")
163188
lua_table = lua.eval("table")
164-
for k, v in pval.iteritems():
189+
for k, v in _iteritems(pval):
165190
lua_table.insert(lua_dict, Script._python_to_lua(k))
166191
lua_table.insert(lua_dict, Script._python_to_lua(v))
167192
return lua_dict
168-
elif isinstance(pval, str):
193+
elif isinstance(pval, _string_or_binary_types):
169194
# Python string --> Lua userdata
170195
return pval
171196
elif isinstance(pval, bool):
172197
# Python bool--> Lua boolean
173198
return lua.eval(str(pval).lower())
174-
elif isinstance(pval, (int, long, float)):
199+
elif isinstance(pval, _number_types):
175200
# Python int --> Lua number
176201
lua_globals = lua.globals()
177202
return lua_globals.tonumber(str(pval))

mockredis/tests/test_script.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
VAL1, VAL2, VAL3, VAL4,
1818
LPOP_SCRIPT
1919
)
20-
from mockredis.tests.fixtures import raises_response_error
2120

2221

2322
if sys.version_info >= (3, 0):
@@ -383,11 +382,24 @@ def test_lua_ok_return(self):
383382
script = self.redis.register_script(script_content)
384383
eq_('OK', script())
385384

386-
@raises_response_error
387385
def test_lua_err_return(self):
388386
script_content = "return {err='ERROR Some message'}"
389387
script = self.redis.register_script(script_content)
390-
script()
388+
with assert_raises(Exception) as error_context:
389+
script()
390+
eq_('ERROR Some message', error_context.exception.args[0])
391+
392+
def test_lua_redis_status_reply(self):
393+
script_content = "return redis.status_reply('OK')"
394+
script = self.redis.register_script(script_content)
395+
eq_('OK', script())
396+
397+
def test_lua_redis_error_reply(self):
398+
script_content = "return redis.error_reply('my error')"
399+
script = self.redis.register_script(script_content)
400+
with assert_raises(Exception) as error_context:
401+
script()
402+
eq_('my error', error_context.exception.args[0])
391403

392404
def test_concurrent_lua(self):
393405
script_content = """

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
'nose'
1919
],
2020
extras_require={
21-
'lua': ['lunatic-python-bugfix==1.1.1'],
21+
'lua': ['lunatic-python-universal~=2.0'],
2222
},
2323
tests_require=[
2424
'redis>=2.9.0'

0 commit comments

Comments
 (0)