comparison elpa/elpy-1.18.0/elpy/rpc.py @ 175:56ea66d76309

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