[Checkins] SVN: zc.ngi/trunk/ New Features:
Jim Fulton
jim at zope.com
Wed Aug 18 10:10:48 EDT 2010
Log message for revision 115757:
New Features:
- Connection objects have a new peer_address attribute, which is
equivilent to calling ``getpeername()`` on sockets.
Bugs Fixed:
- Servers using unix-domain sockets didn't clean up socket files.
- When testing listeners were closed, handle_close, rather than close,
was called on server connections.
Changed:
U zc.ngi/trunk/README.txt
U zc.ngi/trunk/src/zc/ngi/adapters.py
U zc.ngi/trunk/src/zc/ngi/async.py
U zc.ngi/trunk/src/zc/ngi/doc/index.txt
U zc.ngi/trunk/src/zc/ngi/doc/reference.txt
U zc.ngi/trunk/src/zc/ngi/interfaces.py
U zc.ngi/trunk/src/zc/ngi/old.test
U zc.ngi/trunk/src/zc/ngi/testing.py
U zc.ngi/trunk/src/zc/ngi/testing.test
U zc.ngi/trunk/src/zc/ngi/tests.py
-=-
Modified: zc.ngi/trunk/README.txt
===================================================================
--- zc.ngi/trunk/README.txt 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/README.txt 2010-08-18 14:10:47 UTC (rev 115757)
@@ -20,6 +20,22 @@
*******
====================
+2.0.0a5 (2010-08-18)
+====================
+
+New Features:
+
+- Connection objects have a new peer_address attribute, which is
+ equivilent to calling ``getpeername()`` on sockets.
+
+Bugs Fixed:
+
+- Servers using unix-domain sockets didn't clean up socket files.
+
+- When testing listeners were closed, handle_close, rather than close,
+ was called on server connections.
+
+====================
2.0.0a4 (2010-07-27)
====================
Modified: zc.ngi/trunk/src/zc/ngi/adapters.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/adapters.py 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/adapters.py 2010-08-18 14:10:47 UTC (rev 115757)
@@ -61,6 +61,10 @@
def handler(class_, func):
return zc.ngi.generator.handler(func, class_)
+ @property
+ def peer_address(self):
+ return self.connection.peer_address
+
class Lines(Base):
input = ''
Modified: zc.ngi/trunk/src/zc/ngi/async.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/async.py 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/async.py 2010-08-18 14:10:47 UTC (rev 115757)
@@ -433,6 +433,9 @@
def close(self):
self._dispatcher.close_after_write()
+ @property
+ def peer_address(self):
+ return self._dispatcher.socket.getpeername()
class _ServerConnection(_Connection):
zc.ngi.interfaces.implements(zc.ngi.interfaces.IServerConnection)
@@ -569,6 +572,7 @@
self.__close_handler = None
self._thready = thready
self.__connections = set()
+ self.address = addr
BaseListener.__init__(self, implementation)
if isinstance(addr, str):
family = socket.AF_UNIX
@@ -658,6 +662,9 @@
def _close(self, handler):
BaseListener.close(self)
+ if isinstance(self.address, str) and os.path.exists(self.address):
+ os.remove(self.address)
+
if handler is None:
for c in list(self.__connections):
c._dispatcher.handle_close("stopped")
Modified: zc.ngi/trunk/src/zc/ngi/doc/index.txt
===================================================================
--- zc.ngi/trunk/src/zc/ngi/doc/index.txt 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/doc/index.txt 2010-08-18 14:10:47 UTC (rev 115757)
@@ -900,6 +900,7 @@
-> '2 3\n'
>>> listener.close()
+ -> CLOSE
We can also use adapters with generator-based handlers by passing an
adapter factory to ``zc.ngi.generator.handler`` using the
@@ -923,6 +924,7 @@
>>> connection.write('\nhello out\nthere')
-> '2 3\n'
>>> listener.close()
+ -> CLOSE
By separating the low-level protocol handling from the application
logic, we can reuse the low-level protocol in other applications, and
Modified: zc.ngi/trunk/src/zc/ngi/doc/reference.txt
===================================================================
--- zc.ngi/trunk/src/zc/ngi/doc/reference.txt 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/doc/reference.txt 2010-08-18 14:10:47 UTC (rev 115757)
@@ -31,6 +31,12 @@
.. autoclass:: IConnection
:members:
+ .. attribute:: peer_address
+
+ For server connections, the address of the client. For clients,
+ the server address. For socket-based implementations, this is
+ the result of calling ``getpeername()`` on the socket.
+
.. autoclass:: IImplementation
:members:
Modified: zc.ngi/trunk/src/zc/ngi/interfaces.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/interfaces.py 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/interfaces.py 2010-08-18 14:10:47 UTC (rev 115757)
@@ -127,6 +127,16 @@
any time.
"""
+ peer_address = Attribute(
+ """The peer address
+
+ For socket-based connectionss, this is the result of calling
+ getpeername on the socket.
+
+ This is primarily interesting for servers that want to vary
+ behavior depending on where clients connect from.
+ """)
+
class IServerConnection(IConnection):
"""Server connection
Modified: zc.ngi/trunk/src/zc/ngi/old.test
===================================================================
--- zc.ngi/trunk/src/zc/ngi/old.test 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/old.test 2010-08-18 14:10:47 UTC (rev 115757)
@@ -322,8 +322,8 @@
connections are accepted:
>>> listener.close()
- server closed: stopped
- server closed: stopped
+ -> CLOSE
+ -> CLOSE
>>> connection = zc.ngi.testing.Connection()
>>> listener.connect(connection)
@@ -420,11 +420,11 @@
Let's connect our echo server and client. First, we'll create our
server using the listener constructor:
- >>> listener = zc.ngi.testing.listener(EchoServer)
+ >>> listener = zc.ngi.testing.listener(('localhost', 42), EchoServer)
Then we'll use the connect method on the listener:
- >>> client = EchoClient(listener.connect)
+ >>> client = EchoClient(zc.ngi.testing.connect)
>>> client.check(('localhost', 42), ['hello', 'world', 'how are you?'])
server connected
server got input: 'hello\n'
Modified: zc.ngi/trunk/src/zc/ngi/testing.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/testing.py 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/testing.py 2010-08-18 14:10:47 UTC (rev 115757)
@@ -48,13 +48,15 @@
zc.ngi.interfaces.implements(zc.ngi.interfaces.IConnection)
- def __init__(self, peer=None, handler=PrintingHandler):
+ def __init__(self, peer=None, handler=PrintingHandler,
+ address=None, peer_address=None):
self.handler = None
+ self.address = address
self.handler_queue = []
self.control = None
self.closed = None
if peer is None:
- peer = Connection(self)
+ peer = Connection(self, address=peer_address)
handler(peer)
self.peer = peer
@@ -74,15 +76,13 @@
if self.queue is None:
self.queue = queue = [(method, arg)]
- while queue:
+ while queue and not self.closed:
method, arg = queue.pop(0)
if method == 'handle_close':
if self.control is not None:
self.control.closed(self)
self.closed = arg
- elif self.closed:
- break
try:
try:
@@ -102,7 +102,7 @@
raise
handler(self, arg)
- except:
+ except Exception, v:
print "Error test connection calling connection handler:"
traceback.print_exc(file=sys.stdout)
if method != 'handle_close':
@@ -114,6 +114,8 @@
self.queue.append((method, arg))
def close(self):
+ if self.closed:
+ return
self.peer.test_close('closed')
if self.control is not None:
self.control.closed(self)
@@ -161,9 +163,17 @@
except Exception, v:
self._exception(v)
+ @property
+ def peer_address(self):
+ return self.peer.address
+
class _ServerConnection(Connection):
zc.ngi.interfaces.implements(zc.ngi.interfaces.IServerConnection)
+ def __init__(self):
+ Connection.__init__(self, False) # False to avoid setting peer handler
+ self.peer = Connection(self)
+
class TextPrintingHandler(PrintingHandler):
def handle_input(self, connection, data):
@@ -174,13 +184,17 @@
_connectable = {}
_recursing = object()
-def connect(addr, handler):
+def connect(addr, handler, client_address=None):
connections = _connectable.get(addr)
if isinstance(connections, list):
if connections:
- return handler.connected(connections.pop(0))
+ connection = connections.pop(0)
+ connection.peer.address = addr
+ connection.address = client_address
+ return handler.connected(connection)
elif isinstance(connections, listener):
- return connections.connect(addr, handler=handler)
+ return connections.connect(handler=handler,
+ client_address=client_address)
elif connections is _recursing:
print (
"For address, %r, a connect handler called connect from a\n"
@@ -201,6 +215,7 @@
def connectable(addr, connection):
_connectable.setdefault(addr, []).append(connection)
+
class listener:
zc.ngi.interfaces.implements(zc.ngi.interfaces.IListener)
@@ -215,23 +230,31 @@
self._close_handler = None
self._connections = []
- def connect(self, connection=None, handler=None):
- if handler is not None:
- # connection is addr in this case and is ignored
- handler.connected(Connection(None, self._handler))
- return
+ def connect(self, connection=None, handler=None, client_address=None):
if self._handler is None:
raise TypeError("Listener closed")
- if connection is None:
+
+ try: # Round about None (or legacy addr) test
+ connection.write
+ orig = connection
+ except AttributeError:
connection = _ServerConnection()
- peer = connection.peer
- else:
- peer = None
+ orig = None
+
+ connection.control = self
+ connection.peer.address = client_address
+ connection.address = self.address
self._connections.append(connection)
- connection.control = self
self._handler(connection)
- return peer
+ if handler is not None:
+ handler.connected(connection.peer)
+ elif orig is None:
+ PrintingHandler(connection.peer)
+ return connection.peer
+
+ return None
+
connector = connect
def connections(self):
@@ -243,7 +266,7 @@
self._handler = None
if handler is None:
while self._connections:
- self._connections[0].test_close('stopped')
+ self._connections[0].close()
elif not self._connections:
handler(self)
else:
Modified: zc.ngi/trunk/src/zc/ngi/testing.test
===================================================================
--- zc.ngi/trunk/src/zc/ngi/testing.test 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/testing.test 2010-08-18 14:10:47 UTC (rev 115757)
@@ -45,7 +45,6 @@
client i got: Hi
server h got: Hi
client h got: Bye
- client closed b closed
If an exeption is raised by a handler, then the exception is logged
and the connection is closed (if not already closed).
Modified: zc.ngi/trunk/src/zc/ngi/tests.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/tests.py 2010-08-18 13:31:35 UTC (rev 115756)
+++ zc.ngi/trunk/src/zc/ngi/tests.py 2010-08-18 14:10:47 UTC (rev 115757)
@@ -12,12 +12,15 @@
#
##############################################################################
from __future__ import with_statement
+from zope.testing import setupstack
import doctest
import logging
import manuel.capture
import manuel.doctest
import manuel.testing
+import os
+import socket
import sys
import threading
import time
@@ -601,6 +604,116 @@
"""
+def async_peer_address():
+ r"""
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def server(connection):
+ ... host, port = connection.peer_address
+ ... if not (host == '127.0.0.1' and isinstance(port, int)):
+ ... print 'oops', host, port
+ ... data = (yield)
+ ... connection.write(data+'\n')
+ ... listener.close()
+
+ >>> listener = zc.ngi.async.listener(None, server)
+
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def client(connection):
+ ... connection.write('hi\n')
+ ... yield
+
+ >>> zc.ngi.async.connect(listener.address, client); zc.ngi.async.wait(1)
+
+ """
+
+def testing_peer_address():
+ r"""
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def server(connection):
+ ... print `connection.peer_address`
+ ... data = (yield)
+ ... connection.write(data+'\n')
+ ... listener.close()
+
+ >>> listener = zc.ngi.testing.listener('', server)
+
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def client(connection):
+ ... connection.write('hi\n')
+ ... yield
+
+ >>> zc.ngi.testing.connect(listener.address, client,
+ ... client_address=('xxx', 0))
+ ('xxx', 0)
+
+ Obscure:
+
+ >>> conn = zc.ngi.testing.Connection(address='1', peer_address='2')
+ >>> conn.peer_address, conn.peer.peer_address
+ ('2', '1')
+
+ >>> conn = zc.ngi.testing.Connection()
+ >>> zc.ngi.testing.connectable('x', conn)
+ >>> zc.ngi.testing.connect('x', client, client_address='y')
+ -> 'hi\n'
+
+ >>> conn.peer_address, conn.peer.peer_address
+ ('x', 'y')
+
+ """
+
+def async_close_unix():
+ """
+
+When we create and the close a unix-domain socket, we remove the
+socket file so we can reopen it later.
+
+ >>> os.listdir('.')
+ []
+
+ >>> listener = zc.ngi.async.listener('socket', lambda c: None)
+ >>> os.listdir('.')
+ ['socket']
+
+ >>> listener.close(); zc.ngi.async.wait(1)
+ >>> os.listdir('.')
+ []
+
+ >>> listener = zc.ngi.async.listener('socket', lambda c: None)
+ >>> os.listdir('.')
+ ['socket']
+
+ >>> listener.close(); zc.ngi.async.wait(1)
+ >>> os.listdir('.')
+ []
+
+ """
+
+def async_peer_address_unix():
+ r"""
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def server(connection):
+ ... print `connection.peer_address`
+ ... data = (yield)
+ ... connection.write(data+'\n')
+ ... listener.close()
+
+ >>> listener = zc.ngi.async.listener('sock', server)
+
+ >>> @zc.ngi.adapters.Lines.handler
+ ... def client(connection):
+ ... connection.write('hi\n')
+ ... yield
+
+ >>> zc.ngi.async.connect(listener.address, client); zc.ngi.async.wait(1)
+ ''
+
+ """
+
+if not hasattr(socket, 'AF_UNIX'):
+ # windows
+ del async_peer_address_unix, async_close_unix
+
if sys.version_info < (2, 6):
del setHandler_compatibility
@@ -618,16 +731,19 @@
handle_input = handle_close = lambda: xxxxx
+def setUp(test):
+ cleanup()
+ setupstack.setUpDirectory(test)
+ setupstack.register(test, cleanup)
+
def async_evil_setup(test):
+ setUp(test)
# Uncomment the next 2 lines to check that a bunch of lambda type
# errors are logged.
#import logging
#logging.getLogger().addHandler(logging.StreamHandler())
- # clean up the map.
- zc.ngi.async.cleanup_map()
-
# See if we can break the main loop before running the async test
# Connect to bad port with bad handler
@@ -657,7 +773,8 @@
zc.ngi.async.listener(addr, BrokenAfterConnect())
zc.ngi.async.connect(addr, BrokenAfterConnect())
-def cleanup_async(test):
+def cleanup():
+ zc.ngi.testing._connectable.clear()
zc.ngi.async.cleanup_map()
zc.ngi.async.wait(9)
@@ -666,7 +783,7 @@
manuel.testing.TestSuite(
manuel.capture.Manuel() + manuel.doctest.Manuel(),
'doc/index.txt',
- ),
+ setUp=setUp, tearDown=setupstack.tearDown),
doctest.DocFileSuite(
'old.test',
'testing.test',
@@ -674,12 +791,12 @@
'adapters.test',
'blocking.test',
'async-udp.test',
- tearDown=cleanup_async),
+ setUp=setUp, tearDown=setupstack.tearDown),
doctest.DocFileSuite(
'async.test',
- setUp=async_evil_setup, tearDown=cleanup_async,
+ setUp=async_evil_setup, tearDown=setupstack.tearDown,
),
- doctest.DocTestSuite(setUp=cleanup_async, tearDown=cleanup_async),
+ doctest.DocTestSuite(setUp=setUp, tearDown=setupstack.tearDown),
])
if __name__ == '__main__':
More information about the checkins
mailing list