Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.5 KiB

5 years ago
  1. """A simple JSON-RPC-like server.
  2. The server will read and write lines of JSON-encoded method calls and
  3. responses.
  4. See the documentation of the JSONRPCServer class for further details.
  5. """
  6. import json
  7. import sys
  8. import traceback
  9. class JSONRPCServer(object):
  10. """Simple JSON-RPC-like server.
  11. This class will read single-line JSON expressions from stdin,
  12. decode them, and pass them to a handler. Return values from the
  13. handler will be JSON-encoded and written to stdout.
  14. To implement a handler, you need to subclass this class and add
  15. methods starting with "rpc_". Methods then will be found.
  16. Method calls should be encoded like this:
  17. {"id": 23, "method": "method_name", "params": ["foo", "bar"]}
  18. This will call self.rpc_method("foo", "bar").
  19. Responses will be encoded like this:
  20. {"id": 23, "result": "foo"}
  21. Errors will be encoded like this:
  22. {"id": 23, "error": "Simple error message"}
  23. See http://www.jsonrpc.org/ for the inspiration of the protocol.
  24. """
  25. def __init__(self, stdin=None, stdout=None):
  26. """Return a new JSON-RPC server object.
  27. It will read lines of JSON data from stdin, and write the
  28. responses to stdout.
  29. """
  30. if stdin is None:
  31. self.stdin = sys.stdin
  32. else:
  33. self.stdin = stdin
  34. if stdout is None:
  35. self.stdout = sys.stdout
  36. else:
  37. self.stdout = stdout
  38. def read_json(self):
  39. """Read a single line and decode it as JSON.
  40. Can raise an EOFError() when the input source was closed.
  41. """
  42. line = self.stdin.readline()
  43. if line == '':
  44. raise EOFError()
  45. return json.loads(line)
  46. def write_json(self, **kwargs):
  47. """Write an JSON object on a single line.
  48. The keyword arguments are interpreted as a single JSON object.
  49. It's not possible with this method to write non-objects.
  50. """
  51. self.stdout.write(json.dumps(kwargs) + "\n")
  52. self.stdout.flush()
  53. def handle_request(self):
  54. """Handle a single JSON-RPC request.
  55. Read a request, call the appropriate handler method, and
  56. return the encoded result. Errors in the handler method are
  57. caught and encoded as error objects. Errors in the decoding
  58. phase are not caught, as we can not respond with an error
  59. response to them.
  60. """
  61. request = self.read_json()
  62. if 'method' not in request:
  63. raise ValueError("Received a bad request: {0}"
  64. .format(request))
  65. method_name = request['method']
  66. request_id = request.get('id', None)
  67. params = request.get('params') or []
  68. try:
  69. method = getattr(self, "rpc_" + method_name, None)
  70. if method is not None:
  71. result = method(*params)
  72. else:
  73. result = self.handle(method_name, params)
  74. if request_id is not None:
  75. self.write_json(result=result,
  76. id=request_id)
  77. except Fault as fault:
  78. error = {"message": fault.message,
  79. "code": fault.code}
  80. if fault.data is not None:
  81. error["data"] = fault.data
  82. self.write_json(error=error, id=request_id)
  83. except Exception as e:
  84. error = {"message": str(e),
  85. "code": 500,
  86. "data": {"traceback": traceback.format_exc()}}
  87. self.write_json(error=error, id=request_id)
  88. def handle(self, method_name, args):
  89. """Handle the call to method_name.
  90. You should overwrite this method in a subclass.
  91. """
  92. raise Fault("Unknown method {0}".format(method_name))
  93. def serve_forever(self):
  94. """Serve requests forever.
  95. Errors are not caught, so this is a slight misnomer.
  96. """
  97. while True:
  98. try:
  99. self.handle_request()
  100. except (KeyboardInterrupt, EOFError, SystemExit):
  101. break
  102. class Fault(Exception):
  103. """RPC Fault instances.
  104. code defines the severity of the warning.
  105. 2xx: Normal behavior lead to end of operation, i.e. a warning
  106. 4xx: An expected error occurred
  107. 5xx: An unexpected error occurred (usually includes a traceback)
  108. """
  109. def __init__(self, message, code=500, data=None):
  110. super(Fault, self).__init__(message)
  111. self.message = message
  112. self.code = code
  113. self.data = data