Sfoglia il codice sorgente

json_splitter: Don't break when buffer contains leading whitespace.

Add error logging with detailed output for decode errors

Signed-off-by: Joffrey F <[email protected]>
Joffrey F 9 anni fa
parent
commit
1c07d6453f
2 ha cambiato i file con 28 aggiunte e 1 eliminazioni
  1. 11 1
      compose/utils.py
  2. 17 0
      tests/unit/utils_test.py

+ 11 - 1
compose/utils.py

@@ -5,11 +5,13 @@ import codecs
 import hashlib
 import hashlib
 import json
 import json
 import json.decoder
 import json.decoder
+import logging
 
 
 import six
 import six
 
 
 
 
 json_decoder = json.JSONDecoder()
 json_decoder = json.JSONDecoder()
+log = logging.getLogger(__name__)
 
 
 
 
 def get_output_stream(stream):
 def get_output_stream(stream):
@@ -60,13 +62,21 @@ def split_buffer(stream, splitter=None, decoder=lambda a: a):
             yield item
             yield item
 
 
     if buffered:
     if buffered:
-        yield decoder(buffered)
+        try:
+            yield decoder(buffered)
+        except ValueError:
+            log.error(
+                'Compose tried parsing the following chunk as a JSON object, '
+                'but failed:\n%s' % repr(buffered)
+            )
+            raise
 
 
 
 
 def json_splitter(buffer):
 def json_splitter(buffer):
     """Attempt to parse a json object from a buffer. If there is at least one
     """Attempt to parse a json object from a buffer. If there is at least one
     object, return it and the rest of the buffer, otherwise return None.
     object, return it and the rest of the buffer, otherwise return None.
     """
     """
+    buffer = buffer.strip()
     try:
     try:
         obj, index = json_decoder.raw_decode(buffer)
         obj, index = json_decoder.raw_decode(buffer)
         rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end():]
         rest = buffer[json.decoder.WHITESPACE.match(buffer, index).end():]

+ 17 - 0
tests/unit/utils_test.py

@@ -15,6 +15,10 @@ class TestJsonSplitter(object):
         data = '{"foo": "bar"}\n  \n{"next": "obj"}'
         data = '{"foo": "bar"}\n  \n{"next": "obj"}'
         assert utils.json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}')
         assert utils.json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}')
 
 
+    def test_json_splitter_leading_whitespace(self):
+        data = '\n   \r{"foo": "bar"}\n\n   {"next": "obj"}'
+        assert utils.json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}')
+
 
 
 class TestStreamAsText(object):
 class TestStreamAsText(object):
 
 
@@ -43,3 +47,16 @@ class TestJsonStream(object):
             [1, 2, 3],
             [1, 2, 3],
             [],
             [],
         ]
         ]
+
+    def test_with_leading_whitespace(self):
+        stream = [
+            '\n  \r\n  {"one": "two"}{"x": 1}',
+            '  {"three": "four"}\t\t{"x": 2}'
+        ]
+        output = list(utils.json_stream(stream))
+        assert output == [
+            {'one': 'two'},
+            {'x': 1},
+            {'three': 'four'},
+            {'x': 2}
+        ]