1414
1515"""Helpers for server-side streaming in REST."""
1616
17- from collections import deque
18- import string
19- from typing import Deque , Union
17+ from typing import Union
2018
2119import proto
2220import requests
2321import google .protobuf .message
24- from google .protobuf . json_format import Parse
22+ from google .api_core . _rest_streaming_base import BaseResponseIterator
2523
2624
27- class ResponseIterator :
25+ class ResponseIterator ( BaseResponseIterator ) :
2826 """Iterator over REST API responses.
2927
3028 Args:
@@ -33,7 +31,8 @@ class ResponseIterator:
3331 class expected to be returned from an API.
3432
3533 Raises:
36- ValueError: If `response_message_cls` is not a subclass of `proto.Message` or `google.protobuf.message.Message`.
34+ ValueError:
35+ - If `response_message_cls` is not a subclass of `proto.Message` or `google.protobuf.message.Message`.
3736 """
3837
3938 def __init__ (
@@ -42,68 +41,16 @@ def __init__(
4241 response_message_cls : Union [proto .Message , google .protobuf .message .Message ],
4342 ):
4443 self ._response = response
45- self ._response_message_cls = response_message_cls
4644 # Inner iterator over HTTP response's content.
4745 self ._response_itr = self ._response .iter_content (decode_unicode = True )
48- # Contains a list of JSON responses ready to be sent to user.
49- self ._ready_objs : Deque [str ] = deque ()
50- # Current JSON response being built.
51- self ._obj = ""
52- # Keeps track of the nesting level within a JSON object.
53- self ._level = 0
54- # Keeps track whether HTTP response is currently sending values
55- # inside of a string value.
56- self ._in_string = False
57- # Whether an escape symbol "\" was encountered.
58- self ._escape_next = False
46+ super (ResponseIterator , self ).__init__ (
47+ response_message_cls = response_message_cls
48+ )
5949
6050 def cancel (self ):
6151 """Cancel existing streaming operation."""
6252 self ._response .close ()
6353
64- def _process_chunk (self , chunk : str ):
65- if self ._level == 0 :
66- if chunk [0 ] != "[" :
67- raise ValueError (
68- "Can only parse array of JSON objects, instead got %s" % chunk
69- )
70- for char in chunk :
71- if char == "{" :
72- if self ._level == 1 :
73- # Level 1 corresponds to the outermost JSON object
74- # (i.e. the one we care about).
75- self ._obj = ""
76- if not self ._in_string :
77- self ._level += 1
78- self ._obj += char
79- elif char == "}" :
80- self ._obj += char
81- if not self ._in_string :
82- self ._level -= 1
83- if not self ._in_string and self ._level == 1 :
84- self ._ready_objs .append (self ._obj )
85- elif char == '"' :
86- # Helps to deal with an escaped quotes inside of a string.
87- if not self ._escape_next :
88- self ._in_string = not self ._in_string
89- self ._obj += char
90- elif char in string .whitespace :
91- if self ._in_string :
92- self ._obj += char
93- elif char == "[" :
94- if self ._level == 0 :
95- self ._level += 1
96- else :
97- self ._obj += char
98- elif char == "]" :
99- if self ._level == 1 :
100- self ._level -= 1
101- else :
102- self ._obj += char
103- else :
104- self ._obj += char
105- self ._escape_next = not self ._escape_next if char == "\\ " else False
106-
10754 def __next__ (self ):
10855 while not self ._ready_objs :
10956 try :
@@ -115,18 +62,5 @@ def __next__(self):
11562 raise e
11663 return self ._grab ()
11764
118- def _grab (self ):
119- # Add extra quotes to make json.loads happy.
120- if issubclass (self ._response_message_cls , proto .Message ):
121- return self ._response_message_cls .from_json (
122- self ._ready_objs .popleft (), ignore_unknown_fields = True
123- )
124- elif issubclass (self ._response_message_cls , google .protobuf .message .Message ):
125- return Parse (self ._ready_objs .popleft (), self ._response_message_cls ())
126- else :
127- raise ValueError (
128- "Response message class must be a subclass of proto.Message or google.protobuf.message.Message."
129- )
130-
13165 def __iter__ (self ):
13266 return self
0 commit comments