[Jython-checkins] jython (merge 2.5 -> default): merge with 2.5: Fixing the getaddrinfo API to be closer to the C API.

alan.kennedy jython-checkins at python.org
Wed Nov 2 01:32:32 CET 2011


http://hg.python.org/jython/rev/95a08a9556f0
changeset:   6267:95a08a9556f0
parent:      6265:1651fbbecffd
parent:      6266:7306b9651a3a
user:        Alan Kennedy <jython-dev at xhaus.com>
date:        Wed Nov 02 00:31:05 2011 +0000
summary:
  merge with 2.5: Fixing the getaddrinfo API to be closer to the C API.
Fixes http://bugs.jython.org/issue1809

files:
  Lib/socket.py           |  70 ++++++++++++++++----
  Lib/test/test_socket.py |  93 +++++++++++++++++++++++++++++
  2 files changed, 147 insertions(+), 16 deletions(-)


diff --git a/Lib/socket.py b/Lib/socket.py
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -1,5 +1,5 @@
 """
-This is an updated socket module for use on JVMs > 1.5; it is derived from the old jython socket module.
+This is an updated socket module for use on JVMs >= 1.5; it is derived from the old jython socket module.
 It is documented, along with known issues and workarounds, on the jython wiki.
 http://wiki.python.org/jython/NewSocketModule
 """
@@ -174,11 +174,19 @@
 SHUT_RDWR = 2
 
 AF_UNSPEC = 0
-AF_INET = 2
-AF_INET6 = 23
+AF_INET   = 2
+AF_INET6  = 23
 
-AI_PASSIVE=1
-AI_CANONNAME=2
+AI_PASSIVE     = 1
+AI_CANONNAME   = 2
+AI_NUMERICHOST = 4
+AI_V4MAPPED    = 8
+AI_ALL         = 16
+AI_ADDRCONFIG  = 32
+AI_NUMERICSERV = 1024
+
+EAI_NONAME  = -2
+EAI_SERVICE = -8
 
 # For some reason, probably historical, SOCK_DGRAM and SOCK_STREAM are opposite values of what they are on cpython.
 # I.E. The following is the way they are on cpython
@@ -565,21 +573,30 @@
         from jnr.netdb import Service
     except ImportError:
         return None
-    return Service.getServiceByName(service_name, protocol_name).getPort()
+    service = Service.getServiceByName(service_name, protocol_name)
+    if service is None:
+        raise error('service/proto not found')
+    return service.getPort()
 
 def getservbyport(port, protocol_name=None):
     try:
         from jnr.netdb import Service
     except ImportError:
         return None
-    return Service.getServiceByPort(port, protocol_name).getName()
+    service = Service.getServiceByPort(port, protocol_name)
+    if service is None:
+        raise error('port/proto not found')
+    return service.getName()
 
 def getprotobyname(protocol_name=None):
     try:
         from jnr.netdb import Protocol
     except ImportError:
         return None
-    return Protocol.getProtocolByName(protocol_name).getProto()
+    proto = Protocol.getProtocolByName(protocol_name)
+    if proto is None:
+        raise error('protocol not found')
+    return proto.getProto()
 
 def _realsocket(family = AF_INET, sock_type = SOCK_STREAM, protocol=0):
     assert family in (AF_INET, AF_INET6), "Only AF_INET and AF_INET6 sockets are currently supported on jython"
@@ -723,19 +740,39 @@
     global _ipv4_addresses_only
     _ipv4_addresses_only = value
 
+def _get_port_number(port, flags):
+    if isinstance(port, basestring):
+        try:
+            int_port = int(port)
+        except ValueError:
+            if flags and flags & AI_NUMERICSERV:
+                raise gaierror(EAI_NONAME, "Name or service not known")
+            # Lookup the service by name
+            try:
+                int_port = getservbyname(port)
+            except error:
+                raise gaierror(EAI_SERVICE, "Servname not supported for ai_socktype")
+    elif port is None:
+        int_port = 0
+    elif not isinstance(port, (int, long)):
+        raise error("Int or String expected")
+    else:
+        int_port = int(port)
+    return int_port % 65536
+
 def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None):
     try:
+        if _ipv4_addresses_only:
+            family = AF_INET
         if not family in [AF_INET, AF_INET6, AF_UNSPEC]:
             raise gaierror(errno.EIO, 'ai_family not supported')
+        port = _get_port_number(port, flags)
         filter_fns = []
-        if _ipv4_addresses_only:
-            filter_fns.append( lambda x: isinstance(x, java.net.Inet4Address) )
-        else:
-            filter_fns.append({
-                AF_INET:   lambda x: isinstance(x, java.net.Inet4Address),
-                AF_INET6:  lambda x: isinstance(x, java.net.Inet6Address),
-                AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress),
-            }[family])
+        filter_fns.append({
+            AF_INET:   lambda x: isinstance(x, java.net.Inet4Address),
+            AF_INET6:  lambda x: isinstance(x, java.net.Inet6Address),
+            AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress),
+        }[family])
         if host == "":
             host = java.net.InetAddress.getLocalHost().getHostName()
         if isinstance(host, unicode):
@@ -793,6 +830,7 @@
 def ntohl(x): return x
 
 def inet_pton(family, ip_string):
+    # FIXME: java.net.InetAddress.getByName also accepts hostnames
     try:
         ia = java.net.InetAddress.getByName(ip_string)
         bytes = []
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -379,6 +379,39 @@
         if udpport is not None:
             eq(socket.getservbyport(udpport, 'udp'), service)
 
+    def testGetServByExceptions(self):
+        # First getservbyname
+        try:
+            result = socket.getservbyname("nosuchservice")
+        except socket.error:
+            pass
+        except Exception, x:
+            self.fail("getservbyname raised wrong exception for non-existent service: %s" % str(x))
+        else:
+            self.fail("getservbyname failed to raise exception for non-existent service: %s" % str(result))
+
+        # Now getservbyport
+        try:
+            result = socket.getservbyport(55555)
+        except socket.error:
+            pass
+        except Exception, x:
+            self.fail("getservbyport raised wrong exception for unknown port: %s" % str(x))
+        else:
+            self.fail("getservbyport failed to raise exception for unknown port: %s" % str(result))
+
+    def testGetProtoByName(self):
+        self.failUnlessEqual(socket.IPPROTO_TCP, socket.getprotobyname("tcp"))
+        self.failUnlessEqual(socket.IPPROTO_UDP, socket.getprotobyname("udp"))
+        try:
+            result = socket.getprotobyname("nosuchproto")
+        except socket.error:
+            pass
+        except Exception, x:
+            self.fail("getprotobyname raised wrong exception for unknown protocol: %s" % str(x))
+        else:
+            self.fail("getprotobyname failed to raise exception for unknown protocol: %s" % str(result))
+
     def testDefaultTimeout(self):
         # Testing default timeout
         # The default timeout should initially be None
@@ -1537,6 +1570,66 @@
         self.failUnless(str(ipv6_address_tuple) in ["('::1', 80, 0, 0)", "('0:0:0:0:0:0:0:1', 80, 0, 0)"])
         self.failUnless(repr(ipv6_address_tuple) in ["('::1', 80, 0, 0)", "('0:0:0:0:0:0:0:1', 80, 0, 0)"])
 
+    def testNonIntPort(self):
+        hostname = "localhost"
+
+        # Port value of None should map to 0
+        addrs = socket.getaddrinfo(hostname, None)
+        for a in addrs:
+            self.failUnlessEqual(a[4][1], 0, "Port value of None should have returned 0")
+
+        # Port value can be a string rep of the port number
+        addrs = socket.getaddrinfo(hostname, "80")
+        for a in addrs:
+            self.failUnlessEqual(a[4][1], 80, "Port value of '80' should have returned 80")
+
+        # Can also specify a service name
+        # This test assumes that service http will always be at port 80
+        addrs = socket.getaddrinfo(hostname, "http")
+        for a in addrs:
+            self.failUnlessEqual(a[4][1], 80, "Port value of 'http' should have returned 80")
+
+        # Check treatment of non-integer numeric port
+        try:
+            socket.getaddrinfo(hostname, 79.99)
+        except socket.error, se:
+            self.failUnlessEqual(se[0], "Int or String expected")
+        except Exception, x:
+            self.fail("getaddrinfo for float port number raised wrong exception: %s" % str(x))
+        else:
+            self.fail("getaddrinfo for float port number failed to raise exception")
+
+        # Check treatment of non-integer numeric port, as a string
+        # The result is that it should fail in the same way as a non-existent service
+        try:
+            socket.getaddrinfo(hostname, "79.99")
+        except socket.gaierror, g:
+            self.failUnlessEqual(g[0], socket.EAI_SERVICE)
+        except Exception, x:
+            self.fail("getaddrinfo for non-integer numeric port, as a string raised wrong exception: %s" % str(x))
+        else:
+            self.fail("getaddrinfo for non-integer numeric port, as a string failed to raise exception")
+
+        # Check enforcement of AI_NUMERICSERV
+        try:
+            socket.getaddrinfo(hostname, "http", 0, 0, 0, socket.AI_NUMERICSERV)
+        except socket.gaierror, g:
+            self.failUnlessEqual(g[0], socket.EAI_NONAME)
+        except Exception, x:
+            self.fail("getaddrinfo for service name with AI_NUMERICSERV raised wrong exception: %s" % str(x))
+        else:
+            self.fail("getaddrinfo for service name with AI_NUMERICSERV failed to raise exception")
+
+        # Check treatment of non-existent service
+        try:
+            socket.getaddrinfo(hostname, "nosuchservice")
+        except socket.gaierror, g:
+            self.failUnlessEqual(g[0], socket.EAI_SERVICE)
+        except Exception, x:
+            self.fail("getaddrinfo for unknown service name raised wrong exception: %s" % str(x))
+        else:
+            self.fail("getaddrinfo for unknown service name failed to raise exception")
+
 class TestJython_get_jsockaddr(unittest.TestCase):
     "These tests are specific to jython: they test a key internal routine"
 

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list