Compare commits

..

4 Commits
0.49 ... 0.51

Author SHA1 Message Date
Romuald Bierbasz
4d3c9b78c4 Add RegistryMonitor 2019-09-25 11:46:18 +02:00
Romuald Juchnowicz-Bierbasz
392e4c5f68 Print error data in import notifications 2019-09-19 14:47:50 +02:00
Romuald Juchnowicz-Bierbasz
4d6d3b8eb2 Increment version 2019-09-18 12:16:34 +02:00
Romuald Juchnowicz-Bierbasz
d5610221a9 Do not wait for external tasks in run 2019-09-18 12:15:18 +02:00
10 changed files with 126 additions and 30 deletions

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="galaxy.plugin.api",
version="0.49",
version="0.51",
description="GOG Galaxy Integrations Python API",
author='Galaxy team',
author_email='galaxy@gog.com',

View File

@@ -18,6 +18,17 @@ class JsonRpcError(Exception):
def __eq__(self, other):
return self.code == other.code and self.message == other.message and self.data == other.data
def json(self):
obj = {
"code": self.code,
"message": self.message
}
if self.data is not None:
obj["error"]["data"] = self.data
return obj
class ParseError(JsonRpcError):
def __init__(self):
super().__init__(-32700, "Parse error")
@@ -232,15 +243,9 @@ class Server():
response = {
"jsonrpc": "2.0",
"id": request_id,
"error": {
"code": error.code,
"message": error.message
}
"error": error.json()
}
if error.data is not None:
response["error"]["data"] = error.data
self._send(response)
@staticmethod

View File

@@ -174,7 +174,6 @@ class Plugin:
async def run(self):
"""Plugin's main coroutine."""
await self._server.run()
await self._external_task_manager.wait()
def close(self) -> None:
if not self._active:
@@ -332,10 +331,7 @@ class Plugin:
def _game_achievements_import_failure(self, game_id: str, error: ApplicationError) -> None:
params = {
"game_id": game_id,
"error": {
"code": error.code,
"message": error.message
}
"error": error.json()
}
self._notification_client.notify("game_achievements_import_failure", params)
@@ -399,10 +395,7 @@ class Plugin:
def _game_time_import_failure(self, game_id: str, error: ApplicationError) -> None:
params = {
"game_id": game_id,
"error": {
"code": error.code,
"message": error.message
}
"error": error.json()
}
self._notification_client.notify("game_time_import_failure", params)

View File

@@ -0,0 +1,98 @@
import platform
if platform.system().lower() == "windows":
import logging
import ctypes
from ctypes.wintypes import LONG, HKEY, LPCWSTR, DWORD, BOOL, HANDLE, LPVOID
LPSECURITY_ATTRIBUTES = LPVOID
RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW
RegOpenKeyEx.restype = LONG
RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)]
RegCloseKey = ctypes.windll.advapi32.RegCloseKey
RegCloseKey.restype = LONG
RegCloseKey.argtypes = [HKEY]
RegNotifyChangeKeyValue = ctypes.windll.advapi32.RegNotifyChangeKeyValue
RegNotifyChangeKeyValue.restype = LONG
RegNotifyChangeKeyValue.argtypes = [HKEY, BOOL, DWORD, HANDLE, BOOL]
CloseHandle = ctypes.windll.kernel32.CloseHandle
CloseHandle.restype = BOOL
CloseHandle.argtypes = [HANDLE]
CreateEvent = ctypes.windll.kernel32.CreateEventW
CreateEvent.restype = BOOL
CreateEvent.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
WaitForSingleObject.restype = DWORD
WaitForSingleObject.argtypes = [HANDLE, DWORD]
ERROR_SUCCESS = 0x00000000
KEY_READ = 0x00020019
KEY_QUERY_VALUE = 0x00000001
REG_NOTIFY_CHANGE_NAME = 0x00000001
REG_NOTIFY_CHANGE_LAST_SET = 0x00000004
WAIT_OBJECT_0 = 0x00000000
WAIT_TIMEOUT = 0x00000102
class RegistryMonitor:
def __init__(self, root, subkey):
self._root = root
self._subkey = subkey
self._event = CreateEvent(None, False, False, None)
self._key = None
self._open_key()
if self._key:
self._set_key_update_notification()
def close(self):
CloseHandle(self._event)
if self._key:
RegCloseKey(self._key)
self._key = None
def is_updated(self):
wait_result = WaitForSingleObject(self._event, 0)
# previously watched
if wait_result == WAIT_OBJECT_0:
self._set_key_update_notification()
return True
# no changes or no key before
if wait_result != WAIT_TIMEOUT:
# unexpected error
logging.warning("Unexpected WaitForSingleObject result %s", wait_result)
return False
if self._key is None:
self._open_key()
if self._key is None:
return False
self._set_key_update_notification()
return True
def _set_key_update_notification(self):
filter_ = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET
status = RegNotifyChangeKeyValue(self._key, True, filter_, self._event, True)
if status != ERROR_SUCCESS:
# key was deleted
RegCloseKey(self._key)
self._key = None
def _open_key(self):
access = KEY_QUERY_VALUE | KEY_READ
self._key = HKEY()
rc = RegOpenKeyEx(self._root, self._subkey, 0, access, ctypes.byref(self._key))
if rc != ERROR_SUCCESS:
self._key = None

View File

@@ -135,7 +135,7 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
"game_ids": ["14"]
}
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
await plugin.run()
@@ -176,7 +176,7 @@ async def test_import_in_progress(plugin, read, write):
read.side_effect = [
async_return_value(create_message(requests[0])),
async_return_value(create_message(requests[1])),
async_return_value(b"")
async_return_value(b"", 10)
]
await plugin.run()

View File

@@ -17,7 +17,7 @@ async def test_success(plugin, read, write):
"id": "3",
"method": "init_authentication"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
await plugin.run()
plugin.authenticate.assert_called_with()
@@ -55,7 +55,7 @@ async def test_failure(plugin, read, write, error, code, message):
"method": "init_authentication"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.authenticate.side_effect = error()
await plugin.run()
plugin.authenticate.assert_called_with()
@@ -84,7 +84,7 @@ async def test_stored_credentials(plugin, read, write):
}
}
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
await plugin.run()
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})

View File

@@ -15,7 +15,7 @@ async def test_get_friends_success(plugin, read, write):
"method": "import_friends"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_friends.return_value = async_return_value([
FriendInfo("3", "Jan"),
FriendInfo("5", "Ola")
@@ -45,7 +45,7 @@ async def test_get_friends_failure(plugin, read, write):
"method": "import_friends"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_friends.side_effect = UnknownError()
await plugin.run()
plugin.get_friends.assert_called_with()

View File

@@ -135,7 +135,7 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
"game_ids": ["6"]
}
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
await plugin.run()
assert get_messages(write) == [
@@ -174,7 +174,7 @@ async def test_import_in_progress(plugin, read, write):
read.side_effect = [
async_return_value(create_message(requests[0])),
async_return_value(create_message(requests[1])),
async_return_value(b"")
async_return_value(b"", 10)
]
await plugin.run()

View File

@@ -15,7 +15,7 @@ async def test_success(plugin, read, write):
"id": "3",
"method": "import_local_games"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_local_games.return_value = async_return_value([
LocalGame("1", LocalGameState.Running),
@@ -63,7 +63,7 @@ async def test_failure(plugin, read, write, error, code, message):
"id": "3",
"method": "import_local_games"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_local_games.side_effect = error()
await plugin.run()
plugin.get_local_games.assert_called_with()

View File

@@ -15,7 +15,7 @@ async def test_success(plugin, read, write):
"id": "3",
"method": "import_owned_games"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_owned_games.return_value = async_return_value([
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
@@ -80,7 +80,7 @@ async def test_failure(plugin, read, write):
"method": "import_owned_games"
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
plugin.get_owned_games.side_effect = UnknownError()
await plugin.run()
plugin.get_owned_games.assert_called_with()