Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use custom MethodNotFoundError instead of KeyError
* Avoid responding with a method not found error if a KeyError is raised
  in a method implementation.
  • Loading branch information
st31ny committed Oct 27, 2019
commit cf7f312582838481e0eff8fb1af27bccf5a870ec
2 changes: 1 addition & 1 deletion jsonrpcserver/async_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def call(method: Method, *args: Any, **kwargs: Any) -> Any:
async def safe_call(request: Request, methods: Methods, *, debug: bool) -> Response:
with handle_exceptions(request, debug) as handler:
result = await call(
methods.items[request.method], *request.args, **request.kwargs
methods.lookup(request.method), *request.args, **request.kwargs
)
handler.response = SuccessResponse(result=result, id=request.id)
return handler.response
Expand Down
5 changes: 3 additions & 2 deletions jsonrpcserver/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
SuccessResponse,
)
from .errors import (
MethodNotFoundError,
ApiError,
)

Expand Down Expand Up @@ -126,7 +127,7 @@ def handle_exceptions(request: Request, debug: bool) -> Generator:
handler = SimpleNamespace(response=None)
try:
yield handler
except KeyError:
except MethodNotFoundError:
handler.response = MethodNotFoundResponse(
id=request.id, data=request.method, debug=debug
)
Expand Down Expand Up @@ -163,7 +164,7 @@ def safe_call(request: Request, methods: Methods, *, debug: bool) -> Response:
A Response object.
"""
with handle_exceptions(request, debug) as handler:
result = call(methods.items[request.method], *request.args, **request.kwargs)
result = call(methods.lookup(request.method), *request.args, **request.kwargs)
handler.response = SuccessResponse(result=result, id=request.id)
return handler.response

Expand Down
4 changes: 4 additions & 0 deletions jsonrpcserver/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from .response import UNSPECIFIED
from . import status

class MethodNotFoundError(KeyError):
""" Method lookup failed """
pass

class ApiError(RuntimeError):
""" A method responds with a custom error """

Expand Down
20 changes: 20 additions & 0 deletions jsonrpcserver/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from inspect import signature

from .errors import MethodNotFoundError

Method = Callable[..., Any]


Expand Down Expand Up @@ -72,6 +74,24 @@ def subtract(minuend, subtrahend):
return args[0] # for the decorator to work
return None

def lookup(self, method_name) -> Method:
"""
Lookup a method

Args:
method_name: Method name to look up

Returns:
callable method

Raises:
MethodNotFoundError if method_name is not found
"""
method = self.items.get(method_name)
if not method:
raise MethodNotFoundError(method_name)
return method


# A default Methods object which can be used, or user can create their own.
global_methods = Methods()
Expand Down
2 changes: 1 addition & 1 deletion jsonrpcserver/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def __init__(
) -> None:
super().__init__(
"Server error",
code=-32000,
code=status.JSONRPC_SERVER_ERROR_CODE,
data=f"{exc.__class__.__name__}: {str(exc)}",
http_status=http_status,
*args,
Expand Down
16 changes: 16 additions & 0 deletions tests/test_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest

from jsonrpcserver.methods import Methods, add, validate_args
from jsonrpcserver.errors import MethodNotFoundError


def test_validate_no_arguments():
Expand Down Expand Up @@ -163,3 +164,18 @@ def dog():
methods = Methods(cat, dog)
assert methods.items["cat"] == cat
assert methods.items["dog"] == dog

def test_lookup():
def foo():
pass

methods = Methods()
methods.items["foo"] = foo

assert methods.lookup("foo") is foo

def test_lookup_failure():
methods = Methods()

with pytest.raises(MethodNotFoundError):
methods.lookup("bar")