1 | import json
|
---|
2 | import sys
|
---|
3 | from typing import Optional
|
---|
4 |
|
---|
5 | from pyson.ObjectMapper import ObjectMapper
|
---|
6 | from tudelft.utilities.listener.DefaultListenable import DefaultListenable
|
---|
7 | from tudelft.utilities.listener.Listener import Listener
|
---|
8 |
|
---|
9 | from geniusweb.actions.Action import Action
|
---|
10 | from geniusweb.connection.ConnectionEnd import ConnectionEnd
|
---|
11 | from geniusweb.inform.Inform import Inform
|
---|
12 |
|
---|
13 |
|
---|
14 | class StdInOutConnectionEnd(DefaultListenable[Inform],ConnectionEnd[Inform, Action]):
|
---|
15 | '''
|
---|
16 | A connectionend for a party that forwards stdin to the party,
|
---|
17 | and pushes outdata from the party back into the stream.
|
---|
18 | '''
|
---|
19 | def __init__(self):
|
---|
20 | super().__init__()
|
---|
21 | self._pyson = ObjectMapper()
|
---|
22 |
|
---|
23 |
|
---|
24 | def send(self,act:Action ):
|
---|
25 | data=json.dumps(self._pyson.toJson(act))
|
---|
26 | sys.stdout.buffer.write(len(data).to_bytes(4,'little'))
|
---|
27 | sys.stdout.buffer.write(bytes(data, 'utf-8'))
|
---|
28 | sys.stdout.buffer.flush()
|
---|
29 |
|
---|
30 | def run(self, receiver: Listener[Inform]):
|
---|
31 | '''
|
---|
32 | @param receiver the listener for the information
|
---|
33 | runs until the input breaks.
|
---|
34 | returns only after termination
|
---|
35 | @raise exception if the read or the receiver throws:
|
---|
36 | '''
|
---|
37 | # blocking read
|
---|
38 | while True:
|
---|
39 | data=self.readblock()
|
---|
40 | if not data:
|
---|
41 | break
|
---|
42 |
|
---|
43 | obj=self._pyson.parse(json.loads(data.decode("utf-8")),Inform)
|
---|
44 | receiver.notifyChange(obj)
|
---|
45 | # FIXME receiver.terminate?
|
---|
46 |
|
---|
47 | def readblock(self)->Optional[bytearray]:
|
---|
48 | '''
|
---|
49 | @return next block of data from stdin, or None if stream closed.
|
---|
50 | '''
|
---|
51 | sizedata=sys.stdin.buffer.raw.read(4) #type:ignore
|
---|
52 | if len(sizedata)<4:
|
---|
53 | return None
|
---|
54 | remaining=int.from_bytes(sizedata, byteorder='little',signed=True)
|
---|
55 | if remaining>20000000:
|
---|
56 | raise ValueError("Received bad block:"+str(remaining))
|
---|
57 | #sys.stderr.write("remaining: "+str(remaining))
|
---|
58 |
|
---|
59 | data=bytearray()
|
---|
60 | while remaining>0:
|
---|
61 | newdata=sys.stdin.buffer.raw.read(remaining) #type:ignore
|
---|
62 | if len(newdata)==0:
|
---|
63 | # FIXME receiver.terminate ?
|
---|
64 | return None
|
---|
65 | data.extend(bytearray(newdata))
|
---|
66 | remaining=remaining-len(newdata)
|
---|
67 | return data
|
---|
68 | |
---|