SF.net SVN: jython:[7198] trunk/jython/Lib

[email protected] Sun, 13 Feb 2011 20:49:58 +0000
Newsgroups gmane.comp.lang.jython.cvs
Message-ID <[email protected]>
Revision: 7198
          http://jython.svn.sourceforge.net/jython/?rev=7198&view=rev
Author:   amak
Date:     2011-02-13 20:49:57 +0000 (Sun, 13 Feb 2011)

Log Message:
-----------
Checking in support for Internationalized Domain Names (RFC 3490), plus unit-tests for same.
This new support only works on Java 6. The new feature and workarounds for Java 5 are documented on the jython wiki.
http://wiki.python.org/jython/NewSocketModule
Partially fixes http://bugs.jython.org/issue1153

Modified Paths:
--------------
    trunk/jython/Lib/socket.py
    trunk/jython/Lib/test/test_socket.py

Modified: trunk/jython/Lib/socket.py
===================================================================
--- trunk/jython/Lib/socket.py	2011-02-12 01:50:38 UTC (rev 7197)
+++ trunk/jython/Lib/socket.py	2011-02-13 20:49:57 UTC (rev 7198)
@@ -161,6 +161,17 @@
     exception.java_exception = exc
     return exception
 
+_feature_support_map = {
+    'ipv6': True,
+    'idna': False,
+    'tipc': False,
+}
+
+def supports(feature, *args):
+    if len(args) == 1:
+        _feature_support_map[feature] = args[0]
+    return _feature_support_map.get(feature, False)
+
 MODE_BLOCKING    = 'block'
 MODE_NONBLOCKING = 'nonblock'
 MODE_TIMEOUT     = 'timeout'
@@ -591,6 +602,36 @@
             assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets"
         return _udpsocket()
 
+#
+# Attempt to provide IDNA (RFC 3490) support.
+#
+# Try java.net.IDN, built into java 6
+#
+
+idna_libraries = [
+    ('java.net.IDN', 'toASCII', java.lang.IllegalArgumentException)
+]
+
+for idna_lib, idna_fn_name, exc in idna_libraries:
+    try:
+        m = __import__(idna_lib, globals(), locals(), [idna_fn_name])
+        idna_fn = getattr(m, idna_fn_name)
+        def _encode_idna(name):
+            try:
+                return idna_fn(name)
+            except exc:
+                raise UnicodeEncodeError(name)
+        supports('idna', True)
+        break
+    except (AttributeError, ImportError), e:
+        pass
+else:
+    _encode_idna = lambda x: x.encode("ascii")
+
+#
+# Define data structures to support IPV4 and IPV6.
+#
+
 class _ip_address_t: pass
 
 class _ipv4_address_t(_ip_address_t):
@@ -668,10 +709,7 @@
     if hostname is None:
         return java.net.InetSocketAddress(port)
     if isinstance(hostname, unicode):
-        # XXX: Should be encode('idna') (See CPython
-        # socketmodule::getsockaddrarg), but Jython's idna support is
-        # currently broken
-        hostname = hostname.encode()
+        hostname = _encode_idna(hostname)
     if len(address_object) == 4:
         # There is no way to get a Inet6Address: Inet6Address.getByName() simply calls
         # InetAddress.getByName,() which also returns Inet4Address objects
@@ -701,6 +739,8 @@
             }[family])
         if host == "":
             host = java.net.InetAddress.getLocalHost().getHostName()
+        if isinstance(host, unicode):
+            host = _encode_idna(host)
         passive_mode = flags is not None and flags & AI_PASSIVE
         canonname_mode = flags is not None and flags & AI_CANONNAME
         results = []

Modified: trunk/jython/Lib/test/test_socket.py
===================================================================
--- trunk/jython/Lib/test/test_socket.py	2011-02-12 01:50:38 UTC (rev 7197)
+++ trunk/jython/Lib/test/test_socket.py	2011-02-13 20:49:57 UTC (rev 7198)
@@ -1742,6 +1742,47 @@
     def _testUnicodeHostname(self):
         self.cli.connect((unicode(self.HOST), self.PORT))
 
+class IDNATest(unittest.TestCase):
+
+    def testGetAddrInfoIDNAHostname(self):
+        idna_domain = u"al\u00e1n.com"
+        if socket.supports('idna'):
+            try:
+                addresses = socket.getaddrinfo(idna_domain, 80)
+                self.failUnless(len(addresses) > 0, "No addresses returned for test IDNA domain '%s'" % repr(idna_domain))
+            except Exception, x:
+                self.fail("Unexpected exception raised for socket.getaddrinfo(%s)" % repr(idna_domain))
+        else:
+            try:
+                socket.getaddrinfo(idna_domain, 80)
+            except UnicodeEncodeError:
+                pass
+            except Exception, x:
+                self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x)))
+            else:
+                self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain))
+
+    def testAddrTupleIDNAHostname(self):
+        idna_domain = u"al\u00e1n.com"
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        if socket.supports('idna'):
+            try:
+                s.bind( (idna_domain, 80) )
+            except socket.error:
+                # We're not worried about socket errors, i.e. bind problems, etc.
+                pass
+            except Exception, x:
+                self.fail("Unexpected exception raised for socket.bind(%s)" % repr(idna_domain))
+        else:
+            try:
+                s.bind( (idna_domain, 80) )
+            except UnicodeEncodeError:
+                pass
+            except Exception, x:
+                self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x)))
+            else:
+                self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain))
+
 class TestInvalidUsage(unittest.TestCase):
 
     def setUp(self):
@@ -1793,6 +1834,7 @@
         LineBufferedFileObjectClassTestCase,
         SmallBufferedFileObjectClassTestCase,
         UnicodeTest,
+        IDNATest,
     ]
     if hasattr(socket, "socketpair"):
         tests.append(BasicSocketPairTest)
@@ -1804,7 +1846,6 @@
     # Need some way to discover the network setup on the test machine
     if False:
         tests.append(UDPBroadcastTest)
-#    tests = [TestJython_get_jsockaddr]
     suites = [unittest.makeSuite(klass, 'test') for klass in tests]
     test_support.run_suite(unittest.TestSuite(suites))
 


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.

------------------------------------------------------------------------------
The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE:
Pinpoint memory and threading errors before they happen.
Find and fix more than 250 security defects in the development cycle.
Locate bottlenecks in serial and parallel code that limit performance.
http://p.sf.net/sfu/intel-dev2devfeb