view fix_lightscpy_readloop.patch @ 413:9067c2da9572

better commit message
author Louis Opter <kalessin@kalessin.fr>
date Fri, 01 Jan 2016 13:24:12 +0100
parents 65b0dc335243
children 2d7ab98d6266
line wrap: on
line source

# HG changeset patch
# Parent  c8614ad2dc1133c8d50e974afd66d84231b41c78
Fix lightsc.py's read loop

It now handles arbitrarily large and partial responses properly.

We do need to do non-blocking IO in case the last received buffer comes
back full in this case doing another read would block the whole thing.

diff --git a/examples/lightsc.py b/examples/lightsc.py
--- a/examples/lightsc.py
+++ b/examples/lightsc.py
@@ -30,9 +30,11 @@
 
 import argparse
 import contextlib
+import fcntl
 import json
 import locale
 import os
+import select
 import socket
 import subprocess
 import sys
@@ -42,6 +44,9 @@
 
 class LightsClient:
 
+    READ_SIZE = 4096
+    ENCODING = "utf-8"
+
     def __init__(self, url):
         self.url = url
 
@@ -56,6 +61,7 @@
         else:
             raise ValueError("Unsupported url {}".format(url))
 
+        fcntl.fcntl(self._socket, fcntl.F_SETFL, os.O_NONBLOCK)
         self._pipeline = []
         self._batch = False
 
@@ -75,17 +81,27 @@
         }
 
     def _execute_payload(self, payload):
-        print(payload)
-        self._socket.send(json.dumps(payload).encode("utf-8"))
-        # FIXME: proper read loop
-        response = self._socket.recv(64 * 1024).decode("utf-8")
+        select.select([], [self._socket], [])
+        payload = json.dumps(payload).encode(self.ENCODING, "surrogateescape")
+        self._socket.send(payload)
+
+        response = bytearray()
+        select.select([self._socket], [], [])
+        while True:
+            try:
+                part = self._socket.recv(self.READ_SIZE)
+            except BlockingIOError:
+                break
+            if not part:
+                break
+            response += part
+
+        response = response.decode(self.ENCODING, "surrogateescape")
         try:
-            response = json.loads(response)
+            return json.loads(response)
         except Exception:
             print("received invalid json: {}".format(response))
 
-        return response
-
     def _jsonrpc_call(self, method, params):
         payload = self._make_payload(method, params)
         if self._batch: