[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