xref: /trunk/test/aoo-test/src/aootest_worker/proto.py (revision 2cb6d6f119bbe2f78742f1c5c8538cb0d4e72ede)
1################################################################
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20################################################################
21
22"""The worker's request loop. Anything a handler prints goes to stderr so it cannot corrupt the protocol on stdout."""
23import inspect
24import json
25import os
26import sys
27import time
28import traceback
29
30PROTOCOL = 1
31
32
33class Fail(Exception):
34    """An error a handler wants reported with a specific kind: uno, office_dead, bad_args or python."""
35
36    def __init__(self, kind, message=""):
37        super().__init__(message)
38        self.kind = kind
39
40
41def serve(ops, stdin=None, stdout=None):
42    """Sends the hello, then answers requests until stdin closes or an op named 'shutdown' arrives."""
43    out = stdout or sys.stdout
44    inp = stdin or sys.stdin
45    sys.stdout = sys.stderr
46
47    def send(obj):
48        out.write(json.dumps(obj) + "\n")
49        out.flush()
50
51    send({"hello": {"protocol": PROTOCOL, "python": "%d.%d.%d" % sys.version_info[:3], "pid": os.getpid()}})
52    for line in inp:
53        if not line.strip():
54            continue
55        try:
56            req = json.loads(line)
57            rid, op, args = req["id"], req["op"], req.get("args") or {}
58        except (ValueError, KeyError, TypeError) as e:
59            send({"id": None, "ok": False, "error": {"kind": "bad_args", "exc": type(e).__name__, "message": "unreadable request: %s" % e, "traceback": ""}})
60            continue
61        if op == "shutdown":
62            send({"id": rid, "ok": True, "result": {}, "elapsed": 0.0})
63            return
64        t0 = time.time()
65        try:
66            if op == "ping":
67                result = {"pong": True}
68            elif op in ops:
69                try:
70                    inspect.signature(ops[op]).bind(**args)
71                except TypeError as e:
72                    raise Fail("bad_args", "%s: %s" % (op, e))
73                result = ops[op](**args)
74            else:
75                raise Fail("bad_args", "unknown op %r" % op)
76            send({"id": rid, "ok": True, "result": result, "elapsed": round(time.time() - t0, 3)})
77        except Fail as e:
78            send({"id": rid, "ok": False, "error": {"kind": e.kind, "exc": "Fail", "message": str(e), "traceback": ""}})
79        except Exception as e:
80            send({"id": rid, "ok": False, "error": {"kind": "python", "exc": type(e).__name__, "message": str(e), "traceback": traceback.format_exc()}})
81