Committing Py3k version of changelist 64080 and 64257, along with updated tests

for smtpd, which required updating with the new semantics.
This commit is contained in:
Josiah Carlson 2008-07-07 04:15:08 +00:00
parent d51ee54a23
commit d74900ebb5
8 changed files with 269 additions and 134 deletions

View file

@ -50,23 +50,28 @@ import select
import socket
import sys
import time
import os
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcode
ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, errorcode
try:
socket_map
except NameError:
socket_map = {}
def _strerror(err):
res = os.strerror(err)
if res == 'Unknown error':
res = errorcode[err]
return res
class ExitNow(Exception):
pass
def read(obj):
try:
obj.handle_read_event()
except ExitNow:
except (ExitNow, KeyboardInterrupt, SystemExit):
raise
except:
obj.handle_error()
@ -74,15 +79,15 @@ def read(obj):
def write(obj):
try:
obj.handle_write_event()
except ExitNow:
except (ExitNow, KeyboardInterrupt, SystemExit):
raise
except:
obj.handle_error()
def _exception (obj):
def _exception(obj):
try:
obj.handle_expt_event()
except ExitNow:
except (ExitNow, KeyboardInterrupt, SystemExit):
raise
except:
obj.handle_error()
@ -95,7 +100,7 @@ def readwrite(obj, flags):
obj.handle_write_event()
if flags & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
obj.handle_expt_event()
except ExitNow:
except (ExitNow, KeyboardInterrupt, SystemExit):
raise
except:
obj.handle_error()
@ -105,7 +110,7 @@ def poll(timeout=0.0, map=None):
map = socket_map
if map:
r = []; w = []; e = []
for fd, obj in map.items():
for fd, obj in list(map.items()):
is_r = obj.readable()
is_w = obj.writable()
if is_r:
@ -116,14 +121,15 @@ def poll(timeout=0.0, map=None):
e.append(fd)
if [] == r == w == e:
time.sleep(timeout)
else:
try:
r, w, e = select.select(r, w, e, timeout)
except select.error as err:
if err.args[0] != EINTR:
raise
else:
return
return
try:
r, w, e = select.select(r, w, e, timeout)
except select.error as err:
if err[0] != EINTR:
raise
else:
return
for fd in r:
obj = map.get(fd)
@ -152,7 +158,7 @@ def poll2(timeout=0.0, map=None):
timeout = int(timeout*1000)
pollster = select.poll()
if map:
for fd, obj in map.items():
for fd, obj in list(map.items()):
flags = 0
if obj.readable():
flags |= select.POLLIN | select.POLLPRI
@ -166,7 +172,7 @@ def poll2(timeout=0.0, map=None):
try:
r = pollster.poll(timeout)
except select.error as err:
if err.args[0] != EINTR:
if err[0] != EINTR:
raise
r = []
for fd, flags in r:
@ -209,18 +215,29 @@ class dispatcher:
else:
self._map = map
self._fileno = None
if sock:
# Set to nonblocking just to make sure for cases where we
# get a socket from a blocking source.
sock.setblocking(0)
self.set_socket(sock, map)
# I think it should inherit this anyway
self.socket.setblocking(0)
self.connected = True
# XXX Does the constructor require that the socket passed
# be connected?
# The constructor no longer requires that the socket
# passed be connected.
try:
self.addr = sock.getpeername()
except socket.error:
# The addr isn't crucial
pass
except socket.error as err:
if err[0] == ENOTCONN:
# To handle the case where we got an unconnected
# socket.
self.connected = False
else:
# The socket is broken in some unknown way, alert
# the user and remove it from the map (to prevent
# polling of broken sockets).
self.del_channel(map)
raise
else:
self.socket = None
@ -254,10 +271,9 @@ class dispatcher:
def create_socket(self, family, type):
self.family_and_type = family, type
self.socket = socket.socket(family, type)
self.socket.setblocking(0)
self._fileno = self.socket.fileno()
self.add_channel()
sock = socket.socket(family, type)
sock.setblocking(0)
self.set_socket(sock)
def set_socket(self, sock, map=None):
self.socket = sock
@ -295,7 +311,7 @@ class dispatcher:
def listen(self, num):
self.accepting = True
if os.name == 'nt' and num > 5:
num = 1
num = 5
return self.socket.listen(num)
def bind(self, addr):
@ -310,8 +326,7 @@ class dispatcher:
return
if err in (0, EISCONN):
self.addr = address
self.connected = True
self.handle_connect()
self.handle_connect_event()
else:
raise socket.error(err, errorcode[err])
@ -321,7 +336,7 @@ class dispatcher:
conn, addr = self.socket.accept()
return conn, addr
except socket.error as why:
if why.args[0] == EWOULDBLOCK:
if why[0] == EWOULDBLOCK:
pass
else:
raise
@ -331,11 +346,13 @@ class dispatcher:
result = self.socket.send(data)
return result
except socket.error as why:
if why.args[0] == EWOULDBLOCK:
if why[0] == EWOULDBLOCK:
return 0
elif why[0] in (ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED):
self.handle_close()
return 0
else:
raise
return 0
def recv(self, buffer_size):
try:
@ -349,15 +366,21 @@ class dispatcher:
return data
except socket.error as why:
# winsock sometimes throws ENOTCONN
if why.args[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED]:
self.handle_close()
return b''
else:
raise
def close(self):
self.connected = False
self.accepting = False
self.del_channel()
self.socket.close()
try:
self.socket.close()
except socket.error as why:
if why[0] not in (ENOTCONN, EBADF):
raise
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
@ -377,27 +400,53 @@ class dispatcher:
def handle_read_event(self):
if self.accepting:
# for an accepting socket, getting a read implies
# that we are connected
if not self.connected:
self.connected = True
# accepting sockets are never connected, they "spawn" new
# sockets that are connected
self.handle_accept()
elif not self.connected:
self.handle_connect()
self.connected = True
self.handle_connect_event()
self.handle_read()
else:
self.handle_read()
def handle_connect_event(self):
self.connected = True
self.handle_connect()
def handle_write_event(self):
# getting a write implies that we are connected
if self.accepting:
# Accepting sockets shouldn't get a write event.
# We will pretend it didn't happen.
return
if not self.connected:
self.handle_connect()
self.connected = True
#check for errors
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if err != 0:
raise socket.error(err, _strerror(err))
self.handle_connect_event()
self.handle_write()
def handle_expt_event(self):
self.handle_expt()
# if the handle_expt is the same default worthless method,
# we'll not even bother calling it, we'll instead generate
# a useful error
x = True
try:
y1 = self.handle_expt.__func__
y2 = dispatcher.handle_expt
x = y1 is y2
except AttributeError:
pass
if x:
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
msg = _strerror(err)
raise socket.error(err, msg)
else:
self.handle_expt()
def handle_error(self):
nil, t, v, tbinfo = compact_traceback()
@ -461,7 +510,6 @@ class dispatcher_with_send(dispatcher):
return (not self.connected) or len(self.out_buffer)
def send(self, data):
assert isinstance(data, bytes)
if self.debug:
self.log_info('sending %s' % repr(data))
self.out_buffer = self.out_buffer + data
@ -474,7 +522,8 @@ class dispatcher_with_send(dispatcher):
def compact_traceback():
t, v, tb = sys.exc_info()
tbinfo = []
assert tb # Must have a traceback
if not tb: # Must have a traceback
raise AssertionError("traceback does not exist")
while tb:
tbinfo.append((
tb.tb_frame.f_code.co_filename,
@ -490,11 +539,22 @@ def compact_traceback():
info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
return (file, function, line), t, v, info
def close_all(map=None):
def close_all(map=None, ignore_all=False):
if map is None:
map = socket_map
for x in map.values():
x.socket.close()
for x in list(map.values()):
try:
x.close()
except OSError as x:
if x[0] == EBADF:
pass
elif not ignore_all:
raise
except (ExitNow, KeyboardInterrupt, SystemExit):
raise
except:
if not ignore_all:
raise
map.clear()
# Asynchronous File I/O:
@ -514,11 +574,12 @@ if os.name == 'posix':
import fcntl
class file_wrapper:
# here we override just enough to make a file
# Here we override just enough to make a file
# look like a socket for the purposes of asyncore.
# The passed fd is automatically os.dup()'d
def __init__(self, fd):
self.fd = fd
self.fd = os.dup(fd)
def recv(self, *args):
return os.read(self.fd, *args)
@ -540,6 +601,10 @@ if os.name == 'posix':
def __init__(self, fd, map=None):
dispatcher.__init__(self, None, map)
self.connected = True
try:
fd = fd.fileno()
except AttributeError:
pass
self.set_file(fd)
# set it to non-blocking mode
flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)