Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 67f7a38

Browse files
committed
SF patch 555085 (timeout socket implementation) by Michael Gilfix.
I've made considerable changes to Michael's code, specifically to use the select() system call directly and to store the timeout as a C double instead of a Python object; internally, -1.0 (or anything negative) represents the None from the API. I'm not 100% sure that all corner cases are covered correctly, so please keep an eye on this. Next I'm going to try it Windows before Tim complains. No way is this a bugfix candidate. :-)
1 parent c9a5577 commit 67f7a38

5 files changed

Lines changed: 690 additions & 123 deletions

File tree

Lib/socket.py

Lines changed: 74 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ def getfqdn(name=''):
134134
_socketmethods = (
135135
'bind', 'connect', 'connect_ex', 'fileno', 'listen',
136136
'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
137-
'recv', 'recvfrom', 'send', 'sendall', 'sendto', 'setblocking', 'shutdown')
137+
'recv', 'recvfrom', 'send', 'sendall', 'sendto', 'setblocking',
138+
'settimeout', 'gettimeout', 'shutdown')
138139

139140
class _socketobject:
140141

@@ -168,94 +169,108 @@ def makefile(self, mode='r', bufsize=-1):
168169

169170

170171
class _fileobject:
172+
"""Implements a file object on top of a regular socket object."""
171173

172-
def __init__(self, sock, mode, bufsize):
174+
def __init__(self, sock, mode='rb', bufsize=8192):
173175
self._sock = sock
174176
self._mode = mode
175-
if bufsize < 0:
177+
if bufsize <= 0:
176178
bufsize = 512
177-
self._rbufsize = max(1, bufsize)
179+
self._rbufsize = bufsize
178180
self._wbufsize = bufsize
179-
self._wbuf = self._rbuf = ""
181+
self._rbuf = [ ]
182+
self._wbuf = [ ]
180183

181184
def close(self):
182185
try:
183186
if self._sock:
184187
self.flush()
185188
finally:
186-
self._sock = 0
189+
self._sock = None
187190

188191
def __del__(self):
189192
self.close()
190193

191194
def flush(self):
192195
if self._wbuf:
193-
self._sock.sendall(self._wbuf)
194-
self._wbuf = ""
196+
buffer = ''.join(self._wbuf)
197+
self._sock.sendall(buffer)
198+
self._wbuf = [ ]
195199

196-
def fileno(self):
200+
def fileno (self):
197201
return self._sock.fileno()
198202

199203
def write(self, data):
200-
self._wbuf = self._wbuf + data
204+
self._wbuf.append (data)
205+
# A _wbufsize of 1 means we're doing unbuffered IO.
206+
# Flush accordingly.
201207
if self._wbufsize == 1:
202208
if '\n' in data:
203-
self.flush()
204-
else:
205-
if len(self._wbuf) >= self._wbufsize:
206-
self.flush()
209+
self.flush ()
210+
elif self.__get_wbuf_len() >= self._wbufsize:
211+
self.flush()
207212

208213
def writelines(self, list):
209214
filter(self._sock.sendall, list)
210215
self.flush()
211216

212-
def read(self, n=-1):
213-
if n >= 0:
214-
k = len(self._rbuf)
215-
if n <= k:
216-
data = self._rbuf[:n]
217-
self._rbuf = self._rbuf[n:]
218-
return data
219-
n = n - k
220-
L = [self._rbuf]
221-
self._rbuf = ""
222-
while n > 0:
223-
new = self._sock.recv(max(n, self._rbufsize))
224-
if not new: break
225-
k = len(new)
226-
if k > n:
227-
L.append(new[:n])
228-
self._rbuf = new[n:]
229-
break
230-
L.append(new)
231-
n = n - k
232-
return "".join(L)
233-
k = max(512, self._rbufsize)
234-
L = [self._rbuf]
235-
self._rbuf = ""
236-
while 1:
237-
new = self._sock.recv(k)
238-
if not new: break
239-
L.append(new)
240-
k = min(k*2, 1024**2)
241-
return "".join(L)
242-
243-
def readline(self, limit=-1):
244-
data = ""
245-
i = self._rbuf.find('\n')
246-
while i < 0 and not (0 < limit <= len(self._rbuf)):
247-
new = self._sock.recv(self._rbufsize)
248-
if not new: break
249-
i = new.find('\n')
250-
if i >= 0: i = i + len(self._rbuf)
251-
self._rbuf = self._rbuf + new
252-
if i < 0: i = len(self._rbuf)
253-
else: i = i+1
254-
if 0 <= limit < len(self._rbuf): i = limit
255-
data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
217+
def __get_wbuf_len (self):
218+
buf_len = 0
219+
for i in [len(x) for x in self._wbuf]:
220+
buf_len += i
221+
return buf_len
222+
223+
def __get_rbuf_len(self):
224+
buf_len = 0
225+
for i in [len(x) for x in self._rbuf]:
226+
buf_len += i
227+
return buf_len
228+
229+
def read(self, size=-1):
230+
buf_len = self.__get_rbuf_len()
231+
while size < 0 or buf_len < size:
232+
recv_size = max(self._rbufsize, size - buf_len)
233+
data = self._sock.recv(recv_size)
234+
if not data:
235+
break
236+
buf_len += len(data)
237+
self._rbuf.append(data)
238+
data = ''.join(self._rbuf)
239+
# Clear the rbuf at the end so we're not affected by
240+
# an exception during a recv
241+
self._rbuf = [ ]
242+
if buf_len > size and size >= 0:
243+
self._rbuf.append(data[size:])
244+
data = data[:size]
245+
return data
246+
247+
def readline(self, size=-1):
248+
index = -1
249+
buf_len = self.__get_rbuf_len()
250+
if len (self._rbuf):
251+
index = min([x.find('\n') for x in self._rbuf])
252+
while index < 0 and (size < 0 or buf_len < size):
253+
recv_size = max(self._rbufsize, size - buf_len)
254+
data = self._sock.recv(recv_size)
255+
if not data:
256+
break
257+
buf_len += len(data)
258+
self._rbuf.append(data)
259+
index = data.find('\n')
260+
data = ''.join(self._rbuf)
261+
self._rbuf = [ ]
262+
index = data.find('\n')
263+
if index >= 0:
264+
index += 1
265+
elif buf_len > size:
266+
index = size
267+
else:
268+
index = buf_len
269+
self._rbuf.append(data[index:])
270+
data = data[:index]
256271
return data
257272

258-
def readlines(self, sizehint = 0):
273+
def readlines(self, sizehint=0):
259274
total = 0
260275
list = []
261276
while 1:

Lib/test/test_socket.py

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def missing_ok(str):
109109
canfork = hasattr(os, 'fork')
110110
try:
111111
PORT = 50007
112+
msg = 'socket test\n'
112113
if not canfork or os.fork():
113114
# parent is server
114115
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -133,13 +134,52 @@ def missing_ok(str):
133134
f = conn.makefile()
134135
if verbose:
135136
print 'file obj:', f
137+
data = conn.recv(1024)
138+
if verbose:
139+
print 'received:', data
140+
conn.sendall(data)
141+
142+
# Perform a few tests on the windows file object
143+
if verbose:
144+
print "Staring _fileobject tests..."
145+
f = socket._fileobject (conn, 'rb', 8192)
146+
first_seg = f.read(7)
147+
second_seg = f.read(5)
148+
if not first_seg == 'socket ' or not second_seg == 'test\n':
149+
print "Error performing read with the python _fileobject class"
150+
os._exit (1)
151+
elif verbose:
152+
print "_fileobject buffered read works"
153+
f.write (data)
154+
f.flush ()
155+
156+
buf = ''
136157
while 1:
137-
data = conn.recv(1024)
138-
if not data:
158+
char = f.read(1)
159+
if not char:
160+
print "Error performing unbuffered read with the python ", \
161+
"_fileobject class"
162+
os._exit (1)
163+
buf += char
164+
if buf == msg:
165+
if verbose:
166+
print "__fileobject unbuffered read works"
139167
break
140-
if verbose:
141-
print 'received:', data
142-
conn.sendall(data)
168+
if verbose:
169+
# If we got this far, write() must work as well
170+
print "__fileobject write works"
171+
f.write(buf)
172+
f.flush()
173+
174+
line = f.readline()
175+
if not line == msg:
176+
print "Error perferming readline with the python _fileobject class"
177+
os._exit (1)
178+
f.write(line)
179+
f.flush()
180+
if verbose:
181+
print "__fileobject readline works"
182+
143183
conn.close()
144184
else:
145185
try:
@@ -149,11 +189,18 @@ def missing_ok(str):
149189
if verbose:
150190
print 'child connecting'
151191
s.connect(("127.0.0.1", PORT))
152-
msg = 'socket test'
153-
s.send(msg)
154-
data = s.recv(1024)
155-
if msg != data:
156-
print 'parent/client mismatch'
192+
193+
iteration = 0
194+
while 1:
195+
s.send(msg)
196+
data = s.recv(12)
197+
if not data:
198+
break
199+
if msg != data:
200+
print "parent/client mismatch. Failed in %s iteration. Received: [%s]" \
201+
%(iteration, data)
202+
time.sleep (1)
203+
iteration += 1
157204
s.close()
158205
finally:
159206
os._exit(1)

0 commit comments

Comments
 (0)