SDK-2762: Bind method params before calling

This commit is contained in:
Romuald Juchnowicz-Bierbasz
2019-04-29 15:45:03 +02:00
parent dc9fc2cc5d
commit 6e251c6eb9
10 changed files with 54 additions and 36 deletions

View File

@@ -2,6 +2,7 @@ import asyncio
from collections import namedtuple
from collections.abc import Iterable
import logging
import inspect
import json
class JsonRpcError(Exception):
@@ -46,7 +47,7 @@ class UnknownError(ApplicationError):
super().__init__(0, "Unknown error", data)
Request = namedtuple("Request", ["method", "params", "id"], defaults=[{}, None])
Method = namedtuple("Method", ["callback", "internal", "sensitive_params"])
Method = namedtuple("Method", ["callback", "signature", "internal", "sensitive_params"])
def anonymise_sensitive_params(params, sensitive_params):
anomized_data = "****"
@@ -81,7 +82,7 @@ class Server():
:param sensitive_params: list of parameters that will by anonymized before logging; if False - no params
are considered sensitive, if True - all params are considered sensitive
"""
self._methods[name] = Method(callback, internal, sensitive_params)
self._methods[name] = Method(callback, inspect.signature(callback), internal, sensitive_params)
def register_notification(self, name, callback, internal, sensitive_params=False):
"""
@@ -92,7 +93,7 @@ class Server():
:param sensitive_params: list of parameters that will by anonymized before logging; if False - no params
are considered sensitive, if True - all params are considered sensitive
"""
self._notifications[name] = Method(callback, internal, sensitive_params)
self._notifications[name] = Method(callback, inspect.signature(callback), internal, sensitive_params)
def register_eof(self, callback):
self._eof_listeners.append(callback)
@@ -138,15 +139,20 @@ class Server():
logging.error("Received unknown notification: %s", request.method)
return
callback, internal, sensitive_params = method
callback, signature, internal, sensitive_params = method
self._log_request(request, sensitive_params)
try:
bound_args = signature.bind(**request.params)
except TypeError:
self._send_error(request.id, InvalidParams())
if internal:
# internal requests are handled immediately
callback(**request.params)
callback(*bound_args.args, **bound_args.kwargs)
else:
try:
asyncio.create_task(callback(**request.params))
asyncio.create_task(callback(*bound_args.args, **bound_args.kwargs))
except Exception:
logging.exception("Unexpected exception raised in notification handler")
@@ -157,17 +163,22 @@ class Server():
self._send_error(request.id, MethodNotFound())
return
callback, internal, sensitive_params = method
callback, signature, internal, sensitive_params = method
self._log_request(request, sensitive_params)
try:
bound_args = signature.bind(**request.params)
except TypeError:
self._send_error(request.id, InvalidParams())
if internal:
# internal requests are handled immediately
response = callback(**request.params)
response = callback(*bound_args.args, **bound_args.kwargs)
self._send_response(request.id, response)
else:
async def handle():
try:
result = await callback(**request.params)
result = await callback(*bound_args.args, **bound_args.kwargs)
self._send_response(request.id, result)
except TypeError:
self._send_error(request.id, InvalidParams())

View File

@@ -1,5 +1,12 @@
from asyncio import coroutine
from unittest.mock import MagicMock
class AsyncMock(MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
def coroutine_mock():
coro = MagicMock(name="CoroutineResult")
corofunc = MagicMock(name="CoroutineFunction", side_effect=coroutine(coro))
corofunc.coro = coro
return corofunc

View File

@@ -6,7 +6,7 @@ import pytest
from galaxy.api.plugin import Plugin
from galaxy.api.consts import Platform
from galaxy.unittest.mock import AsyncMock
from galaxy.unittest.mock import AsyncMock, coroutine_mock
@pytest.fixture()
def reader():
@@ -57,7 +57,7 @@ def plugin(reader, writer):
with ExitStack() as stack:
for method in async_methods:
stack.enter_context(patch.object(Plugin, method, new_callable=AsyncMock))
stack.enter_context(patch.object(Plugin, method, new_callable=coroutine_mock))
for method in methods:
stack.enter_context(patch.object(Plugin, method))
yield Plugin(Platform.Generic, "0.1", reader, writer, "token")

View File

@@ -23,7 +23,7 @@ def test_success(plugin, readline, write):
}
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_unlocked_achievements.return_value = [
plugin.get_unlocked_achievements.coro.return_value = [
Achievement(achievement_id="lvl10", unlock_time=1548421241),
Achievement(achievement_name="Got level 20", unlock_time=1548422395),
Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633)
@@ -65,7 +65,7 @@ def test_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_unlocked_achievements.side_effect = UnknownError()
plugin.get_unlocked_achievements.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_unlocked_achievements.assert_called()
response = json.loads(write.call_args[0][0])

View File

@@ -18,7 +18,7 @@ def test_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.authenticate.return_value = Authentication("132", "Zenek")
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
asyncio.run(plugin.run())
plugin.authenticate.assert_called_with()
response = json.loads(write.call_args[0][0])
@@ -56,7 +56,7 @@ def test_failure(plugin, readline, write, error, code, message):
}
readline.side_effect = [json.dumps(request), ""]
plugin.authenticate.side_effect = error()
plugin.authenticate.coro.side_effect = error()
asyncio.run(plugin.run())
plugin.authenticate.assert_called_with()
response = json.loads(write.call_args[0][0])
@@ -82,7 +82,7 @@ def test_stored_credentials(plugin, readline, write):
}
}
readline.side_effect = [json.dumps(request), ""]
plugin.authenticate.return_value = Authentication("132", "Zenek")
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
asyncio.run(plugin.run())
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
write.assert_called()

View File

@@ -21,7 +21,7 @@ def test_send_message_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.send_message.return_value = None
plugin.send_message.coro.return_value = None
asyncio.run(plugin.run())
plugin.send_message.assert_called_with(room_id="14", message="Hello!")
response = json.loads(write.call_args[0][0])
@@ -52,7 +52,7 @@ def test_send_message_failure(plugin, readline, write, error, code, message):
}
readline.side_effect = [json.dumps(request), ""]
plugin.send_message.side_effect = error()
plugin.send_message.coro.side_effect = error()
asyncio.run(plugin.run())
plugin.send_message.assert_called_with(room_id="15", message="Bye")
response = json.loads(write.call_args[0][0])
@@ -78,7 +78,7 @@ def test_mark_as_read_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.mark_as_read.return_value = None
plugin.mark_as_read.coro.return_value = None
asyncio.run(plugin.run())
plugin.mark_as_read.assert_called_with(room_id="14", last_message_id="67")
response = json.loads(write.call_args[0][0])
@@ -114,7 +114,7 @@ def test_mark_as_read_failure(plugin, readline, write, error, code, message):
}
readline.side_effect = [json.dumps(request), ""]
plugin.mark_as_read.side_effect = error()
plugin.mark_as_read.coro.side_effect = error()
asyncio.run(plugin.run())
plugin.mark_as_read.assert_called_with(room_id="18", last_message_id="7")
response = json.loads(write.call_args[0][0])
@@ -136,7 +136,7 @@ def test_get_rooms_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_rooms.return_value = [
plugin.get_rooms.coro.return_value = [
Room("13", 0, None),
Room("15", 34, "8")
]
@@ -170,7 +170,7 @@ def test_get_rooms_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_rooms.side_effect = UnknownError()
plugin.get_rooms.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_rooms.assert_called_with()
response = json.loads(write.call_args[0][0])
@@ -196,7 +196,7 @@ def test_get_room_history_from_message_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_room_history_from_message.return_value = [
plugin.get_room_history_from_message.coro.return_value = [
Message("13", "149", 1549454837, "Hello"),
Message("14", "812", 1549454899, "Hi")
]
@@ -245,7 +245,7 @@ def test_get_room_history_from_message_failure(plugin, readline, write, error, c
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_room_history_from_message.side_effect = error()
plugin.get_room_history_from_message.coro.side_effect = error()
asyncio.run(plugin.run())
plugin.get_room_history_from_message.assert_called_with(room_id="33", message_id="88")
response = json.loads(write.call_args[0][0])
@@ -271,7 +271,7 @@ def test_get_room_history_from_timestamp_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_room_history_from_timestamp.return_value = [
plugin.get_room_history_from_timestamp.coro.return_value = [
Message("12", "155", 1549454836, "Bye")
]
asyncio.run(plugin.run())
@@ -308,7 +308,7 @@ def test_get_room_history_from_timestamp_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_room_history_from_timestamp.side_effect = UnknownError()
plugin.get_room_history_from_timestamp.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_room_history_from_timestamp.assert_called_with(
room_id="10",

View File

@@ -12,7 +12,7 @@ def test_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_game_times.return_value = [
plugin.get_game_times.coro.return_value = [
GameTime("3", 60, 1549550504),
GameTime("5", 10, 1549550502)
]
@@ -47,7 +47,7 @@ def test_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_game_times.side_effect = UnknownError()
plugin.get_game_times.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_game_times.assert_called_with()
response = json.loads(write.call_args[0][0])

View File

@@ -16,7 +16,7 @@ def test_success(plugin, readline, write):
readline.side_effect = [json.dumps(request), ""]
plugin.get_local_games.return_value = [
plugin.get_local_games.coro.return_value = [
LocalGame("1", LocalGameState.Running),
LocalGame("2", LocalGameState.Installed),
LocalGame("3", LocalGameState.Installed | LocalGameState.Running)
@@ -61,7 +61,7 @@ def test_failure(plugin, readline, write, error, code, message):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_local_games.side_effect = error()
plugin.get_local_games.coro.side_effect = error()
asyncio.run(plugin.run())
plugin.get_local_games.assert_called_with()
response = json.loads(write.call_args[0][0])

View File

@@ -13,7 +13,7 @@ def test_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_owned_games.return_value = [
plugin.get_owned_games.coro.return_value = [
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
Game(
"5",
@@ -75,7 +75,7 @@ def test_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_owned_games.side_effect = UnknownError()
plugin.get_owned_games.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_owned_games.assert_called_with()
response = json.loads(write.call_args[0][0])

View File

@@ -13,7 +13,7 @@ def test_get_friends_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_friends.return_value = [
plugin.get_friends.coro.return_value = [
UserInfo(
"3",
True,
@@ -74,7 +74,7 @@ def test_get_friends_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_friends.side_effect = UnknownError()
plugin.get_friends.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_friends.assert_called_with()
response = json.loads(write.call_args[0][0])
@@ -164,7 +164,7 @@ def test_get_users_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_users.return_value = [
plugin.get_users.coro.return_value = [
UserInfo("5", False, "Ula", "http://avatar.png", Presence(PresenceState.Offline))
]
asyncio.run(plugin.run())
@@ -200,7 +200,7 @@ def test_get_users_failure(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_users.side_effect = UnknownError()
plugin.get_users.coro.side_effect = UnknownError()
asyncio.run(plugin.run())
plugin.get_users.assert_called_with(user_id_list=["10", "11", "12"])
response = json.loads(write.call_args[0][0])