-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[V3 RPC] Initial RPC library switch (#1634)
* Initial RPC library switch * Use weak refs to the methods so cog unload works * Add docs * Black fixes * Add jsonrpcserver to Pipfile.lock
- Loading branch information
Showing
8 changed files
with
170 additions
and
90 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,135 @@ | ||
import asyncio | ||
import weakref | ||
|
||
from aiohttp.web import Application | ||
from aiohttp_json_rpc import JsonRpc | ||
from aiohttp import web | ||
import jsonrpcserver.aio | ||
|
||
import inspect | ||
import logging | ||
|
||
from .utils import TYPE_CHECKING, NewType | ||
|
||
if TYPE_CHECKING: | ||
from .bot import Red | ||
__all__ = ["methods", "RPC", "Methods"] | ||
|
||
log = logging.getLogger("red.rpc") | ||
JsonSerializable = NewType("JsonSerializable", dict) | ||
|
||
_rpc = JsonRpc(logger=log) | ||
|
||
_rpc_server = None # type: asyncio.AbstractServer | ||
class Methods(jsonrpcserver.aio.AsyncMethods): | ||
""" | ||
Container class for all registered RPC methods, please use the existing `methods` | ||
attribute rather than creating a new instance of this class. | ||
.. warning:: | ||
async def initialize(bot: "Red"): | ||
global _rpc_server | ||
**NEVER** create a new instance of this class! | ||
""" | ||
|
||
app = Application(loop=bot.loop) | ||
app.router.add_route("*", "/rpc", _rpc) | ||
def __init__(self): | ||
super().__init__() | ||
|
||
handler = app.make_handler() | ||
self._items = weakref.WeakValueDictionary() | ||
|
||
_rpc_server = await bot.loop.create_server(handler, "127.0.0.1", 6133) | ||
def add(self, method, name: str = None): | ||
""" | ||
Registers a method to the internal RPC server making it available for | ||
RPC users to call. | ||
log.debug("Created RPC _rpc_server listener.") | ||
.. important:: | ||
Any method added here must take ONLY JSON serializable parameters and | ||
MUST return a JSON serializable object. | ||
def add_topic(topic_name: str): | ||
""" | ||
Adds a topic for clients to listen to. | ||
Parameters | ||
---------- | ||
method : function | ||
A reference to the function to register. | ||
Parameters | ||
---------- | ||
topic_name | ||
""" | ||
_rpc.add_topics(topic_name) | ||
name : str | ||
Name of the function as seen by the RPC clients. | ||
""" | ||
if not inspect.iscoroutinefunction(method): | ||
raise TypeError("Method must be a coroutine.") | ||
|
||
if name is None: | ||
name = method.__qualname__ | ||
|
||
def notify(topic_name: str, data: JsonSerializable): | ||
""" | ||
Publishes a notification for the given topic name to all listening clients. | ||
self._items[str(name)] = method | ||
|
||
data MUST be json serializable. | ||
def remove(self, *, name: str = None, method=None): | ||
""" | ||
Unregisters an RPC method. Either a name or reference to the method must | ||
be provided and name will take priority. | ||
note:: | ||
Parameters | ||
---------- | ||
name : str | ||
method : function | ||
""" | ||
if name and name in self._items: | ||
del self._items[name] | ||
|
||
This method will fail silently. | ||
elif method and method in self._items.values(): | ||
to_remove = [] | ||
for name, val in self._items.items(): | ||
if method == val: | ||
to_remove.append(name) | ||
|
||
Parameters | ||
---------- | ||
topic_name | ||
data | ||
""" | ||
_rpc.notify(topic_name, data) | ||
for name in to_remove: | ||
del self._items[name] | ||
|
||
def all_methods(self): | ||
""" | ||
Lists all available method names. | ||
def add_method(prefix, method): | ||
""" | ||
Makes a method available to RPC clients. The name given to clients will be as | ||
follows:: | ||
Returns | ||
------- | ||
list of str | ||
""" | ||
return self._items.keys() | ||
|
||
"{}__{}".format(prefix, method.__name__) | ||
|
||
note:: | ||
methods = Methods() | ||
|
||
This method will fail silently. | ||
|
||
Parameters | ||
---------- | ||
prefix | ||
method | ||
MUST BE A COROUTINE OR OBJECT. | ||
""" | ||
_rpc.add_methods(("", method), prefix=prefix) | ||
class BaseRPCMethodMixin: | ||
|
||
def __init__(self): | ||
methods.add(self.all_methods, name="all_methods") | ||
|
||
async def all_methods(self): | ||
return list(methods.all_methods()) | ||
|
||
|
||
def clean_up(): | ||
if _rpc_server is not None: | ||
_rpc_server.close() | ||
class RPC(BaseRPCMethodMixin): | ||
""" | ||
RPC server manager. | ||
""" | ||
|
||
def __init__(self, bot): | ||
self.app = web.Application(loop=bot.loop) | ||
self.app.router.add_post("/rpc", self.handle) | ||
|
||
self.app_handler = self.app.make_handler() | ||
|
||
self.server = None | ||
|
||
super().__init__() | ||
|
||
async def initialize(self): | ||
""" | ||
Finalizes the initialization of the RPC server and allows it to begin | ||
accepting queries. | ||
""" | ||
self.server = await self.app.loop.create_server(self.app_handler, "127.0.0.1", 6133) | ||
log.debug("Created RPC server listener.") | ||
|
||
def close(self): | ||
""" | ||
Closes the RPC server. | ||
""" | ||
self.server.close() | ||
|
||
async def handle(self, request): | ||
request = await request.text() | ||
response = await methods.dispatch(request) | ||
if response.is_notification: | ||
return web.Response() | ||
else: | ||
return web.json_response(response, status=response.http_status) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters