[Checkins] SVN: zc.ngi/trunk/ Ignore stuff.
Jim Fulton
jim at zope.com
Tue Jan 1 12:37:54 EST 2008
Log message for revision 82627:
Ignore stuff.
Changed:
_U zc.ngi/trunk/
U zc.ngi/trunk/src/zc/ngi/README.txt
U zc.ngi/trunk/src/zc/ngi/async.py
U zc.ngi/trunk/src/zc/ngi/async.txt
U zc.ngi/trunk/src/zc/ngi/blocking.py
U zc.ngi/trunk/src/zc/ngi/interfaces.py
U zc.ngi/trunk/src/zc/ngi/testing.py
U zc.ngi/trunk/src/zc/ngi/tests.py
U zc.ngi/trunk/src/zc/ngi/wordcount.py
-=-
Property changes on: zc.ngi/trunk
___________________________________________________________________
Name: svn:ignore
+ develop-eggs
documentation.txt
bin
parts
Modified: zc.ngi/trunk/src/zc/ngi/README.txt
===================================================================
--- zc.ngi/trunk/src/zc/ngi/README.txt 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/README.txt 2008-01-01 17:37:53 UTC (rev 82627)
@@ -403,14 +403,41 @@
...
TypeError: Connection closed
-Peer connectors
-===============
+Connecting servers and clients
+==============================
It is sometimes useful to connect a client handler and a server
-handler. The zc.ngi.testing.peer function can be used to create a
-connection to a peer handler. To illustrate, we'll set up an echo
-client that connects to our echo server:
+handler. Listeners created with the zc.ngi.testing.listener class have a
+connector method that can be used to create connections to a server.
+Let's connect out echo server and client. First, we'll create out
+server using the listener constructor:
+
+ >>> listener = zc.ngi.testing.listener(EchoServer)
+
+Then we'll use the connector method on the listener:
+
+ >>> client = EchoClient(listener.connector)
+ >>> client.check(('localhost', 42), ['hello', 'world', 'how are you?'])
+ server connected
+ server got input: 'hello\n'
+ server got input: 'world\n'
+ server got input: 'how are you?\n'
+ got input: 'hello\nworld\nhow are you?\n'
+ matched: hello
+ matched: world
+ matched: how are you?
+ server closed: closed
+
+.. Peer connectors
+
+ Below is an older API for connecting servers and clients in a
+ testing environment. The mechanisms defined above are prefered.
+
+ The zc.ngi.testing.peer function can be used to create a
+ connection to a peer handler. To illustrate, we'll set up an echo
+ client that connects to our echo server:
+
>>> client = EchoClient(zc.ngi.testing.peer(('localhost', 42), EchoServer))
>>> client.check(('localhost', 42), ['hello', 'world', 'how are you?'])
server connected
Modified: zc.ngi/trunk/src/zc/ngi/async.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/async.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/async.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -58,9 +58,10 @@
try:
self.handle_close(reason)
except:
- self.logger.exception("Exception raised by handle_close(%r)",
- reason)
- self.close()
+ self.logger.exception(
+ "Exception raised by dispatcher handle_close(%r)",
+ reason)
+ self.close()
def close(self):
self.del_channel(_map)
@@ -93,9 +94,19 @@
if self.__exception:
exception = self.__exception
self.__exception = None
- handler.handle_exception(self, exception)
+ try:
+ handler.handle_exception(self, exception)
+ except:
+ self.logger.exception("handle_exception failed")
+ return self.handle_close("handle_exception failed")
+
if self.__closed:
- handler.handle_close(self, self.__closed)
+ try:
+ handler.handle_close(self, self.__closed)
+ except:
+ self.logger.exception("Exception raised by handle_close(%r)",
+ self.__closed)
+ raise
def write(self, data):
if __debug__:
@@ -141,7 +152,12 @@
if __debug__:
self.logger.debug('input %r', d)
- self.__handler.handle_input(self, d)
+ try:
+ self.__handler.handle_input(self, d)
+ except:
+ self.logger.exception("handle_input failed")
+ self.handle_close("handle_input failed")
+
if len(d) < 8192:
break
@@ -194,7 +210,11 @@
def __report_exception(self, exception):
if self.__handler is not None:
- self.__handler.handle_exception(self, exception)
+ try:
+ self.__handler.handle_exception(self, exception)
+ except:
+ self.logger.exception("handle_exception failed")
+ self.handle_close("handle_exception failed")
else:
self.__exception = exception
@@ -202,7 +222,11 @@
if __debug__:
self.logger.debug('close %r', reason)
if self.__handler is not None:
- self.__handler.handle_close(self, reason)
+ try:
+ self.__handler.handle_close(self, reason)
+ except:
+ self.logger.exception("Exception raised by handle_close(%r)",
+ reason)
else:
self.__closed = reason
self.close()
@@ -261,7 +285,10 @@
def handle_close(self, reason=None):
if __debug__:
self.logger.debug('connector close %r', reason)
- self.__handler.failed_connect(reason)
+ try:
+ self.__handler.failed_connect(reason)
+ except:
+ self.logger.exception("failed_connect(%r) failed", reason)
self.close()
def handle_write_event(self):
@@ -280,7 +307,11 @@
self.logger.debug('outgoing connected %r', self.addr)
connection = _Connection(self.socket, self.addr, self.logger)
- self.__handler.connected(connection)
+ try:
+ self.__handler.connected(connection)
+ except:
+ self.logger.exception("connection handler failed")
+ connection.handle_close("connection handler failed")
return
def handle_error(self):
@@ -337,7 +368,11 @@
connection = _Connection(sock, addr, self.logger)
self.__connections[connection] = 1
connection.control = self
- self.__handler(connection)
+ try:
+ self.__handler(connection)
+ except:
+ self.logger.exception("server handler failed")
+ self.close()
def connections(self):
return iter(self.__connections)
Modified: zc.ngi/trunk/src/zc/ngi/async.txt
===================================================================
--- zc.ngi/trunk/src/zc/ngi/async.txt 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/async.txt 2008-01-01 17:37:53 UTC (rev 82627)
@@ -68,7 +68,7 @@
.. Error handling:
If we pass a non-iterable to writelines, we'll get an immediate
- error. To demonstrate this we'll violate out output file and
+ error. To demonstrate this we'll violate our output file and
access it's _connection attribute so that we can bypass the check
in the blocking writelines method:
@@ -83,7 +83,7 @@
AssertionError: writelines does not accept strings
If we pass an iterable that returns a non-string, we'll get a type
- error when we try to read because handle_exception is caused ion
+ error when we try to read because handle_exception is called in
the input handler.
>>> output.writelines([2], timeout=0.1)
@@ -96,7 +96,86 @@
...
TypeError: ('iterable output returned a non-string', 2)
+ If there is an error, then the connection is closed:
+ >>> input.read()
+ ''
+
+ >>> output.write('hello')
+ Traceback (most recent call last):
+ ...
+ IOError: I/O operation on closed file
+
+ Handler errors cause connections to be closed. To see this, we'll
+ send the server an error message, which foreces an error:
+
+ >>> output, input = zc.ngi.blocking.open(addr, zc.ngi.async.connector,
+ ... timeout=1.0)
+ >>> output.write('E\0')
+ >>> input.read()
+ ''
+
+ Let's create some lame clients:
+
+ >>> import zope.testing.loggingsupport, logging
+ >>> loghandler = zope.testing.loggingsupport.InstalledHandler(
+ ... None, level=logging.ERROR)
+
+ >>> event = threading.Event()
+ >>> class LameClientConnectionHandler:
+ ... def connected(self, connection):
+ ... connection.setHandler(self)
+ ... raise ValueError('Broken connector')
+ ... def handle_close(self, conection, reason):
+ ... self.closed = reason
+ ... event.set()
+
+ >>> handler = LameClientConnectionHandler()
+ >>> _ = zc.ngi.async.connector(addr, handler)
+ >>> event.wait(1)
+
+ >>> print loghandler
+ zc.ngi.async.client ERROR
+ connection handler failed
+
+ >>> handler.closed
+ 'connection handler failed'
+
+
+ >>> loghandler.clear()
+ >>> class LameClientConnectionHandler:
+ ... def connected(self, connection):
+ ... connection.setHandler(self)
+ ... connection.write('foo\0')
+ ...
+ ... def handle_input(self, data):
+ ... raise ValueError()
+ ...
+ ... def handle_close(self, conection, reason):
+ ... self.closed = reason
+ ... event.set()
+ >>> handler = LameClientConnectionHandler()
+ >>> _ = zc.ngi.async.connector(addr, handler)
+ >>> event.wait(1)
+
+ >>> print loghandler
+ zc.ngi.async.client ERROR
+ handle_input failed
+
+ >>> handler.closed
+ 'handle_input failed'
+
+ >>> loghandler.uninstall()
+
+
.. stop the server
>>> zc.ngi.wordcount.stop_server_process(zc.ngi.async.connector, addr)
+ ... # doctest: +ELLIPSIS
+ handle_input failed
+ Traceback (most recent call last):
+ ...
+ ValueError: E
+
+ The server log was printed. Note that we see the Error that we
+ requested above.
Modified: zc.ngi/trunk/src/zc/ngi/blocking.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/blocking.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/blocking.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -61,15 +61,16 @@
if connector is None:
connection = connection_or_address
else:
- connection = connect(connection_or_address, connector, timeout)
- return OutputFile(connection), InputFile(connection)
+ connection = connect(connection_or_address, connector, timeout)
+ outputfile = OutputFile(connection)
+ return outputfile, InputFile(connection, outputfile)
+
class _BaseFile:
def __init__(self, connection):
self._connection = connection
self._position = 0
- self._closed = False
def seek(self, offset, whence=0):
position = self._position
@@ -88,9 +89,10 @@
def tell(self):
return self._position
+ _closed = False
def _check_open(self):
if self._closed:
- raise ValueError("I/O operation on closed file")
+ raise IOError("I/O operation on closed file")
class OutputFile(_BaseFile):
@@ -100,7 +102,7 @@
read = readline = readlines = invalid_method
def flush(self):
- pass
+ self._check_exception()
def close(self):
if not self._closed:
@@ -108,12 +110,14 @@
self._closed = True
def write(self, data):
+ self._check_exception()
self._check_open()
assert isinstance(data, str)
self._position += len(data)
self._connection.write(data)
def writelines(self, data, timeout=None, nonblocking=False):
+ self._check_exception()
self._check_open()
if nonblocking:
self._connection.writelines(iter(data))
@@ -126,9 +130,14 @@
event.wait(timeout)
if not event.isSet():
raise Timeout()
-
-
+ _exception = None
+ def _check_exception(self):
+ if self._exception is not None:
+ exception = self._exception
+ self._exception = None
+ raise exception
+
class _writelines_iterator:
def __init__(self, base, file, notify):
@@ -150,11 +159,12 @@
class InputFile(_BaseFile):
- def __init__(self, connection):
+ def __init__(self, connection, outputfile):
_BaseFile.__init__(self, connection)
self._condition = threading.Condition()
self._data = ''
- self._exception = None
+ self._outputfile = outputfile
+ self._outputfile._exception = None
connection.setHandler(self)
def invalid_method(*args, **kw):
@@ -173,7 +183,7 @@
condition = self._condition
condition.acquire()
try:
- self._closed = True
+ self._closed = self._outputfile._closed = True
condition.notifyAll()
finally:
condition.release()
@@ -182,7 +192,7 @@
condition = self._condition
condition.acquire()
try:
- self._exception = exception
+ self._outputfile._exception = exception
condition.notifyAll()
finally:
condition.release()
@@ -191,7 +201,7 @@
condition = self._condition
condition.acquire()
try:
- self._closed = True
+ self._closed = self._outputfile._closed = True
self._connection.close()
condition.notifyAll()
finally:
@@ -211,7 +221,7 @@
condition = self._condition
condition.acquire()
try:
- self._check_exception()
+ self._outputfile._check_exception()
while 1:
data = self._data
if size is not None and size <= len(data):
@@ -234,7 +244,7 @@
condition = self._condition
condition.acquire()
try:
- self._check_exception()
+ self._outputfile._check_exception()
while 1:
data = self._data
l = data.find('\n')
@@ -264,7 +274,7 @@
condition = self._condition
condition.acquire()
try:
- self._check_exception()
+ self._outputfile._check_exception()
while 1:
data = self._data
if sizehint is not None and sizehint <= len(data):
@@ -281,12 +291,6 @@
finally:
condition.release()
- def _check_exception(self):
- if self._exception is not None:
- exception = self._exception
- self._exception = None
- raise exception
-
def _wait(self, timeout, deadline):
if timeout is not None:
if deadline is None:
@@ -301,7 +305,7 @@
else:
self._condition.wait()
- self._check_exception()
+ self._outputfile._check_exception()
return timeout, deadline
Modified: zc.ngi/trunk/src/zc/ngi/interfaces.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/interfaces.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/interfaces.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -22,7 +22,8 @@
register handlers that respond to input events. There are 3 kinds of
handlers:
-- Input handlers receive network input
+- Input handlers receive network input and notification of connection
+ closes and exceptions,
- Client-connect handlers respond to outbound connection events, and
@@ -36,6 +37,10 @@
Theoretically, different implementations could call handlers at the
same time.)
+ Note that when an application calls setHandler on a connection, the
+ connection handler may have it's methods called immediately with
+ pending input or notifications.
+
- All handler calls that are associated with a connection include the
connection as a parameter, This allows a single handler object to
respond to events from multiple connections.
@@ -71,7 +76,11 @@
This method can only be called in direct response to an
implementation call to a IConnectionHandler,
- IClientConnectHandler, or IServer
+ IClientConnectHandler, or IServer.
+
+ Any failure of a handler call must be caught and logged. If
+ an exception is raised by a call to hande_input or
+ handle_exception, the connection must be closed.
"""
def write(data):
Modified: zc.ngi/trunk/src/zc/ngi/testing.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/testing.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/testing.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -16,6 +16,8 @@
$Id$
"""
+import sys
+import traceback
import zc.ngi
class PrintingHandler:
@@ -57,6 +59,27 @@
def __nonzero__(self):
return not self.closed
+ queue = None
+ def _callHandler(self, method, *args):
+ if self.queue is None:
+ self.queue = [(method, args)]
+ while self.queue:
+ method, args = self.queue.pop(0)
+ if self.closed and method != 'handle_close':
+ break
+ try:
+ getattr(self.handler, method)(self, *args)
+ except:
+ print "Error test connection calling connection handler:"
+ traceback.print_exc(file=sys.stdout)
+ if method != 'handle_close':
+ self.close()
+ self.handler.handle_close(self, method+' error')
+
+ self.queue = None
+ else:
+ self.queue.append((method, args))
+
def close(self):
self.peer.test_close('closed')
if self.control is not None:
@@ -71,19 +94,19 @@
if self.exception:
exception = self.exception
self.exception = None
- handler.handle_exception(self, exception)
+ self._callHandler('handle_exception', exception)
if self.input:
- handler.handle_input(self, self.input)
+ self._callHandler('handle_input', self.input)
self.input = ''
# Note is self.closed is True, we self closed and we
# don't want to call handle_close.
if self.closed and isinstance(self.closed, str):
- handler.handle_close(self, self.closed)
+ self._callHandler('handle_close', self.closed)
def test_input(self, data):
if self.handler is not None:
- self.handler.handle_input(self, data)
+ self._callHandler('handle_input', data)
else:
self.input += data
@@ -92,7 +115,7 @@
self.control.closed(self)
self.closed = reason
if self.handler is not None:
- self.handler.handle_close(self, reason)
+ self._callHandler('handle_close', reason)
def write(self, data):
if data is zc.ngi.END_OF_DATA:
@@ -118,7 +141,7 @@
if self.handler is None:
self.exception = exception
else:
- self.handler.handle_exception(self, exception)
+ self._callHandler('handle_exception', exception)
class TextPrintingHandler(PrintingHandler):
@@ -176,6 +199,10 @@
if not self._connections and self._close_handler:
self._close_handler(self)
+ def connector(self, addr, handler):
+ handler.connected(Connection(None, self._handler))
+
+
class peer:
def __init__(self, addr, handler):
Modified: zc.ngi/trunk/src/zc/ngi/tests.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/tests.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/tests.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -106,6 +106,7 @@
return unittest.TestSuite([
doctest.DocFileSuite(
'README.txt',
+ 'testing.test',
'message.txt',
'adapters.txt',
'blocking.txt',
Modified: zc.ngi/trunk/src/zc/ngi/wordcount.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/wordcount.py 2008-01-01 14:46:34 UTC (rev 82626)
+++ zc.ngi/trunk/src/zc/ngi/wordcount.py 2008-01-01 17:37:53 UTC (rev 82627)
@@ -59,6 +59,8 @@
elif data == 'C':
connection.write(zc.ngi.END_OF_DATA)
return
+ elif data == 'E':
+ raise ValueError(data)
else:
cc = len(data)
lc = len(data.split('\n'))-1
@@ -72,6 +74,8 @@
def serve():
mod, name, port, level = sys.argv[1:]
__import__(mod)
+ logging.getLogger().addHandler(
+ logging.StreamHandler(open('server.log', 'w')))
logger.setLevel(int(level))
logger.addHandler(logging.StreamHandler())
logger.info('serving')
@@ -120,7 +124,7 @@
else:
print "Server still accepting connections"
-def start_server_process(listener):
+def start_server_process(listener, loglevel=None):
"""Start a server in a subprocess and return the port used
"""
module = listener.__module__
@@ -130,8 +134,10 @@
os.environ,
PYTHONPATH=os.pathsep.join(sys.path),
)
+ if loglevel is None:
+ loglevel = logger.getEffectiveLevel()
os.spawnle(os.P_NOWAIT, sys.executable, sys.executable, __file__,
- module, name, str(port), str(logger.getEffectiveLevel()),
+ module, name, str(port), str(loglevel),
env)
addr = 'localhost', port
wait(addr)
@@ -140,6 +146,9 @@
def stop_server_process(connector, addr):
zc.ngi.message.message(connector, addr, 'Q\0', lambda s: s == 'Q\n')
wait(addr, up=False)
+ log = open('server.log').read()
+ os.remove('server.log')
+ print log,
sample_docs = [
"""Hello world
More information about the Checkins
mailing list