Skip to content

Commit

Permalink
Fix issues 130 and 131: Support Lua in Python 3 and improve mock sign…
Browse files Browse the repository at this point in the history
…atures
  • Loading branch information
beamerblvd committed Nov 1, 2017
1 parent ba41d90 commit c793b89
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 13 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Use pip:
## Usage

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

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

Expand All @@ -24,6 +25,11 @@ Or:

@patch('redis.StrictRedis', mock_strict_redis_client)

Or, for more control:

@patch('redis.Redis', get_mock_redis_client_creator(load_lua_dependencies=False))
@patch('redis.StrictRedis', get_mock_redis_client_creator(strict=True, clock=my_frozen_clock))

## Testing

Many unit tests exist to verify correctness of mock functionality. In addition, most
Expand Down
23 changes: 21 additions & 2 deletions mockredis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,7 @@ def get_total_milliseconds(td):
return int((td.days * 24 * 60 * 60 + td.seconds) * 1000 + td.microseconds / 1000.0)


def mock_redis_client(**kwargs):
def mock_redis_client(*_, **__):
"""
Mock common.util.redis_client so we
can return a MockRedis object
Expand All @@ -1572,7 +1572,7 @@ def mock_redis_client(**kwargs):
mock_redis_client.from_url = mock_redis_client


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

mock_strict_redis_client.from_url = mock_strict_redis_client


def get_mock_redis_client_creator(**kwargs):
"""
Generate a getter for a MockRedis
object that passes the given kwargs
to each MockRedis object instantiated
by the getter returned. Sample usage:
@mock.patch('redis.Redis', get_mock_redis_client_creator(load_lua_dependencies=False))
@mock.patch('redis.StrictRedis', get_mock_redis_client_creator(strict=True, clock=frozen_clock))
"""

def _getter(*_, **__):
return MockRedis(**kwargs)

_getter.from_url = _getter

return _getter
37 changes: 31 additions & 6 deletions mockredis/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,26 @@
import threading
from mockredis.exceptions import ResponseError


LuaLock = threading.Lock()

if sys.version_info[0] == 3:
_string_types = (str, )
_integer_types = (int, )
_number_types = (int, float)
_string_or_binary_types = (str, bytes)
_binary_type = bytes
_long_type = int
_iteritems = lambda d, **kw: iter(d.items(**kw))
else:
_string_types = (basestring, )
_integer_types = (int, long)
_number_types = (int, long, float)
_string_or_binary_types = (basestring, )
_binary_type = str
_long_type = long
_iteritems = lambda d, **kw: d.iteritems(**kw)


class Script(object):
"""
Expand Down Expand Up @@ -47,7 +65,14 @@ def _call(*call_args):
response = client.call(*call_args)
return self._python_to_lua(response)

lua_globals.redis = {"call": _call}
def _reply_table(field, message):
return lua.eval("{{{field}='{message}'}}".format(field=field, message=message))

lua_globals.redis = {
'call': _call,
'status_reply': lambda status: _reply_table('ok', status),
'error_reply': lambda error: _reply_table('err', error),
}
return self._lua_to_python(lua.execute(self.script), return_status=True)

@staticmethod
Expand Down Expand Up @@ -117,9 +142,9 @@ def _lua_to_python(lval, return_status=False):
raise ResponseError(lval[i])
pval.append(Script._lua_to_python(lval[i]))
return pval
elif isinstance(lval, long):
elif isinstance(lval, _integer_types):
# Lua number --> Python long
return long(lval)
return _long_type(lval)
elif isinstance(lval, float):
# Lua number --> Python float
return float(lval)
Expand Down Expand Up @@ -161,17 +186,17 @@ def _python_to_lua(pval):
# in Lua returns: {k1, v1, k2, v2, k3, v3}
lua_dict = lua.eval("{}")
lua_table = lua.eval("table")
for k, v in pval.iteritems():
for k, v in _iteritems(pval):
lua_table.insert(lua_dict, Script._python_to_lua(k))
lua_table.insert(lua_dict, Script._python_to_lua(v))
return lua_dict
elif isinstance(pval, str):
elif isinstance(pval, _string_or_binary_types):
# Python string --> Lua userdata
return pval
elif isinstance(pval, bool):
# Python bool--> Lua boolean
return lua.eval(str(pval).lower())
elif isinstance(pval, (int, long, float)):
elif isinstance(pval, _number_types):
# Python int --> Lua number
lua_globals = lua.globals()
return lua_globals.tonumber(str(pval))
Expand Down
18 changes: 15 additions & 3 deletions mockredis/tests/test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
VAL1, VAL2, VAL3, VAL4,
LPOP_SCRIPT
)
from mockredis.tests.fixtures import raises_response_error


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

@raises_response_error
def test_lua_err_return(self):
script_content = "return {err='ERROR Some message'}"
script = self.redis.register_script(script_content)
script()
with assert_raises(Exception) as error_context:
script()
eq_('ERROR Some message', error_context.exception.args[0])

def test_lua_redis_status_reply(self):
script_content = "return redis.status_reply('OK')"
script = self.redis.register_script(script_content)
eq_('OK', script())

def test_lua_redis_error_reply(self):
script_content = "return redis.error_reply('my error')"
script = self.redis.register_script(script_content)
with assert_raises(Exception) as error_context:
script()
eq_('my error', error_context.exception.args[0])

def test_concurrent_lua(self):
script_content = """
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'nose'
],
extras_require={
'lua': ['lunatic-python-bugfix==1.1.1'],
'lua': ['lunatic-python-universal~=2.0'],
},
tests_require=[
'redis>=2.9.0'
Expand Down

0 comments on commit c793b89

Please sign in to comment.