22# pylint: disable=C0301,W0105,W0401,W0614
33
44import ctypes
5- import socket
65import struct
76import sys
87
@@ -75,16 +74,19 @@ class Response(list):
7574 packet received from the server.
7675 '''
7776
78- def __init__ (self , _socket , field_types = None ):
77+ def __init__ (self , header , body , field_types = None ):
7978 '''\
8079 Create an instance of `Response` using data received from the server.
8180
8281 __init__() itself reads data from the socket, parses response body and
8382 sets appropriate instance attributes.
8483
85- :params _socket: socket connected to the server
86- :type _socket: instance of socket.socket class (from stdlib)
84+ :param header: header of the response
85+ :type header: array of bytes
86+ :param body: body of the response
87+ :type body: array of bytes
8788 '''
89+
8890 # This is not necessary, because underlying list data structures are created in the __new__(). But let it be.
8991 super (Response , self ).__init__ ()
9092
@@ -97,57 +99,10 @@ def __init__(self, _socket, field_types=None):
9799 self ._rowcount = None
98100 self .field_types = field_types
99101
100- # Read response header
101- buff = ctypes .create_string_buffer (16 )
102- nbytes = _socket .recv_into (buff , 16 , )
103-
104- # Immediately raises an exception if the data cannot be read
105- if nbytes != 16 :
106- raise socket .error (socket .errno .ECONNABORTED , "Software caused connection abort" )
107-
108- # Unpack header (including <return_code> attribute)
109- self ._request_type , self ._body_length , self ._request_id , self ._return_code = struct_LLLL .unpack (buff )
110-
111- # Separate return_code and completion_code
112- self ._completion_status = self ._return_code & 0x00ff
113- self ._return_code = self ._return_code >> 8
114-
115- # Unpack body if there is one (i.e. not PING)
116- if self ._body_length != 0 :
117-
118- # In the protocol description <body_length> includes 4 bytes of <return_code>
119- self ._body_length -= 4
120-
121- # Read response body
122- buff = ctypes .create_string_buffer (self ._body_length )
123- nbytes = _socket .recv_into (buff )
124-
125- # Immediately raises an exception if the data cannot be read
126- if nbytes != self ._body_length :
127- raise socket .error (socket .errno .ECONNABORTED , "Software caused connection abort" )
128-
129- if self ._return_code == 0 :
130- # If no errors, unpack response body
131- self ._unpack_body (buff )
132- else :
133- # In case of error unpack body as error message
134- self ._unpack_message (buff )
135- if self ._completion_status == 2 :
136- raise DatabaseError (self ._return_code , self ._return_message )
137-
138-
139- def _unpack_message (self , buff ):
140- '''\
141- Extract error message from response body
142- Called when return_code! = 0.
143-
144- :param buff: buffer containing request body
145- :type byff: ctypes buffer
146- :return: error message
147- :rtype: str
148- '''
149-
150- self ._return_message = unicode (buff .value , "utf8" , "replace" )
102+ # Unpack header
103+ self ._request_type , self ._body_length , self ._request_id = struct_LLL .unpack (header )
104+ if body :
105+ self ._unpack_body (body )
151106
152107
153108 @staticmethod
@@ -209,16 +164,26 @@ def _unpack_body(self, buff):
209164 :type byff: ctypes buffer
210165 '''
211166
212- # Unpack <count> (first 4 bytes) - how many records returned
213- self ._rowcount = struct_L .unpack_from (buff )[0 ]
167+ # Unpack <return_code> and <count> (how many records affected or selected)
168+ self ._return_code , self ._rowcount = struct_LL .unpack_from (buff , offset = 0 )
169+
170+ # Separate return_code and completion_code
171+ self ._completion_status = self ._return_code & 0x00ff
172+ self ._return_code = self ._return_code >> 8
173+
174+ # In case of an error unpack the body as an error message
175+ if self ._return_code != 0 :
176+ self ._return_message = unicode (buff .value , "utf8" , "replace" )
177+ if self ._completion_status == 2 :
178+ raise DatabaseError (self ._return_code , self ._return_message )
214179
215- # If the response body contains only <count> - there is no tuples to unpack
216- if self ._body_length == 4 :
180+ # If the response don't contains any tuples - there is no tuples to unpack
181+ if self ._body_length == 8 :
217182 return
218183
219184 # Parse response tuples (<fq_tuple>)
220185 if self ._rowcount > 0 :
221- offset = 4 # The first 4 bytes in the response body is the <count> we have already read
186+ offset = 8 # The first 4 bytes in the response body is the <count> we have already read
222187 while offset < self ._body_length :
223188 '''
224189 # In resonse tuples have the form <size><tuple> (<fq_tuple> ::= <size><tuple>).
0 commit comments