-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcommon.py
More file actions
65 lines (49 loc) · 2.41 KB
/
common.py
File metadata and controls
65 lines (49 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# -*- coding: utf-8; -*-
"""Pieces common to the REPL server and client."""
import json
from .msg import encodemsg
__all__ = ["ApplevelProtocolMixin"]
class ApplevelProtocolMixin:
"""Application-level communication protocol.
We encode the payload dictionary as JSON, then encode the text as utf-8.
The resulting bytes are stuffed into a message using the `unpythonic.net.msg`
low-level message protocol.
This format was chosen instead of pickle to ensure the client and server
can talk to each other regardless of the Python versions on each end of the
connection.
Transmission is synchronous; when one end is sending, the other one must be
receiving. Receiving will block until success, or until the socket is closed.
This is a mixin class for communication between server/client object pairs.
**NOTE**: The class class mixing this in must define two attributes:
- `sock`: a TCP socket. When `_send` or `_recv` is called, the socket
must be open and connected to the peer to communicate with.
- `decoder`: `unpythonic.net.msg.MessageDecoder` instance for receiving
messages. Typically this is connected to `sock` using an
`unpythonic.net.util.socketsource`, like
`MessageDecoder(socketsource(sock))`.
These are left to the user code to define, because typically the client and
server sides must handle them differently. The client can create `sock` and
`decoder` in its constructor, whereas a TCP server typically inherits from
`socketserver.BaseRequestHandler`, and receives an incoming connection in
its `handle` method (which is then the official place to create any
session-specific attributes).
"""
def _send(self, data):
"""Send a message using the application-level protocol.
data: dict-like.
"""
json_data = json.dumps(data)
bytes_out = json_data.encode("utf-8")
self.sock.sendall(encodemsg(bytes_out))
def _recv(self):
"""Receive a message using the application-level protocol.
Returns a dict-like, or `None` if the decoder's message source
signaled EOF.
Blocks if no data is currently available at the message source,
but EOF has not been signaled.
"""
bytes_in = self.decoder.decode()
if not bytes_in:
return None
json_data = bytes_in.decode("utf-8")
return json.loads(json_data)