Mercurial > hg > dotemacs
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 |